This is page 31 of 181. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ └── config.json ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-blog.yml │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── layout-changes.md │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── blog-page-component.png │ │ │ │ ├── blog-scrabble.png │ │ │ │ ├── integrated-blog-search.png │ │ │ │ └── lorem-ipsum.png │ │ │ ├── lorem-ipsum.md │ │ │ ├── newest-post.md │ │ │ ├── older-post.md │ │ │ └── welcome-to-the-xmlui-blog.md │ │ ├── mockServiceWorker.js │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ ├── rss.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ └── blog-theme.ts │ └── tsconfig.json ├── CONTRIBUTING.md ├── docs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── ComponentRefLinks.txt │ ├── content │ │ ├── _meta.json │ │ ├── components │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── APICall.md │ │ │ ├── App.md │ │ │ ├── AppHeader.md │ │ │ ├── AppState.md │ │ │ ├── AutoComplete.md │ │ │ ├── Avatar.md │ │ │ ├── Backdrop.md │ │ │ ├── Badge.md │ │ │ ├── BarChart.md │ │ │ ├── Bookmark.md │ │ │ ├── Breakout.md │ │ │ ├── Button.md │ │ │ ├── Card.md │ │ │ ├── Carousel.md │ │ │ ├── ChangeListener.md │ │ │ ├── Checkbox.md │ │ │ ├── CHStack.md │ │ │ ├── ColorPicker.md │ │ │ ├── Column.md │ │ │ ├── ContentSeparator.md │ │ │ ├── CVStack.md │ │ │ ├── DataSource.md │ │ │ ├── DateInput.md │ │ │ ├── DatePicker.md │ │ │ ├── DonutChart.md │ │ │ ├── DropdownMenu.md │ │ │ ├── EmojiSelector.md │ │ │ ├── ExpandableItem.md │ │ │ ├── FileInput.md │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FlowLayout.md │ │ │ ├── Footer.md │ │ │ ├── Form.md │ │ │ ├── FormItem.md │ │ │ ├── FormSection.md │ │ │ ├── Fragment.md │ │ │ ├── H1.md │ │ │ ├── H2.md │ │ │ ├── H3.md │ │ │ ├── H4.md │ │ │ ├── H5.md │ │ │ ├── H6.md │ │ │ ├── Heading.md │ │ │ ├── HSplitter.md │ │ │ ├── HStack.md │ │ │ ├── Icon.md │ │ │ ├── IFrame.md │ │ │ ├── Image.md │ │ │ ├── Items.md │ │ │ ├── LabelList.md │ │ │ ├── Legend.md │ │ │ ├── LineChart.md │ │ │ ├── Link.md │ │ │ ├── List.md │ │ │ ├── Logo.md │ │ │ ├── Markdown.md │ │ │ ├── MenuItem.md │ │ │ ├── MenuSeparator.md │ │ │ ├── ModalDialog.md │ │ │ ├── NavGroup.md │ │ │ ├── NavLink.md │ │ │ ├── NavPanel.md │ │ │ ├── NoResult.md │ │ │ ├── NumberBox.md │ │ │ ├── Option.md │ │ │ ├── Page.md │ │ │ ├── PageMetaTitle.md │ │ │ ├── Pages.md │ │ │ ├── Pagination.md │ │ │ ├── PasswordInput.md │ │ │ ├── PieChart.md │ │ │ ├── ProgressBar.md │ │ │ ├── Queue.md │ │ │ ├── RadioGroup.md │ │ │ ├── RealTimeAdapter.md │ │ │ ├── Redirect.md │ │ │ ├── Select.md │ │ │ ├── Slider.md │ │ │ ├── Slot.md │ │ │ ├── SpaceFiller.md │ │ │ ├── Spinner.md │ │ │ ├── Splitter.md │ │ │ ├── Stack.md │ │ │ ├── StickyBox.md │ │ │ ├── SubMenuItem.md │ │ │ ├── Switch.md │ │ │ ├── TabItem.md │ │ │ ├── Table.md │ │ │ ├── TableOfContents.md │ │ │ ├── Tabs.md │ │ │ ├── Text.md │ │ │ ├── TextArea.md │ │ │ ├── TextBox.md │ │ │ ├── Theme.md │ │ │ ├── TimeInput.md │ │ │ ├── Timer.md │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneSwitch.md │ │ │ ├── Tooltip.md │ │ │ ├── Tree.md │ │ │ ├── VSplitter.md │ │ │ ├── VStack.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ ├── xmlui-spreadsheet │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Spreadsheet.md │ │ │ └── xmlui-website-blocks │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Carousel.md │ │ │ ├── HelloMd.md │ │ │ ├── HeroSection.md │ │ │ └── ScrollToTop.md │ │ └── extensions │ │ ├── _meta.json │ │ ├── xmlui-animations │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Animation.md │ │ │ ├── FadeAnimation.md │ │ │ ├── FadeInAnimation.md │ │ │ ├── FadeOutAnimation.md │ │ │ ├── ScaleAnimation.md │ │ │ └── SlideInAnimation.md │ │ └── xmlui-website-blocks │ │ ├── _meta.json │ │ ├── _overview.md │ │ ├── Carousel.md │ │ ├── HelloMd.md │ │ ├── HeroSection.md │ │ └── ScrollToTop.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── feed.rss │ │ ├── mockServiceWorker.js │ │ ├── pages │ │ │ ├── _meta.json │ │ │ ├── app-structure.md │ │ │ ├── build-editor-component.md │ │ │ ├── build-hello-world-component.md │ │ │ ├── components-intro.md │ │ │ ├── context-variables.md │ │ │ ├── forms.md │ │ │ ├── globals.md │ │ │ ├── glossary.md │ │ │ ├── helper-tags.md │ │ │ ├── hosted-deployment.md │ │ │ ├── howto │ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md │ │ │ │ ├── chain-a-refetch.md │ │ │ │ ├── debug-a-component.md │ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md │ │ │ │ ├── delegate-a-method.md │ │ │ │ ├── do-custom-form-validation.md │ │ │ │ ├── expose-a-method-from-a-component.md │ │ │ │ ├── filter-and-transform-data-from-an-api.md │ │ │ │ ├── group-items-in-list-by-a-property.md │ │ │ │ ├── handle-background-operations.md │ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md │ │ │ │ ├── make-a-set-of-equal-width-cards.md │ │ │ │ ├── make-a-table-responsive.md │ │ │ │ ├── make-navpanel-width-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── build-system.md │ ├── build-xmlui.md │ ├── component-behaviors.md │ ├── components-with-options.md │ ├── containers.md │ ├── data-operations.md │ ├── glossary.md │ ├── index.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── standalone-app.md │ ├── ud-components.md │ └── xmlui-repo.md ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ └── SelectNative.tsx │ │ ├── SelectionStore │ │ │ ├── SelectionStore.md │ │ │ ├── SelectionStore.tsx │ │ │ └── SelectionStoreNative.tsx │ │ ├── Slider │ │ │ ├── Slider.md │ │ │ ├── Slider.module.scss │ │ │ ├── Slider.spec.ts │ │ │ ├── Slider.tsx │ │ │ └── SliderNative.tsx │ │ ├── Slot │ │ │ ├── Slot.md │ │ │ ├── Slot.spec.ts │ │ │ └── Slot.ts │ │ ├── SlotItem.tsx │ │ ├── SpaceFiller │ │ │ ├── SpaceFiller.md │ │ │ ├── SpaceFiller.module.scss │ │ │ ├── SpaceFiller.spec.ts │ │ │ ├── SpaceFiller.tsx │ │ │ └── SpaceFillerNative.tsx │ │ ├── Spinner │ │ │ ├── Spinner.md │ │ │ ├── Spinner.module.scss │ │ │ ├── Spinner.spec.ts │ │ │ ├── Spinner.tsx │ │ │ └── SpinnerNative.tsx │ │ ├── Splitter │ │ │ ├── HSplitter.md │ │ │ ├── HSplitter.spec.ts │ │ │ ├── Splitter.md │ │ │ ├── Splitter.module.scss │ │ │ ├── Splitter.spec.ts │ │ │ ├── Splitter.tsx │ │ │ ├── SplitterNative.tsx │ │ │ ├── utils.ts │ │ │ ├── VSplitter.md │ │ │ └── VSplitter.spec.ts │ │ ├── Stack │ │ │ ├── CHStack.md │ │ │ ├── CHStack.spec.ts │ │ │ ├── CVStack.md │ │ │ ├── CVStack.spec.ts │ │ │ ├── HStack.md │ │ │ ├── HStack.spec.ts │ │ │ ├── Stack.md │ │ │ ├── Stack.module.scss │ │ │ ├── Stack.spec.ts │ │ │ ├── Stack.tsx │ │ │ ├── StackNative.tsx │ │ │ ├── VStack.md │ │ │ └── VStack.spec.ts │ │ ├── StickyBox │ │ │ ├── StickyBox.md │ │ │ ├── StickyBox.module.scss │ │ │ ├── StickyBox.tsx │ │ │ └── StickyBoxNative.tsx │ │ ├── Switch │ │ │ ├── Switch.md │ │ │ ├── Switch.spec.ts │ │ │ └── Switch.tsx │ │ ├── Table │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── react-table-config.d.ts │ │ │ ├── Table.md │ │ │ ├── Table.module.scss │ │ │ ├── Table.spec.ts │ │ │ ├── Table.tsx │ │ │ ├── TableNative.tsx │ │ │ └── useRowSelection.tsx │ │ ├── TableOfContents │ │ │ ├── TableOfContents.module.scss │ │ │ ├── TableOfContents.spec.ts │ │ │ ├── TableOfContents.tsx │ │ │ └── TableOfContentsNative.tsx │ │ ├── Tabs │ │ │ ├── TabContext.tsx │ │ │ ├── TabItem.md │ │ │ ├── TabItem.tsx │ │ │ ├── TabItemNative.tsx │ │ │ ├── Tabs.md │ │ │ ├── Tabs.module.scss │ │ │ ├── Tabs.spec.ts │ │ │ ├── Tabs.tsx │ │ │ └── TabsNative.tsx │ │ ├── Text │ │ │ ├── Text.md │ │ │ ├── Text.module.scss │ │ │ ├── Text.spec.ts │ │ │ ├── Text.tsx │ │ │ └── TextNative.tsx │ │ ├── TextArea │ │ │ ├── TextArea.md │ │ │ ├── TextArea.module.scss │ │ │ ├── TextArea.spec.ts │ │ │ ├── TextArea.tsx │ │ │ ├── TextAreaNative.tsx │ │ │ ├── TextAreaResizable.tsx │ │ │ └── useComposedRef.ts │ │ ├── TextBox │ │ │ ├── TextBox.md │ │ │ ├── TextBox.module.scss │ │ │ ├── TextBox.spec.ts │ │ │ ├── TextBox.tsx │ │ │ └── TextBoxNative.tsx │ │ ├── Theme │ │ │ ├── NotificationToast.tsx │ │ │ ├── Theme.md │ │ │ ├── Theme.module.scss │ │ │ ├── Theme.spec.ts │ │ │ ├── Theme.tsx │ │ │ └── ThemeNative.tsx │ │ ├── TimeInput │ │ │ ├── TimeInput.md │ │ │ ├── TimeInput.module.scss │ │ │ ├── TimeInput.spec.ts │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeInputNative.tsx │ │ │ └── utils.ts │ │ ├── Timer │ │ │ ├── Timer.md │ │ │ ├── Timer.spec.ts │ │ │ ├── Timer.tsx │ │ │ └── TimerNative.tsx │ │ ├── Toggle │ │ │ ├── Toggle.module.scss │ │ │ └── Toggle.tsx │ │ ├── ToneChangerButton │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneChangerButton.spec.ts │ │ │ └── ToneChangerButton.tsx │ │ ├── ToneSwitch │ │ │ ├── ToneSwitch.md │ │ │ ├── ToneSwitch.module.scss │ │ │ ├── ToneSwitch.spec.ts │ │ │ ├── ToneSwitch.tsx │ │ │ └── ToneSwitchNative.tsx │ │ ├── Tooltip │ │ │ ├── Tooltip.md │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.spec.ts │ │ │ ├── Tooltip.tsx │ │ │ └── TooltipNative.tsx │ │ ├── Tree │ │ │ ├── testData.ts │ │ │ ├── Tree-dynamic.spec.ts │ │ │ ├── Tree-icons.spec.ts │ │ │ ├── Tree.md │ │ │ ├── Tree.spec.ts │ │ │ ├── TreeComponent.module.scss │ │ │ ├── TreeComponent.tsx │ │ │ └── TreeNative.tsx │ │ ├── TreeDisplay │ │ │ ├── TreeDisplay.md │ │ │ ├── TreeDisplay.module.scss │ │ │ ├── TreeDisplay.tsx │ │ │ └── TreeDisplayNative.tsx │ │ ├── ValidationSummary │ │ │ ├── ValidationSummary.module.scss │ │ │ └── ValidationSummary.tsx │ │ └── VisuallyHidden.tsx │ ├── components-core │ │ ├── abstractions │ │ │ ├── ComponentRenderer.ts │ │ │ ├── LoaderRenderer.ts │ │ │ ├── standalone.ts │ │ │ └── treeAbstractions.ts │ │ ├── action │ │ │ ├── actions.ts │ │ │ ├── APICall.tsx │ │ │ ├── FileDownloadAction.tsx │ │ │ ├── FileUploadAction.tsx │ │ │ ├── NavigateAction.tsx │ │ │ └── TimedAction.tsx │ │ ├── ApiBoundComponent.tsx │ │ ├── appContext │ │ │ ├── date-functions.ts │ │ │ ├── math-function.ts │ │ │ └── misc-utils.ts │ │ ├── AppContext.tsx │ │ ├── behaviors │ │ │ ├── Behavior.tsx │ │ │ └── CoreBehaviors.tsx │ │ ├── component-hooks.ts │ │ ├── ComponentDecorator.tsx │ │ ├── ComponentViewer.tsx │ │ ├── CompoundComponent.tsx │ │ ├── constants.ts │ │ ├── DebugViewProvider.tsx │ │ ├── descriptorHelper.ts │ │ ├── devtools │ │ │ ├── InspectorDialog.module.scss │ │ │ ├── InspectorDialog.tsx │ │ │ └── InspectorDialogVisibilityContext.tsx │ │ ├── EngineError.ts │ │ ├── event-handlers.ts │ │ ├── InspectorButton.module.scss │ │ ├── InspectorContext.tsx │ │ ├── interception │ │ │ ├── abstractions.ts │ │ │ ├── ApiInterceptor.ts │ │ │ ├── ApiInterceptorProvider.tsx │ │ │ ├── apiInterceptorWorker.ts │ │ │ ├── Backend.ts │ │ │ ├── Errors.ts │ │ │ ├── IndexedDb.ts │ │ │ ├── initMock.ts │ │ │ ├── InMemoryDb.ts │ │ │ ├── ReadonlyCollection.ts │ │ │ └── useApiInterceptorContext.tsx │ │ ├── loader │ │ │ ├── ApiLoader.tsx │ │ │ ├── DataLoader.tsx │ │ │ ├── ExternalDataLoader.tsx │ │ │ ├── Loader.tsx │ │ │ ├── MockLoaderRenderer.tsx │ │ │ └── PageableLoader.tsx │ │ ├── LoaderComponent.tsx │ │ ├── markup-check.ts │ │ ├── parts.ts │ │ ├── renderers.ts │ │ ├── rendering │ │ │ ├── AppContent.tsx │ │ │ ├── AppRoot.tsx │ │ │ ├── AppWrapper.tsx │ │ │ ├── buildProxy.ts │ │ │ ├── collectFnVarDeps.ts │ │ │ ├── ComponentAdapter.tsx │ │ │ ├── ComponentWrapper.tsx │ │ │ ├── Container.tsx │ │ │ ├── containers.ts │ │ │ ├── ContainerWrapper.tsx │ │ │ ├── ErrorBoundary.module.scss │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── InvalidComponent.module.scss │ │ │ ├── InvalidComponent.tsx │ │ │ ├── nodeUtils.ts │ │ │ ├── reducer.ts │ │ │ ├── renderChild.tsx │ │ │ ├── StandaloneComponent.tsx │ │ │ ├── StateContainer.tsx │ │ │ ├── UnknownComponent.module.scss │ │ │ ├── UnknownComponent.tsx │ │ │ └── valueExtractor.ts │ │ ├── reportEngineError.ts │ │ ├── RestApiProxy.ts │ │ ├── script-runner │ │ │ ├── asyncProxy.ts │ │ │ ├── AttributeValueParser.ts │ │ │ ├── bannedFunctions.ts │ │ │ ├── BindingTreeEvaluationContext.ts │ │ │ ├── eval-tree-async.ts │ │ │ ├── eval-tree-common.ts │ │ │ ├── eval-tree-sync.ts │ │ │ ├── ParameterParser.ts │ │ │ ├── process-statement-async.ts │ │ │ ├── process-statement-common.ts │ │ │ ├── process-statement-sync.ts │ │ │ ├── ScriptingSourceTree.ts │ │ │ ├── simplify-expression.ts │ │ │ ├── statement-queue.ts │ │ │ └── visitors.ts │ │ ├── StandaloneApp.tsx │ │ ├── StandaloneExtensionManager.ts │ │ ├── TableOfContentsContext.tsx │ │ ├── theming │ │ │ ├── _themes.scss │ │ │ ├── component-layout-resolver.ts │ │ │ ├── extendThemeUtils.ts │ │ │ ├── hvar.ts │ │ │ ├── layout-resolver.ts │ │ │ ├── parse-layout-props.ts │ │ │ ├── StyleContext.tsx │ │ │ ├── StyleRegistry.ts │ │ │ ├── ThemeContext.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ ├── themes │ │ │ │ ├── base-utils.ts │ │ │ │ ├── palette.ts │ │ │ │ ├── root.ts │ │ │ │ ├── solid.ts │ │ │ │ ├── theme-colors.ts │ │ │ │ └── xmlui.ts │ │ │ ├── themeVars.module.scss │ │ │ ├── themeVars.ts │ │ │ ├── transformThemeVars.ts │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── actionUtils.ts │ │ │ ├── audio-utils.ts │ │ │ ├── base64-utils.ts │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /xmlui/src/components/Form/FormContext.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { Dispatch } from "react"; 2 | import { createContext, useContext, useContextSelector } from "use-context-selector"; 3 | 4 | import type { ContainerAction } from "../../components-core/rendering/containers"; 5 | import type { FormAction } from "../Form/formActions"; 6 | import type { LabelPosition } from "../abstractions"; 7 | import type { PropertyValueDescription } from "../../abstractions/ComponentDefs"; 8 | 9 | export type InteractionFlags = { 10 | isDirty: boolean; 11 | invalidToValid: boolean; 12 | isValidOnFocus: boolean; 13 | isValidLostFocus: boolean; 14 | focused: boolean; 15 | afterFirstDirtyBlur: boolean; 16 | forceShowValidationResult: boolean; 17 | }; 18 | 19 | interface IFormContext { 20 | subject: Record<string, any>; 21 | originalSubject: Record<string, any>; 22 | validationResults: Record<string, ValidationResult>; 23 | interactionFlags: Record<string, InteractionFlags>; 24 | dispatch: Dispatch<ContainerAction | FormAction>; 25 | enabled?: boolean; 26 | itemLabelWidth?: string; 27 | itemLabelBreak?: boolean; 28 | itemLabelPosition?: string | LabelPosition; 29 | } 30 | 31 | export type ValidationResult = { 32 | isValid: boolean; 33 | validations: Array<SingleValidationResult>; 34 | validatedValue: any; 35 | partial: boolean; 36 | }; 37 | 38 | export type SingleValidationResult = { 39 | isValid: boolean; 40 | severity: ValidationSeverity; 41 | invalidMessage?: string; 42 | validMessage?: string; 43 | async?: boolean; 44 | stale?: boolean; 45 | fromBackend?: boolean; 46 | }; 47 | 48 | export interface FormItemValidations { 49 | required: boolean | undefined; 50 | requiredInvalidMessage: string | undefined; 51 | minLength: number | undefined; 52 | maxLength: number | undefined; 53 | lengthInvalidMessage: string | undefined; 54 | lengthInvalidSeverity: ValidationSeverity | undefined; 55 | minValue: number | undefined; 56 | maxValue: number | undefined; 57 | rangeInvalidMessage: string | undefined; 58 | rangeInvalidSeverity: ValidationSeverity | undefined; 59 | pattern: string | undefined; 60 | patternInvalidMessage: string | undefined; 61 | patternInvalidSeverity: ValidationSeverity | undefined; 62 | regex: string | undefined; 63 | regexInvalidMessage: string | undefined; 64 | regexInvalidSeverity: ValidationSeverity | undefined; 65 | } 66 | 67 | export const validationSeverityValues = ["error", "warning", "valid", "none"] as const; 68 | export type ValidationSeverity = (typeof validationSeverityValues)[number]; 69 | export const validationSeverityMd: PropertyValueDescription[] = [ 70 | { value: "valid", description: "Visual indicator for an input that is accepted" }, 71 | { value: "warning", description: "Visual indicator for an input that produced a warning" }, 72 | { value: "error", description: "Visual indicator for an input that produced an error" }, 73 | ]; 74 | 75 | export type ValidateEventHandler = ((value: any) => Promise<ValidateFunctionResult>) | undefined; 76 | 77 | type ValidateFunctionResult = boolean | SingleValidationResult | Array<SingleValidationResult>; 78 | 79 | export const validationModeValues = ["errorLate", "onChanged", "onLostFocus"] as const; 80 | export type ValidationMode = (typeof validationModeValues)[number]; 81 | export const defaultValidationMode = "errorLate"; 82 | export const validationModeMd: PropertyValueDescription[] = [ 83 | { 84 | value: "errorLate", 85 | description: 86 | "Display the error when the field loses focus." + 87 | "If an error is already displayed, continue for every keystroke until input is accepted.", 88 | }, 89 | { 90 | value: "onChanged", 91 | description: "Display error (if present) for every keystroke.", 92 | }, 93 | { 94 | value: "onLostFocus", 95 | description: "Show/hide error (if present) only if the field loses focus.", 96 | }, 97 | ]; 98 | 99 | export const FormContext = createContext<IFormContext>(undefined as unknown as IFormContext); 100 | 101 | export function useFormContextPart<T = unknown>(selector: (value?: IFormContext) => T) { 102 | return useContextSelector(FormContext, selector); 103 | } 104 | 105 | export function useIsInsideForm(){ 106 | const contextPart = useFormContextPart((value) => value?.dispatch); 107 | return contextPart !== undefined; 108 | } 109 | 110 | export const formControlTypes = [ 111 | "text", 112 | "password", 113 | "textarea", 114 | "checkbox", 115 | "number", 116 | "integer", 117 | "number2", 118 | "integer2", 119 | "file", 120 | "select", 121 | "autocomplete", 122 | "datePicker", 123 | "radioGroup", 124 | "custom", 125 | "switch", 126 | "slider", 127 | "colorpicker", 128 | "items", 129 | ] as const; 130 | 131 | export const formControlTypesMd: PropertyValueDescription[] = [ 132 | { 133 | value: "text", 134 | description: "Renders TextBox", 135 | }, 136 | { 137 | value: "password", 138 | description: "Renders TextBox with `password` type", 139 | }, 140 | { 141 | value: "textarea", 142 | description: "Renders Textarea", 143 | }, 144 | { 145 | value: "checkbox", 146 | description: "Renders Checkbox", 147 | }, 148 | { 149 | value: "number", 150 | description: "Renders NumberBox", 151 | }, 152 | { 153 | value: "integer", 154 | description: "Renders NumberBox with `integersOnly` set to true", 155 | }, 156 | { 157 | value: "file", 158 | description: "Renders FileInput", 159 | }, 160 | { 161 | value: "datePicker", 162 | description: "Renders DatePicker", 163 | }, 164 | { 165 | value: "radioGroup", 166 | description: "Renders RadioGroup", 167 | }, 168 | { 169 | value: "switch", 170 | description: "Renders Switch", 171 | }, 172 | { 173 | value: "select", 174 | description: "Renders Select", 175 | }, 176 | { 177 | value: "autocomplete", 178 | description: "Renders AutoComplete", 179 | }, 180 | { 181 | value: "slider", 182 | description: "Renders Slider", 183 | }, 184 | { 185 | value: "colorpicker", 186 | description: "Renders ColorPicker", 187 | }, 188 | { 189 | value: "items", 190 | description: "Renders Items", 191 | }, 192 | { 193 | value: "custom", 194 | description: 195 | "A custom control specified in children. If `type` is not specified " + 196 | "but the `FormItem` has children, it considers the control a custom one.", 197 | }, 198 | ]; 199 | export type FormControlType = (typeof formControlTypes)[number]; 200 | ``` -------------------------------------------------------------------------------- /docs/public/pages/components-intro.md: -------------------------------------------------------------------------------- ```markdown 1 | # Components 2 | 3 | We've already seen a number of XMLUI components in action: [DataSource](/components/DataSource), 4 | 5 | [Items](/components/Items), 6 | [List](/components/List), 7 | [Markdown](/components/Markdown), 8 | [Select](/components/Select), 9 | [Option](/components/Option), 10 | [Table](/components/Table). 11 | 12 | ## Built-in components 13 | 14 | As an XMLUI developer you'll create user interfaces by combining these with others in the [core library](/components/_overview), drawing from these categories: 15 | 16 | **Data**: [AppState](/components/AppState), 17 | [DataSource](/components/DataSource), 18 | [APICall](/components/APICall) ... 19 | 20 | **Display**: [Avatar](/components/Avatar), 21 | [Card](/components/Card), [Heading](/components/Heading), [Image](/components/Image), 22 | [Icon](/components/Icon), 23 | [Markdown](/components/Markdown), 24 | [Text](/components/Text), 25 | [Table](/components/Table) ... 26 | 27 | **Input**: [Checkbox](/components/Checkbox), 28 | [DatePicker](/components/DatePicker), [Form](/components/Form), 29 | [FormItem](/components/FormItem), 30 | [FileInput](/components/FileInput), 31 | [NumberBox](/components/NumberBox), 32 | [Select](/components/Select), 33 | [TextArea](/components/TextArea), 34 | [TextBox](/components/TextBox) ... 35 | 36 | **Layout**: [FlowLayout](/components/FlowLayout), [HStack](/components/HStack), [VStack](/components/VStack) ... 37 | 38 | **Navigation**: [DropdownMenu](/components/DropdownMenu), 39 | [MenuItem](/components/MenuItem), 40 | [NavLink](/components/NavLink), 41 | [NavPanel](/components/NavPanel), 42 | [Tabs](/components/Tabs), 43 | [TabItem](/components/TabItem) ... 44 | 45 | ## Custom components 46 | 47 | You'll also create your own components to combine and extend the built-ins. For example, here's a component that represents the stops on a London tube line. 48 | 49 | ```xmlui-pg 50 | ---app display /line/ 51 | <App> 52 | <TubeStops line="Bakerloo"/> 53 | </App> 54 | ---comp display /line/ 55 | <Component name="TubeStops"> 56 | <DataSource 57 | id="stops" 58 | when="{$props.line}" 59 | url="https://api.tfl.gov.uk/Line/{$props.line}/StopPoints" 60 | transformResult="{window.transformStops}" 61 | /> 62 | <Text variant="strong">{$props.line}</Text> 63 | 64 | <Table data="{stops}"> 65 | <Column width="3*" bindTo="name" /> 66 | <Column bindTo="zone" /> 67 | <Column bindTo="wifi" > 68 | <Fragment when="{$item.wifi === 'yes'}"> 69 | <Icon name="checkmark"/> 70 | </Fragment> 71 | </Column> 72 | <Column bindTo="toilets" > 73 | <Fragment when="{$item.toilets === 'yes'}"> 74 | <Icon name="checkmark"/> 75 | </Fragment> 76 | </Column> 77 | </Table> 78 | 79 | </Component> 80 | ``` 81 | 82 | ```javascript 83 | window.transformStops = function(stops) { 84 | return stops.map(function(stop) { 85 | // Helper to extract a value from additionalProperties by key 86 | function getProp(key) { 87 | if (!stop.additionalProperties) return ''; 88 | var propObj = stop.additionalProperties.find(function(p) { 89 | return p.key === key; 90 | }); 91 | return propObj ? propObj.value : ''; 92 | } 93 | return { 94 | name: stop.commonName, 95 | zone: getProp('Zone'), 96 | wifi: getProp('WiFi'), 97 | toilets: getProp('Toilets'), 98 | // A comma-separated list of line names that serve this stop 99 | lines: stop.lines 100 | ? stop.lines.map(function(line) { return line.name; }).join(', ') 101 | : '' 102 | }; 103 | }); 104 | } 105 | ``` 106 | 107 | An instance of `TubeStops` extracts details for a given tube line. Multiple instances can be arranged on the display using layout components. For example, here's a two-column layout. 108 | 109 | ```xmlui-pg display 110 | ---app display 111 | <App> 112 | <FlowLayout> 113 | <Stack width="*"> 114 | <TubeStops line="victoria"/> 115 | </Stack> 116 | <Stack width="*"> 117 | <TubeStops line="waterloo-city"/> 118 | </Stack> 119 | </FlowLayout> 120 | </App> 121 | ---comp 122 | <Component name="TubeStops"> 123 | <DataSource 124 | id="stops" 125 | when="{$props.line}" 126 | url="https://api.tfl.gov.uk/Line/{$props.line}/StopPoints" 127 | transformResult="{window.transformStops}" 128 | /> 129 | <Text variant="strong">{$props.line}</Text> 130 | 131 | <Table data="{stops}"> 132 | <Column width="3*" bindTo="name" /> 133 | <Column bindTo="zone" /> 134 | <Column bindTo="wifi" > 135 | <Fragment when="{$item.wifi === 'yes'}"> 136 | <Icon name="checkmark"/> 137 | </Fragment> 138 | </Column> 139 | <Column bindTo="toilets" > 140 | <Fragment when="{$item.toilets === 'yes'}"> 141 | <Icon name="checkmark"/> 142 | </Fragment> 143 | </Column> 144 | </Table> 145 | 146 | </Component> 147 | ``` 148 | 149 | 150 | The `TubeStops` component: 151 | 152 | - **Lives in the `components` folder**. The full path is `components/TubeStops.xmlui`. 153 | - **Can handle any properties passed in the `$props` [context variable](/context-variables)**. A calling component can send one or more `name="value"` pairs like `line="Bakerloo"`. 154 | - **Defines a dynamic data source**. When this page embeds `<TubeStops line="Bakerloo"/>`, the `TubeStops` component receives a `line` property used to form the URL that fetches data. 155 | - **Transforms data**. When API responses are complex, the expressions needed to unpack them can clutter your XMLUI markup. In this case the component offloads that work to the `transformStops` function so it can work with a simplified structure. 156 | - **Enriches data**. The transformed data has `yes`/`no` values for `wifi` and `toilets`. `TubeStops` uses [Fragment](/components/Fragment) to display an [Icon](/components/Icon) only for `yes` values. 157 | 158 | When you use custom components to access, transform, and present data, your XMLUI markup stays clean, readable, and easy to read and maintain. 159 | 160 | Another way to keep your markup clean: rely on XMLUI's layout and style engine to make your app look good and behave gracefully by default. You can adjust the (many!) settings that define an XMLUI [Theme](/components/Theme), but you'll rarely need to. The next chapter explains why. 161 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/AreaChart/AreaChartNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | AreaChart as RAreaChart, 3 | Area, 4 | CartesianGrid, 5 | XAxis, 6 | YAxis, 7 | ResponsiveContainer, 8 | Tooltip, 9 | Legend as RLegend, 10 | } from "recharts"; 11 | 12 | import type { ReactNode} from "react"; 13 | import { useEffect, useRef, useState, useCallback } from "react"; 14 | import { useMemo } from "react"; 15 | import ChartProvider, { useChartContextValue } from "../utils/ChartProvider"; 16 | import { TooltipContent } from "../Tooltip/TooltipContent"; 17 | import { useTheme } from "../../../components-core/theming/ThemeContext"; 18 | 19 | export type AreaChartProps = { 20 | data: any[]; 21 | nameKey: string; 22 | dataKeys?: string[]; 23 | className?: string; 24 | hideTickX?: boolean; 25 | hideTickY?: boolean; 26 | hideX?: boolean; 27 | hideY?: boolean; 28 | hideTooltip?: boolean; 29 | tickFormatterX?: (value: any) => any; 30 | tickFormatterY?: (value: any) => any; 31 | children?: ReactNode; 32 | showLegend?: boolean; 33 | stacked?: boolean; 34 | curved?: boolean; 35 | tooltipRenderer?: (tooltipData: any) => ReactNode; 36 | }; 37 | 38 | export const defaultProps: Pick< 39 | AreaChartProps, 40 | | "hideTickX" 41 | | "hideTickY" 42 | | "hideX" 43 | | "hideY" 44 | | "hideTooltip" 45 | | "tickFormatterX" 46 | | "tickFormatterY" 47 | | "showLegend" 48 | | "stacked" 49 | | "curved" 50 | > = { 51 | hideTickX: false, 52 | hideTickY: false, 53 | hideX: false, 54 | hideY: false, 55 | hideTooltip: false, 56 | tickFormatterX: (value) => value, 57 | tickFormatterY: (value) => value, 58 | showLegend: false, 59 | stacked: false, 60 | curved: false, 61 | }; 62 | 63 | export function AreaChart({ 64 | data = [], 65 | nameKey, 66 | dataKeys = [], 67 | hideTickX = defaultProps.hideTickX, 68 | hideTickY = defaultProps.hideTickY, 69 | hideY = defaultProps.hideY, 70 | hideX = defaultProps.hideX, 71 | hideTooltip = defaultProps.hideTooltip, 72 | tickFormatterX = defaultProps.tickFormatterX, 73 | tickFormatterY = defaultProps.tickFormatterY, 74 | className, 75 | children, 76 | showLegend = defaultProps.showLegend, 77 | stacked = defaultProps.stacked, 78 | curved = defaultProps.curved, 79 | tooltipRenderer, 80 | }: AreaChartProps) { 81 | // Validate and normalize data 82 | const validData = Array.isArray(data) ? data : []; 83 | const { getThemeVar } = useTheme(); 84 | 85 | const colorValues = useMemo(() => { 86 | return [ 87 | getThemeVar("color-primary-500"), 88 | getThemeVar("color-primary-300"), 89 | getThemeVar("color-success-500"), 90 | getThemeVar("color-success-300"), 91 | getThemeVar("color-warn-500"), 92 | getThemeVar("color-warn-300"), 93 | getThemeVar("color-danger-500"), 94 | getThemeVar("color-danger-300"), 95 | getThemeVar("color-info-500"), 96 | getThemeVar("color-info-300"), 97 | getThemeVar("color-secondary-500"), 98 | getThemeVar("color-secondary-300"), 99 | ]; 100 | }, [getThemeVar]); 101 | 102 | const config = useMemo(() => { 103 | return Object.assign( 104 | {}, 105 | ...dataKeys.map((key, index) => { 106 | return { 107 | [key]: { 108 | label: key, 109 | color: colorValues[index % colorValues.length], 110 | }, 111 | }; 112 | }), 113 | ); 114 | }, [colorValues, dataKeys]); 115 | 116 | const chartContextValue = useChartContextValue({ dataKeys, nameKey }); 117 | 118 | // Process data and create chart elements based on dataKeys 119 | const chartElements = useMemo(() => { 120 | return dataKeys.map((key, index) => { 121 | const color = colorValues[index % colorValues.length]; 122 | 123 | return ( 124 | <Area 125 | key={key} 126 | dataKey={key} 127 | fill={color} 128 | stroke={color} 129 | fillOpacity={0.6} 130 | strokeWidth={2} 131 | type={curved ? "monotone" : "linear"} 132 | stackId={stacked ? "1" : undefined} 133 | /> 134 | ); 135 | }); 136 | }, [dataKeys, colorValues, curved, stacked]); 137 | 138 | // Handle responsive behavior 139 | const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); 140 | const containerRef = useRef<HTMLDivElement>(null); 141 | 142 | useEffect(() => { 143 | const updateSize = () => { 144 | if (containerRef.current) { 145 | const { width, height } = containerRef.current.getBoundingClientRect(); 146 | setContainerSize({ width, height }); 147 | } 148 | }; 149 | 150 | updateSize(); 151 | window.addEventListener("resize", updateSize); 152 | return () => window.removeEventListener("resize", updateSize); 153 | }, []); 154 | 155 | // Determine if we're in mini mode (very small container) 156 | const isMiniMode = containerSize.height < 150; 157 | 158 | const safeTooltipRenderer = useCallback( 159 | (props: any) => { 160 | if (!tooltipRenderer) return <TooltipContent {...props} />; 161 | 162 | const payloadObject: Record<string, any> = {}; 163 | 164 | if (props.payload && props.payload.length > 0 && props.payload[0].payload) { 165 | Object.assign(payloadObject, props.payload[0].payload); 166 | } 167 | 168 | // Extract tooltip data from Recharts props 169 | const tooltipData = { 170 | label: props.label, 171 | payload: payloadObject, 172 | active: props.active, 173 | }; 174 | 175 | return tooltipRenderer(tooltipData); 176 | }, 177 | [tooltipRenderer], 178 | ); 179 | 180 | return ( 181 | <ChartProvider value={chartContextValue}> 182 | <div ref={containerRef} className={className}> 183 | <ResponsiveContainer width="100%" height="100%"> 184 | <RAreaChart data={validData}> 185 | {!hideX && ( 186 | <XAxis 187 | dataKey={nameKey} 188 | tick={!hideTickX} 189 | tickFormatter={tickFormatterX} 190 | hide={isMiniMode} 191 | /> 192 | )} 193 | {!hideY && <YAxis tick={!hideTickY} tickFormatter={tickFormatterY} hide={isMiniMode} />} 194 | <CartesianGrid strokeDasharray="3 3" /> 195 | {!isMiniMode && !hideTooltip && <Tooltip content={safeTooltipRenderer} />} 196 | {showLegend && !isMiniMode && <RLegend />} 197 | {chartElements} 198 | {children} 199 | </RAreaChart> 200 | </ResponsiveContainer> 201 | </div> 202 | </ChartProvider> 203 | ); 204 | } 205 | ``` -------------------------------------------------------------------------------- /docs/content/components/Stack.md: -------------------------------------------------------------------------------- ```markdown 1 | # Stack [#stack] 2 | 3 | `Stack` is the fundamental layout container that organizes child elements in configurable horizontal or vertical arrangements. As the most versatile building block in XMLUI's layout system, it provides comprehensive alignment, spacing, and flow control options that serve as the foundation for all specialized stack variants. 4 | 5 | **Key features:** 6 | - **Dynamic orientation**: Switch between horizontal and vertical layouts programmatically 7 | - **Comprehensive alignment**: Precise control over both horizontal and vertical child positioning 8 | - **Flexible spacing**: Configurable gaps between elements with theme-aware sizing 9 | - **Content wrapping**: Automatic wrapping when space constraints require it 10 | - **Order control**: Reverse child element order with the reverse property 11 | - **Foundation for variants**: Powers HStack, VStack, CHStack, and CVStack specialized components 12 | 13 | For common scenarios, consider the specialized variants: [HStack](/components/HStack) (horizontal), [VStack](/components/VStack) (vertical), [CHStack](/components/CHStack) (centered horizontal), and [CVStack](/components/CVStack) (centered vertical). 14 | 15 | ## Properties [#properties] 16 | 17 | ### `gap` (default: "$gap-normal") [#gap-default-gap-normal] 18 | 19 | Optional size value indicating the gap between child elements. 20 | 21 | In the following example we use pixels, characters (shorthand `ch`), and the `em` CSS unit size which is a relative size to the font size of the element (See size values). 22 | 23 | ```xmlui-pg copy {3, 10} display name="Example: gap" 24 | <App> 25 | <Stack orientation="horizontal" backgroundColor="cyan" 26 | gap="80px"> 27 | <Stack height="40px" width="40px" backgroundColor="red" /> 28 | <Stack height="40px" width="40px" backgroundColor="green" /> 29 | <Stack height="40px" width="40px" backgroundColor="blue" /> 30 | <Stack height="40px" width="40px" backgroundColor="yellow" /> 31 | </Stack> 32 | <Stack orientation="horizontal" backgroundColor="cyan" 33 | gap="12ch"> 34 | <Stack height="40px" width="40px" backgroundColor="red" /> 35 | <Stack height="40px" width="40px" backgroundColor="green" /> 36 | <Stack height="40px" width="40px" backgroundColor="blue" /> 37 | <Stack height="40px" width="40px" backgroundColor="yellow" /> 38 | </Stack> 39 | </App> 40 | ``` 41 | 42 | ### `horizontalAlignment` (default: "start") [#horizontalalignment-default-start] 43 | 44 | Manages the horizontal content alignment for each child element in the Stack. 45 | 46 | Available values: `start` **(default)**, `center`, `end` 47 | 48 | >[!INFO] 49 | > The `start` and `end` values can be affected by i18n if the layout is in a right-to-left writing style. 50 | 51 | ```xmlui-pg copy {3} display name="Example: horizontalAlignment" 52 | <App> 53 | <Stack width="100%" horizontalAlignment="center" backgroundColor="cyan"> 54 | <Stack width="36px" height="36px" backgroundColor="red" /> 55 | </Stack> 56 | </App> 57 | ``` 58 | 59 | ### `orientation` (default: "vertical") [#orientation-default-vertical] 60 | 61 | An optional property that governs the Stack's orientation (whether the Stack lays out its children in a row or a column). 62 | 63 | Available values: `horizontal`, `vertical` **(default)** 64 | 65 | ### `reverse` (default: false) [#reverse-default-false] 66 | 67 | Optional boolean property to reverse the order of child elements. 68 | 69 | Default is **false**, which indicates a left-to-right layout. 70 | 71 | ```xmlui-pg copy display name="Example: reverse" 72 | <App> 73 | <Stack backgroundColor="cyan"> 74 | <Stack gap="10px" orientation="horizontal"> 75 | <Stack height="40px" width="40px" backgroundColor="red" /> 76 | <Stack height="40px" width="40px" backgroundColor="green" /> 77 | <Stack height="40px" width="40px" backgroundColor="blue" /> 78 | </Stack> 79 | <Stack reverse="true" orientation="horizontal"> 80 | <Stack height="40px" width="40px" backgroundColor="red" /> 81 | <Stack height="40px" width="40px" backgroundColor="green" /> 82 | <Stack height="40px" width="40px" backgroundColor="blue" /> 83 | </Stack> 84 | </Stack> 85 | </App> 86 | ``` 87 | 88 | ### `verticalAlignment` (default: "start") [#verticalalignment-default-start] 89 | 90 | Manages the vertical content alignment for each child element in the Stack. 91 | 92 | Available values: `start` **(default)**, `center`, `end` 93 | 94 | ```xmlui-pg copy {2} display name="Example: verticalAlignment" 95 | <App> 96 | <Stack height="100px" verticalAlignment="end" backgroundColor="cyan"> 97 | <Stack width="36px" height="36px" backgroundColor="red" /> 98 | </Stack> 99 | </App> 100 | ``` 101 | 102 | ### `wrapContent` (default: false) [#wrapcontent-default-false] 103 | 104 | Optional boolean which wraps the content if set to true and the available space is not big enough. Works only with horizontal orientations. 105 | 106 | Optional boolean which wraps the content if set to true and the available space is not big enough. Works in all orientations. 107 | 108 | ```xmlui-pg copy display name="Example: wrapContent" 109 | <App> 110 | <Stack wrapContent="true" width="140px" orientation="horizontal" backgroundColor="cyan"> 111 | <Stack height="40px" width="40px" backgroundColor="blue" /> 112 | <Stack height="40px" width="40px" backgroundColor="blue" /> 113 | <Stack height="40px" width="40px" backgroundColor="blue" /> 114 | <Stack height="40px" width="40px" backgroundColor="blue" /> 115 | </Stack> 116 | </App> 117 | ``` 118 | 119 | ## Events [#events] 120 | 121 | ### `click` [#click] 122 | 123 | This event is triggered when the Stack is clicked. 124 | 125 | Describes the logic that fires when the component is clicked. 126 | 127 | ```xmlui-pg copy display name="Example: click" 128 | <App> 129 | <HStack var.shown="{false}"> 130 | <Stack height="40px" width="40px" backgroundColor="red" onClick="shown = !shown" /> 131 | <Stack when="{shown}" height="40px" width="40px" backgroundColor="blue" /> 132 | </HStack> 133 | </App> 134 | ``` 135 | 136 | ## Exposed Methods [#exposed-methods] 137 | 138 | This component does not expose any methods. 139 | 140 | ## Styling [#styling] 141 | 142 | This component does not have any styles. 143 | ``` -------------------------------------------------------------------------------- /tools/codefence/xmlui-code-fence-docs.md: -------------------------------------------------------------------------------- ```markdown 1 | # XMLUI Code Fence Syntax Documentation 2 | 3 | This document describes the various syntax options supported in XMLUI markdown code fences. 4 | 5 | ## Basic Fence Types 6 | 7 | ### Standard Code Display: `xmlui` 8 | ```xmlui 9 | <Button label="Hello World" /> 10 | ``` 11 | Basic code fence for showing XMLUI code without any special features. 12 | 13 | ### Playground: `xmlui-pg` 14 | ```xmlui-pg 15 | <Button label="Interactive Button" /> 16 | ``` 17 | Creates an interactive playground where the XMLUI code can be executed and displayed inline. 18 | 19 | ### Application Context: `---app` 20 | ``` 21 | ---app 22 | <App> 23 | <Button label="Full App Example" /> 24 | </App> 25 | ``` 26 | Used for showing complete application examples with full App context. 27 | 28 | ### Component Context: `---comp` 29 | ``` 30 | ---comp 31 | <Button label="Component Example" /> 32 | ``` 33 | Used for showing component-level examples. 34 | 35 | ## Supported Modifiers 36 | 37 | ### `copy` 38 | Adds a copy button to the code block, allowing users to copy the code to clipboard. 39 | 40 | ```xmlui copy 41 | <Button label="Copyable Code" /> 42 | ``` 43 | 44 | ```xmlui-pg copy 45 | <Button label="Copyable Playground" /> 46 | ``` 47 | 48 | ### `display` 49 | Shows the rendered output alongside or instead of just the code. 50 | 51 | ```xmlui-pg copy display 52 | <Button label="Both Code and Output" /> 53 | ``` 54 | 55 | ### `name="Example Name"` 56 | Provides a descriptive name for the example. 57 | 58 | ```xmlui-pg copy display name="Example: Basic Button" 59 | <Button label="Named Example" /> 60 | ``` 61 | 62 | ### `height="XXXpx"` 63 | Sets a specific height for the playground container. 64 | 65 | ```xmlui-pg copy display height="200px" 66 | <VStack> 67 | <Button label="Button 1" /> 68 | <Button label="Button 2" /> 69 | </VStack> 70 | ``` 71 | 72 | ### `filename="filename.xmlui"` 73 | Specifies a filename for the code block, useful for multi-file examples. 74 | 75 | ```xmlui copy filename="Main.xmlui" 76 | <App> 77 | <Button label="Main App" /> 78 | </App> 79 | ``` 80 | 81 | ## Line Highlighting: `{line-numbers}` 82 | 83 | Highlights specific lines in the code block. Supports various formats: 84 | 85 | ### Single Line 86 | ```xmlui copy {3} 87 | <App> 88 | <VStack> 89 | <Button label="This line is highlighted" /> 90 | </VStack> 91 | </App> 92 | ``` 93 | 94 | ### Multiple Lines (comma-separated) 95 | ```xmlui copy {2, 4, 6} 96 | <App> 97 | <VStack> 98 | <Button label="Button 1" /> 99 | <Button label="Button 2" /> 100 | <Button label="Button 3" /> 101 | </VStack> 102 | </App> 103 | ``` 104 | 105 | ### Line Ranges 106 | ```xmlui copy {2-4} 107 | <App> 108 | <VStack> 109 | <Button label="Button 1" /> 110 | <Button label="Button 2" /> 111 | </VStack> 112 | </App> 113 | ``` 114 | 115 | ### Mixed Ranges and Individual Lines 116 | ```xmlui copy {2-4, 7, 10-12} 117 | <!-- Complex highlighting example --> 118 | ``` 119 | 120 | ## Attribute Highlighting: `/attribute/` 121 | 122 | Highlights specific attributes, values, or patterns within the code using forward slash delimiters. 123 | 124 | ### Highlight Specific Attributes 125 | ```xmlui copy /variant="outlined"/ /themeColor="primary"/ 126 | <Button label="Highlighted Attributes" variant="outlined" themeColor="primary" /> 127 | ``` 128 | 129 | ### Highlight Attribute Names Only 130 | ```xmlui copy /name/ /size/ 131 | <Icon name="star" size="large" /> 132 | ``` 133 | 134 | ### Highlight Specific Values 135 | ```xmlui copy /direction="rtl"/ 136 | <Button label="Right-to-Left" direction="rtl" /> 137 | ``` 138 | 139 | ### Highlight IDs or References 140 | ```xmlui copy /#red/ /#green/ /#blue/ /id="red"/ /id="green"/ /id="blue"/ 141 | <VStack> 142 | <Bookmark id="red" /> 143 | <Bookmark id="green" /> 144 | <Bookmark id="blue" /> 145 | </VStack> 146 | ``` 147 | 148 | ### Highlight Method Names or Complex Values 149 | ```xmlui copy /onSelect="(emoji) => { selected = emoji }"/ 150 | <EmojiSelector onSelect="(emoji) => { selected = emoji }" /> 151 | ``` 152 | 153 | ```xmlui copy /loggedInUser="{{ name: 'Joe', token: '1234' }}"/ 154 | <App loggedInUser="{{ name: 'Joe', token: '1234' }}" /> 155 | ``` 156 | 157 | ### Multiple Attribute Highlights 158 | You can highlight multiple different attributes in the same code block: 159 | 160 | ```xmlui copy /orientation="horizontal"/ /showAvatar="true"/ /subtitle="Example"/ 161 | <Card orientation="horizontal" showAvatar="true" subtitle="Example" /> 162 | ``` 163 | 164 | ### Red Border Highlighting: `!/pattern/` 165 | Using `!` before the forward slash delimiters produces a red border instead of the standard highlight: 166 | 167 | ```xmlui !/{ 6 * 7 }/ 168 | <Text value="Life, the universe, and everything: { 6 * 7 }" /> 169 | ``` 170 | 171 | ## Complete Examples 172 | 173 | ### Full-Featured Playground 174 | ```xmlui-pg copy display name="Example: Button Variants" height="300px" {3-5} 175 | <App> 176 | <VStack gap="8px"> 177 | <Button label="Solid" variant="solid" /> 178 | <Button label="Outlined" variant="outlined" /> 179 | <Button label="Ghost" variant="ghost" /> 180 | </VStack> 181 | </App> 182 | ``` 183 | 184 | ### Multi-File Example with App Context 185 | ``` 186 | ---app copy display filename="Main.xmlui" height="200px" 187 | <App> 188 | <Component1 /> 189 | </App> 190 | 191 | ---comp copy display filename="Component1.xmlui" 192 | <VStack> 193 | <Button label="Component Button" /> 194 | </VStack> 195 | ``` 196 | 197 | ## Modifier Combinations 198 | 199 | All modifiers can be combined in various ways: 200 | 201 | - `copy + display` = Shows both code and rendered output with copy functionality 202 | - `copy + display + name` = Named example with copy and display 203 | - `copy + display + height` = Sized playground with copy and display 204 | - `copy + {lines}` = Copyable code with line highlighting 205 | - `filename + any combination` = File-specific examples 206 | 207 | ## Context-Specific Usage 208 | 209 | ### In Component Documentation 210 | - Use `xmlui-pg` for interactive examples that users can modify 211 | - Use `xmlui copy` for code snippets users will copy into their projects 212 | - Use line highlighting to draw attention to specific properties or patterns 213 | 214 | ### In Multi-File Examples 215 | - Use `---app` for the main application file 216 | - Use `---comp` for individual component files 217 | - Always include `filename` for clarity in multi-file scenarios 218 | 219 | ### For Complex Layouts 220 | - Use `height` to ensure adequate space for the rendered output 221 | - Use `name` to provide clear context for what the example demonstrates 222 | - Combine `display` with appropriate sizing for optimal presentation 223 | 224 | 225 | ### Descriptions 226 | 227 | Use `---desc` to inject narrative markdown. ``` -------------------------------------------------------------------------------- /xmlui/src/components/ModalDialog/ModalDialog.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Overlay presentation**: Appears above existing content with backdrop dimming 5 | - **Programmatic control**: Open and close via exposed methods like `open()` and `close()` 6 | - **Parameter passing**: Accept data when opened for dynamic dialog content 7 | - **Focus management**: Automatically handles focus trapping and accessibility 8 | - **Form integration**: When containing Form components, automatically closes on form submission or cancellation (unless overridden) 9 | 10 | ## Using the Component 11 | 12 | >[!INFO] 13 | > When using the examples in this article, pop them out to the full screen to check how they work. 14 | 15 | Opening and closing the modal dialog can be done in two ways depending on circumstances. 16 | 17 | ### With Imperative API 18 | 19 | Event-driven display of the `ModalDialog` dialog is also possible using imperative API. 20 | 21 | This method is a good way to toggle the display of the `ModalDialog` if no deep linking is necessary. 22 | It also lends to itself that these events can be triggered programmatically from codebehind. 23 | 24 | Note the `id` property of the `ModalDialog` in the example below and how it is used to call the [`open`](#open-api) and [`close`](#close-api) 25 | operations of the component in the `onClick` event handlers. 26 | 27 | ```xmlui-pg copy display name="Example: imperative API" height="220px" 28 | <App> 29 | <ModalDialog id="dialog" title="Example Dialog"> 30 | <Button label="Close Dialog" onClick="dialog.close()" /> 31 | </ModalDialog> 32 | <Button label="Open Dialog" onClick="dialog.open()" /> 33 | </App> 34 | ``` 35 | 36 | >[!INFO] 37 | > The imperative approach is perhaps the most intuitive way to display and hide modal dialogs. 38 | 39 | ### With `when` 40 | 41 | The `when` property accepts a primitive boolean or a binding expression resolving to a boolean value to toggle the display of a component. 42 | 43 | Using the `when` property in a `ModalDialog` dialog component is commonly used with deep linking: 44 | showing the modal in conjunction with an updated URL so that the opened state of the modal dialog is referable. 45 | 46 | ```xmlui-pg height="220px" 47 | ---app copy display name="Example: when" 48 | <App> 49 | <variable name="isDialogShown" value="{false}"/> 50 | <Button label="Open Dialog" onClick="isDialogShown = true" /> 51 | <ModalDialog 52 | when="{isDialogShown}" 53 | title="Example Dialog" 54 | onClose="isDialogShown = false" /> 55 | </App> 56 | ---desc 57 | Click on the button in the demo below to open the modal dialog. Click anywhere outside the opened dialog or the close button to close it. 58 | ``` 59 | 60 | Setting the `when` property is the most straightforward way for deep-linked modals. If you use deep links with query parameters to show a particular dialog, you can set the `when` property to show or hide the dialog according to parameter values. 61 | 62 | ### The `ModalDialog` as a Container 63 | 64 | The `ModalDialog` component is also a container such as the [`Card`](/components/Card), that it also accepts child components. 65 | 66 | ```xmlui-pg copy {3-8} display name="Example: children" height="340px" 67 | <App> 68 | <Button label="Open Dialog" onClick="dialog.open()" /> 69 | <ModalDialog id="dialog" title="Example Dialog"> 70 | <Form data="{{ firstName: 'Billy', lastName: 'Bob' }}"> 71 | <FormItem bindTo="firstName" required="true" /> 72 | <FormItem bindTo="lastName" required="true" /> 73 | </Form> 74 | </ModalDialog> 75 | </App> 76 | ``` 77 | 78 | >[!INFO] 79 | > When a form is nested into a modal dialog, closing the form (canceling it or completing its submit action) automatically closes the dialog. 80 | 81 | %-DESC-END 82 | 83 | %-PROP-START fullScreen 84 | 85 | ```xmlui-pg height="220px" 86 | ---app copy display name="Example: fullScreen" 87 | <App> 88 | <Button label="Open Dialog" onClick="dialog.open()" /> 89 | <ModalDialog id="dialog" fullScreen="true" title="Example Dialog" /> 90 | </App> 91 | ---desc 92 | Click the button to display a full-screen dialog. The icon at the top-right corner of the dialog allows you to close it. 93 | ``` 94 | 95 | %-PROP-END 96 | 97 | %-PROP-START title 98 | 99 | ```xmlui-pg copy {3} display name="Example: title" height="220px" 100 | <App> 101 | <Button label="Open Dialog" onClick="dialog.open()" /> 102 | <ModalDialog id="dialog" title="Example Title" /> 103 | </App> 104 | ``` 105 | 106 | %-PROP-END 107 | 108 | %-PROP-START closeButtonVisible 109 | 110 | ```xmlui-pg height="220px" 111 | ---app copy display name="Example: closeButtonVisible" 112 | <App> 113 | <Button label="Open Dialog" onClick="dialog.open()" /> 114 | <ModalDialog id="dialog" closeButtonVisible="false" title="Example Dialog" /> 115 | </App> 116 | ---desc 117 | Click outside the dialog to close it. 118 | ``` 119 | 120 | %-PROP-END 121 | 122 | %-EVENT-START close 123 | 124 | In this example, the `close` event counts how many times you closed the dialog: 125 | 126 | ```xmlui-pg height="220px" 127 | ---app copy {6-8} display name="Example: open/close events" 128 | <App> 129 | <Button label="Open Dialog" onClick="myDialog.open()" /> 130 | <ModalDialog 131 | id="myDialog" 132 | title="Example Dialog" 133 | var.counter="{0}" 134 | onClose="counter++"> 135 | <Text value="Dialog closed {counter} number of times." /> 136 | </ModalDialog> 137 | </App> 138 | ---desc 139 | Open and close the dialog several times to test that it changes the counter. 140 | ``` 141 | 142 | %-EVENT-END 143 | 144 | %-EVENT-START open 145 | 146 | In this example, the `open` event counts how many times you opened the dialog: 147 | 148 | ```xmlui-pg height="220px" 149 | ---app copy {6-8} display name="Example: open/close events" 150 | <App> 151 | <Button label="Open Dialog" onClick="myDialog.open()" /> 152 | <ModalDialog 153 | id="myDialog" 154 | title="Example Dialog" 155 | var.counter="{0}" 156 | onOpen="counter++"> 157 | <Text value="Dialog opened {counter} number of times." /> 158 | </ModalDialog> 159 | </App> 160 | ---desc 161 | Open and close the dialog several times to test that it changes the counter. 162 | ``` 163 | 164 | %-EVENT-END 165 | 166 | %-API-START open 167 | 168 | See the [\`With Imperative API\`](#with-imperative-api) subsection for an example. 169 | 170 | %-API-END 171 | 172 | %-API-START close 173 | 174 | See the [\`With Imperative API\`](#with-imperative-api) subsection for an example. 175 | 176 | %-API-END 177 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/interception/orderBy.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it } from "vitest"; 2 | import { orderBy } from "../../../src/components-core/utils/misc" 3 | 4 | const numArray = [3,4,1,2,5]; 5 | const strArray = ["beta", "delta", "alpha", "gamma", "charlie"] 6 | const objArray = [ 7 | { key: "hey", value: 1}, 8 | { key: "bye", value: 114}, 9 | { key: "hey", value: 3}, 10 | { key: "bye", value: 41}, 11 | { key: "ok", value: 1000}, 12 | ] 13 | 14 | describe("orderBy", () => { 15 | it("single number", async () => { 16 | // --- Act 17 | const res = await orderBy(numArray, async (i: any) => i); 18 | 19 | // --- Assert 20 | expect(res[0]).equal(1); 21 | expect(res[1]).equal(2); 22 | expect(res[2]).equal(3); 23 | expect(res[3]).equal(4); 24 | expect(res[4]).equal(5); 25 | }); 26 | 27 | it("single number, desc", async () => { 28 | // --- Act 29 | const res = await orderBy(numArray, async (i: any) => i, true); 30 | 31 | // --- Assert 32 | expect(res[0]).equal(5); 33 | expect(res[1]).equal(4); 34 | expect(res[2]).equal(3); 35 | expect(res[3]).equal(2); 36 | expect(res[4]).equal(1); 37 | }); 38 | 39 | it("single string", async () => { 40 | // --- Act 41 | const res = await orderBy(strArray, async (i: any) => i); 42 | 43 | // --- Assert 44 | expect(res[0]).equal("alpha"); 45 | expect(res[1]).equal("beta"); 46 | expect(res[2]).equal("charlie"); 47 | expect(res[3]).equal("delta"); 48 | expect(res[4]).equal("gamma"); 49 | }); 50 | 51 | it("single string, desc", async () => { 52 | // --- Act 53 | const res = await orderBy(strArray, async (i: any) => i, true); 54 | 55 | // --- Assert 56 | expect(res[0]).equal("gamma"); 57 | expect(res[1]).equal("delta"); 58 | expect(res[2]).equal("charlie"); 59 | expect(res[3]).equal("beta"); 60 | expect(res[4]).equal("alpha"); 61 | }); 62 | 63 | it("single object, string mapper", async () => { 64 | // --- Act 65 | const res = await orderBy(objArray, "value"); 66 | 67 | // --- Assert 68 | expect(res[0].value).equal(1); 69 | expect(res[1].value).equal(3); 70 | expect(res[2].value).equal(41); 71 | expect(res[3].value).equal(114); 72 | expect(res[4].value).equal(1000); 73 | }); 74 | 75 | it("single object, string mapper, desc", async () => { 76 | // --- Act 77 | const res = await orderBy(objArray, "value", true); 78 | 79 | // --- Assert 80 | expect(res[0].value).equal(1000); 81 | expect(res[1].value).equal(114); 82 | expect(res[2].value).equal(41); 83 | expect(res[3].value).equal(3); 84 | expect(res[4].value).equal(1); 85 | }); 86 | 87 | it("single object, func mapper", async () => { 88 | // --- Act 89 | const res = await orderBy(objArray, async (i: any) => i.value); 90 | 91 | // --- Assert 92 | expect(res[0].value).equal(1); 93 | expect(res[1].value).equal(3); 94 | expect(res[2].value).equal(41); 95 | expect(res[3].value).equal(114); 96 | expect(res[4].value).equal(1000); 97 | }); 98 | 99 | it("single object, func mapper, desc", async () => { 100 | // --- Act 101 | const res = await orderBy(objArray, async (i: any) => i.value, true); 102 | 103 | // --- Assert 104 | expect(res[0].value).equal(1000); 105 | expect(res[1].value).equal(114); 106 | expect(res[2].value).equal(41); 107 | expect(res[3].value).equal(3); 108 | expect(res[4].value).equal(1); 109 | }); 110 | 111 | it("multiple object #1", async () => { 112 | // --- Act 113 | const res = await orderBy(objArray, async (i: any) => i.key, async (i: any) => i.value); 114 | 115 | // --- Assert 116 | expect(res[0].key).equal("bye"); 117 | expect(res[0].value).equal(41); 118 | expect(res[1].key).equal("bye"); 119 | expect(res[1].value).equal(114); 120 | expect(res[2].key).equal("hey"); 121 | expect(res[2].value).equal(1); 122 | expect(res[3].key).equal("hey"); 123 | expect(res[3].value).equal(3); 124 | expect(res[4].key).equal("ok"); 125 | expect(res[4].value).equal(1000); 126 | }); 127 | 128 | it("multiple object #2", async () => { 129 | // --- Act 130 | const res = await orderBy(objArray, async (i: any) => i.key, true, async (i: any) => i.value); 131 | 132 | // --- Assert 133 | expect(res[0].key).equal("ok"); 134 | expect(res[0].value).equal(1000); 135 | expect(res[1].key).equal("hey"); 136 | expect(res[1].value).equal(1); 137 | expect(res[2].key).equal("hey"); 138 | expect(res[2].value).equal(3); 139 | expect(res[3].key).equal("bye"); 140 | expect(res[3].value).equal(41); 141 | expect(res[4].key).equal("bye"); 142 | expect(res[4].value).equal(114); 143 | }); 144 | 145 | it("multiple object #3", async () => { 146 | // --- Act 147 | const res = await orderBy(objArray, async (i: any) => i.key, async (i: any) => i.value, true); 148 | 149 | // --- Assert 150 | expect(res[0].key).equal("bye"); 151 | expect(res[0].value).equal(114); 152 | expect(res[1].key).equal("bye"); 153 | expect(res[1].value).equal(41); 154 | expect(res[2].key).equal("hey"); 155 | expect(res[2].value).equal(3); 156 | expect(res[3].key).equal("hey"); 157 | expect(res[3].value).equal(1); 158 | expect(res[4].key).equal("ok"); 159 | expect(res[4].value).equal(1000); 160 | }); 161 | 162 | it("multiple object #4", async () => { 163 | // --- Act 164 | const res = await orderBy(objArray, async (i: any) => i.key, true, async (i: any) => i.value, true); 165 | 166 | // --- Assert 167 | expect(res[0].key).equal("ok"); 168 | expect(res[0].value).equal(1000); 169 | expect(res[1].key).equal("hey"); 170 | expect(res[1].value).equal(3); 171 | expect(res[2].key).equal("hey"); 172 | expect(res[2].value).equal(1); 173 | expect(res[3].key).equal("bye"); 174 | expect(res[3].value).equal(114); 175 | expect(res[4].key).equal("bye"); 176 | expect(res[4].value).equal(41); 177 | }); 178 | }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/RadioGroup/RadioGroup.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $gap-RadioGroupOption: createThemeVar("gap-RadioGroupOption"); 11 | 12 | // --- Borders 13 | 14 | $borderWidth-RadioGroupOption: createThemeVar("borderWidth-RadioGroupOption"); 15 | $borderWidth-RadioGroupOption-validation: createThemeVar("borderWidth-RadioGroupOption-validation"); 16 | 17 | $borderColor-RadioGroupOption-default: createThemeVar("Input:borderColor-RadioGroupOption-default"); 18 | $borderColor-checked-RadioGroupOption: createThemeVar("Input:borderColor-checked-RadioGroupOption"); 19 | $borderColor-RadioGroupOption-default--hover: createThemeVar( 20 | "Input:borderColor-RadioGroupOption-default--hover" 21 | ); 22 | $borderColor-RadioGroupOption-default--active: createThemeVar( 23 | "Input:borderColor-RadioGroupOption-default--active" 24 | ); 25 | $borderColor-RadioGroupOption--disabled: createThemeVar( 26 | "Input:borderColor-RadioGroupOption--disabled" 27 | ); 28 | 29 | $borderColor-RadioGroupOption-error: createThemeVar("Input:borderColor-RadioGroupOption-error"); 30 | $borderColor-RadioGroupOption-warning: createThemeVar("Input:borderColor-RadioGroupOption-warning"); 31 | $borderColor-RadioGroupOption-success: createThemeVar("Input:borderColor-RadioGroupOption-success"); 32 | 33 | // --- Backgrounds 34 | 35 | $backgroundColor-RadioGroupOption-default: createThemeVar( 36 | "Input:backgroundColor-RadioGroupOption-default" 37 | ); 38 | $backgroundColor-checked-RadioGroupOption: createThemeVar( 39 | "Input:backgroundColor-checked-RadioGroupOption" 40 | ); 41 | $backgroundColor-checked-RadioGroupOption--disabled: createThemeVar( 42 | "Input:backgroundColor-checked-RadioGroupOption--disabled" 43 | ); 44 | 45 | // --- Foreground 46 | 47 | $color-RadioGroupOption--disabled: createThemeVar("Input:color-RadioGroupOption--disabled"); 48 | 49 | // --- Text 50 | 51 | $fontSize-RadioGroupOption: createThemeVar("Input:fontSize-RadioGroupOption"); 52 | $fontWeight-RadioGroupOption: createThemeVar("Input:fontWeight-RadioGroupOption"); 53 | 54 | $textColor-RadioGroupOption-default: createThemeVar("Input:textColor-RadioGroupOption-default"); 55 | $textColor-RadioGroupOption-error: createThemeVar("Input:textColor-RadioGroupOption-error"); 56 | $textColor-RadioGroupOption-warning: createThemeVar("Input:textColor-RadioGroupOption-warning"); 57 | $textColor-RadioGroupOption-success: createThemeVar("Input:textColor-RadioGroupOption-success"); 58 | 59 | 60 | @layer components { 61 | .radioGroupContainer { 62 | width: 100%; 63 | display: flex; 64 | flex-direction: column; 65 | gap: t.$space-2; 66 | } 67 | 68 | .radioOptionContainer { 69 | display: flex; 70 | align-items: center; 71 | gap: $gap-RadioGroupOption; 72 | } 73 | 74 | .radioOption { 75 | flex-shrink: 0; 76 | width: 18px; 77 | height: 18px; 78 | border-radius: 100%; 79 | background: $backgroundColor-RadioGroupOption-default; 80 | border-width: $borderWidth-RadioGroupOption; 81 | border-style: solid; 82 | border-color: $borderColor-RadioGroupOption-default; 83 | padding: 0; 84 | cursor: pointer; 85 | 86 | &:hover { 87 | border-color: $borderColor-RadioGroupOption-default--hover; 88 | } 89 | &:focus-visible { 90 | outline-width: createThemeVar("Input:outlineWidth-RadioGroupOption--focus"); 91 | outline-color: createThemeVar("Input:outlineColor-RadioGroupOption--focus"); 92 | outline-style: createThemeVar("Input:outlineStyle-RadioGroupOption--focus"); 93 | outline-offset: createThemeVar("Input:outlineOffset-RadioGroupOption--focus"); 94 | } 95 | &:disabled { 96 | cursor: not-allowed; 97 | border-color: $borderColor-RadioGroupOption--disabled; 98 | } 99 | &:disabled + label { 100 | cursor: not-allowed; 101 | color: $color-RadioGroupOption--disabled; 102 | } 103 | &:not(:disabled) { 104 | &:not(.error):not(.warning):not(.valid) { 105 | border-color: $borderColor-checked-RadioGroupOption; 106 | } 107 | &.error { 108 | border-width: $borderWidth-RadioGroupOption-validation; 109 | border-color: $borderColor-RadioGroupOption-error; 110 | } 111 | &.warning { 112 | border-width: $borderWidth-RadioGroupOption-validation; 113 | border-color: $borderColor-RadioGroupOption-warning; 114 | } 115 | &.valid { 116 | border-width: $borderWidth-RadioGroupOption-validation; 117 | border-color: $borderColor-RadioGroupOption-success; 118 | } 119 | } 120 | } 121 | 122 | // The indicator is the circle that appears inside the radio button: 123 | // both the larger circle and an inner circle. 124 | .indicator { 125 | display: grid; 126 | place-content: center; 127 | width: 100%; 128 | height: 100%; 129 | border-radius: 50%; 130 | background: radial-gradient( 131 | circle, 132 | transparent 28%, 133 | $backgroundColor-checked-RadioGroupOption 34% 134 | ); 135 | 136 | &.disabled { 137 | background: radial-gradient( 138 | circle, 139 | transparent 28%, 140 | $backgroundColor-checked-RadioGroupOption--disabled 34% 141 | ); 142 | } 143 | } 144 | 145 | .itemContainer { 146 | z-index: -1; 147 | position: relative; 148 | opacity: 0; 149 | width: 0; 150 | height: 0; 151 | } 152 | 153 | .optionLabel { 154 | width: 100%; 155 | cursor: pointer; 156 | } 157 | 158 | .label { 159 | width: 100%; 160 | color: $textColor-RadioGroupOption-default; 161 | font-size: $fontSize-RadioGroupOption; 162 | font-weight: $fontWeight-RadioGroupOption; 163 | user-select: none; 164 | cursor: pointer; 165 | 166 | &.error { 167 | color: $textColor-RadioGroupOption-error; 168 | } 169 | &.warning { 170 | color: $textColor-RadioGroupOption-warning; 171 | } 172 | &.valid { 173 | color: $textColor-RadioGroupOption-success; 174 | } 175 | } 176 | } 177 | 178 | // --- We export the theme variables to add them to the component renderer 179 | :export { 180 | themeVars: t.json-stringify($themeVars); 181 | } 182 | ``` -------------------------------------------------------------------------------- /docs/public/resources/files/clients.json: -------------------------------------------------------------------------------- ```json 1 | [{"id":28,"name":"Abstergo Industries","email":"[email protected]","phone":"193-387-4195","address":"685 Commerce Way, New York, WY 71341","created_at":"2023-04-25T10:39:54.288Z"}, 2 | {"id":1,"name":"Acme Corp","email":"[email protected]","phone":"837-171-5594","address":"1936 Mountain View, Minneapolis, OH 24211","created_at":"2023-05-30T18:18:58.754Z"}, 3 | {"id":29,"name":"Axiom Telecommunication","email":"[email protected]","phone":"964-696-9437","address":"596 Broadway, San Antonio, FL 37788","created_at":"2023-02-19T14:14:02.199Z"}, 4 | {"id":26,"name":"Beneke Fabricators","email":"[email protected]","phone":"224-643-3157","address":"5769 Commerce Way, New York, ME 10531","created_at":"2022-07-02T05:59:43.216Z"}, 5 | {"id":21,"name":"Bluth Company","email":"[email protected]","phone":"431-671-1197","address":"2136 Tech Plaza, Denver, MN 79275","created_at":"2023-06-01T02:16:06.550Z"}, 6 | {"id":8,"name":"Cyberdyne Systems","email":"[email protected]","phone":"958-492-4123","address":"959 Market St, Miami, NE 90247","created_at":"2023-04-21T23:33:46.662Z"}, 7 | {"id":13,"name":"Dunder Mifflin","email":"[email protected]","phone":"606-327-8844","address":"471 Main St, San Jose, AR 15771","created_at":"2023-06-15T15:59:30.585Z"}, 8 | {"id":15,"name":"Gekko & Co","email":"billing@gekko&co.hotmail.com","phone":"649-277-3273","address":"9537 Commerce Way, Jacksonville, CA 42531","created_at":"2022-10-21T00:08:26.253Z"}, 9 | {"id":27,"name":"Genco Pura Oil","email":"[email protected]","phone":"318-505-3974","address":"6930 Lake St, San Jose, NE 96152","created_at":"2023-03-25T05:54:13.340Z"}, 10 | {"id":2,"name":"Globex Corporation","email":"[email protected]","phone":"961-780-9184","address":"1381 Park Ave, Detroit, IL 10647","created_at":"2023-03-18T01:01:50.040Z"}, 11 | {"id":24,"name":"Goliath National Bank","email":"[email protected]","phone":"648-346-7002","address":"3230 Oak Ave, Chicago, GA 30285","created_at":"2023-04-17T10:56:27.700Z"}, 12 | {"id":30,"name":"Gringotts Bank","email":"[email protected]","phone":"404-501-6883","address":"8738 Pine St, Detroit, MO 58497","created_at":"2022-10-07T04:48:18.939Z"}, 13 | {"id":11,"name":"Hooli","email":"[email protected]","phone":"706-613-2435","address":"1756 Main St, San Antonio, MI 39851","created_at":"2023-03-19T15:28:23.979Z"}, 14 | {"id":4,"name":"Initech","email":"[email protected]","phone":"397-163-5711","address":"4245 Lake St, Dallas, VT 98968","created_at":"2022-07-03T11:41:47.875Z"}, 15 | {"id":18,"name":"Iron Mountain","email":"[email protected]","phone":"927-752-2353","address":"9905 Oak Ave, San Jose, OR 92222","created_at":"2022-03-07T13:20:09.399Z"}, 16 | {"id":17,"name":"Los Pollos Hermanos","email":"[email protected]","phone":"348-921-6305","address":"182 River Rd, San Jose, WA 90187","created_at":"2022-08-30T17:06:50.456Z"}, 17 | {"id":9,"name":"Massive Dynamic","email":"[email protected]","phone":"774-813-4144","address":"4589 Elm St, Boston, CO 13343","created_at":"2023-04-17T01:46:05.229Z"}, 18 | {"id":14,"name":"Oceanic Airlines","email":"[email protected]","phone":"875-430-4773","address":"5831 Cedar Ln, Houston, NY 53770","created_at":"2022-10-27T00:03:43.543Z"}, 19 | {"id":25,"name":"Oscorp Industries","email":"[email protected]","phone":"619-459-5592","address":"4630 Commerce Way, San Antonio, ND 78191","created_at":"2022-08-05T16:47:42.371Z"}, 20 | {"id":12,"name":"Pied Piper","email":"[email protected]","phone":"293-941-4381","address":"7511 Lake St, Seattle, ID 55253","created_at":"2023-07-21T18:35:59.514Z"}, 21 | {"id":22,"name":"Prestige Worldwide","email":"[email protected]","phone":"962-697-5383","address":"5031 Pine St, Dallas, MD 80661","created_at":"2022-11-18T19:01:46.865Z"}, 22 | {"id":3,"name":"Soylent Corp","email":"[email protected]","phone":"289-702-4140","address":"4433 Oak Ave, San Antonio, WV 87396","created_at":"2023-06-19T12:06:22.986Z"}, 23 | {"id":16,"name":"Stanfield LLC","email":"[email protected]","phone":"100-568-1916","address":"7480 Mountain View, Denver, IN 43223","created_at":"2022-09-24T03:40:55.798Z"}, 24 | {"id":6,"name":"Stark Industries","email":"[email protected]","phone":"996-381-2579","address":"9117 Washington Ave, San Diego, WY 73075","created_at":"2022-07-28T02:36:32.356Z"}, 25 | {"id":20,"name":"Sterling Cooper","email":"[email protected]","phone":"431-927-9146","address":"9432 River Rd, Los Angeles, NY 26752","created_at":"2022-08-24T19:23:11.564Z"}, 26 | {"id":5,"name":"Umbrella Corporation","email":"[email protected]","phone":"857-505-4462","address":"3783 Maple Dr, San Jose, MI 70268","created_at":"2022-08-06T05:02:45.139Z"}, 27 | {"id":23,"name":"Vehement Capital Partners","email":"[email protected]","phone":"785-799-5617","address":"3625 Broadway, Minneapolis, MN 30364","created_at":"2022-02-25T15:45:54.843Z"}, 28 | {"id":7,"name":"Wayne Enterprises","email":"[email protected]","phone":"719-975-7720","address":"1501 Commerce Way, San Jose, MS 86832","created_at":"2022-10-19T05:19:32.363Z"}, 29 | {"id":10,"name":"Waystar Royco","email":"[email protected]","phone":"935-654-8514","address":"1413 Elm St, Minneapolis, ND 43976","created_at":"2022-10-26T12:58:51.059Z"}, 30 | {"id":19,"name":"Weyland-Yutani","email":"[email protected]","phone":"713-592-8787","address":"8040 Maple Dr, Atlanta, ND 12004","created_at":"2022-11-03T03:38:22.119Z"}] 31 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/utils/date-utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { format, formatRelative, isThisWeek, isToday, isTomorrow, isYesterday } from "date-fns"; 2 | import { humanReadableDateTimeTillNow } from "../utils/misc"; 3 | 4 | export function isoDateString (date?: string) { 5 | return (!date ? new Date() : new Date(date)).toJSON(); 6 | } 7 | 8 | export function formatDate (date: string | Date, formatString?: string) { 9 | if (formatString) { 10 | return format(new Date(date), formatString); 11 | } 12 | return new Date(date).toLocaleDateString(); 13 | } 14 | 15 | export function formatDateTime (date: string | Date, formatString?: string) { 16 | if (formatString) { 17 | return format(new Date(date), formatString); 18 | } 19 | return new Date(date).toLocaleString(); 20 | } 21 | 22 | export function formatTime (date: string | Date, formatString?: string) { 23 | if (formatString) { 24 | return format(new Date(date), formatString); 25 | } 26 | return new Date(date).toLocaleTimeString(); 27 | } 28 | 29 | export function formatTimeWithoutSeconds (date: string | Date) { 30 | return format(new Date(date), "H:m"); 31 | } 32 | 33 | export function formatDateWithoutYear (date: string | Date) { 34 | return new Date(date).toLocaleDateString([], { 35 | month: "numeric", 36 | day: "2-digit" 37 | }); 38 | } 39 | 40 | export function getDate (date?: string | number | Date) { 41 | return date ? new Date(date) : new Date(); 42 | } 43 | 44 | export function getDateUntilNow (date?: string | number | Date, nowLabel?: string, time?: string) { 45 | return date ? humanReadableDateTimeTillNow(date, nowLabel, time) : "-"; 46 | } 47 | 48 | export function smartFormatDateTime (date?: string | number | Date) { 49 | if (!date) { 50 | return "-"; 51 | } 52 | if (isToday(new Date(date))) { 53 | return new Date(date).toLocaleTimeString(); 54 | } 55 | if (isThisWeek(new Date(date))) { 56 | return `${formatRelative(new Date(date), new Date())}`; 57 | } 58 | return new Date(date).toLocaleString(); 59 | } 60 | 61 | export function smartFormatDate (date?: string | number | Date) { 62 | if (!date) { 63 | return "-"; 64 | } 65 | //TODO we could use formatRelative when they implement this: https://github.com/date-fns/date-fns/issues/1218 66 | if (isToday(new Date(date))) { 67 | return "Today"; 68 | } 69 | if (isYesterday(new Date(date))) { 70 | return "Yesterday"; 71 | } 72 | return new Date(date).toLocaleDateString(); 73 | } 74 | 75 | export function isDateToday (date: string | Date) { 76 | return isToday(new Date(date)); 77 | } 78 | 79 | export function isDateYesterday (date: string | Date) { 80 | return isYesterday(new Date(date)); 81 | } 82 | 83 | export function isDateTomorrow (date: string | Date) { 84 | return isTomorrow(new Date(date)); 85 | } 86 | 87 | /** 88 | * Formats a date into a human-readable elapsed time string. 89 | * Returns strings like "now", "12 seconds ago", "3 hours ago", "today", "yesterday", "3 weeks ago", etc. 90 | * 91 | * @param date The date to format 92 | * @param shortFormat When true, uses abbreviated time units (e.g. "s" instead of "seconds") 93 | * @returns A human-readable elapsed time string 94 | */ 95 | export function formatHumanElapsedTime(date: string | Date, shortFormat = false): string { 96 | const now = new Date(); 97 | const inputDate = new Date(date); 98 | 99 | // Calculate time difference in milliseconds 100 | const diffMs = now.getTime() - inputDate.getTime(); 101 | 102 | // Handle future dates 103 | if (diffMs < 0) { 104 | return formatDate(date); 105 | } 106 | 107 | // Convert to seconds, minutes, hours, days, weeks 108 | const diffSeconds = Math.floor(diffMs / 1000); 109 | const diffMinutes = Math.floor(diffSeconds / 60); 110 | const diffHours = Math.floor(diffMinutes / 60); 111 | const diffDays = Math.floor(diffHours / 24); 112 | const diffWeeks = Math.floor(diffDays / 7); 113 | const diffMonths = Math.floor(diffDays / 30); 114 | const diffYears = Math.floor(diffDays / 365); 115 | 116 | // Define unit formats based on shortFormat parameter 117 | const units = { 118 | second: { 119 | singular: shortFormat ? 's' : 'second', 120 | plural: shortFormat ? 's' : 'seconds' 121 | }, 122 | minute: { 123 | singular: shortFormat ? 'min' : 'minute', 124 | plural: shortFormat ? 'min' : 'minutes' 125 | }, 126 | hour: { 127 | singular: shortFormat ? 'hr' : 'hour', 128 | plural: shortFormat ? 'hrs' : 'hours' 129 | }, 130 | day: { 131 | singular: shortFormat ? 'd' : 'day', 132 | plural: shortFormat ? 'd' : 'days' 133 | }, 134 | week: { 135 | singular: shortFormat ? 'wk' : 'week', 136 | plural: shortFormat ? 'wks' : 'weeks' 137 | }, 138 | month: { 139 | singular: shortFormat ? 'mo' : 'month', 140 | plural: shortFormat ? 'mos' : 'months' 141 | }, 142 | year: { 143 | singular: shortFormat ? 'y' : 'year', 144 | plural: shortFormat ? 'yrs' : 'years' 145 | } 146 | }; 147 | 148 | // Just now (within 10 seconds) 149 | if (diffSeconds < 10) { 150 | return "now"; 151 | } 152 | 153 | // Seconds ago (up to 1 minute) 154 | if (diffSeconds < 60) { 155 | const unit = diffSeconds === 1 ? units.second.singular : units.second.plural; 156 | return `${diffSeconds} ${unit} ago`; 157 | } 158 | 159 | // Minutes ago (up to 1 hour) 160 | if (diffMinutes < 60) { 161 | const unit = diffMinutes === 1 ? units.minute.singular : units.minute.plural; 162 | return `${diffMinutes} ${unit} ago`; 163 | } 164 | 165 | // Hours ago (up to today) 166 | if (isToday(inputDate)) { 167 | const unit = diffHours === 1 ? units.hour.singular : units.hour.plural; 168 | return `${diffHours} ${unit} ago`; 169 | } 170 | 171 | // Yesterday 172 | if (isYesterday(inputDate)) { 173 | return "yesterday"; 174 | } 175 | 176 | // Days ago (up to 1 week) 177 | if (diffDays < 7) { 178 | const unit = diffDays === 1 ? units.day.singular : units.day.plural; 179 | return `${diffDays} ${unit} ago`; 180 | } 181 | 182 | // Weeks ago (up to 4 weeks / 1 month) 183 | if (diffWeeks < 4) { 184 | const unit = diffWeeks === 1 ? units.week.singular : units.week.plural; 185 | return `${diffWeeks} ${unit} ago`; 186 | } 187 | 188 | // Months ago (up to 12 months / 1 year) 189 | if (diffMonths < 12) { 190 | const unit = diffMonths === 1 ? units.month.singular : units.month.plural; 191 | return `${diffMonths} ${unit} ago`; 192 | } 193 | 194 | // Years ago 195 | const unit = diffYears === 1 ? units.year.singular : units.year.plural; 196 | return `${diffYears} ${unit} ago`; 197 | } 198 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/DataSource/DataSource.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key characteristics:** 4 | - **Conditional loading**: Use `when` property to prevent fetching until dependent data is ready 5 | - **Built-in caching**: Prevents unnecessary requests and provides instant data access 6 | - **Polling support**: Automatically refetch data at specified intervals 7 | - **Data transformation**: Process and filter responses before components use the data 8 | 9 | ## Preventing the `DataSource` from Executing 10 | 11 | Prevent the `DataSource` from executing until the specified condition in the `when` attribute is true. 12 | 13 | ```xmlui 14 | <DataSource 15 | id="userProfile" 16 | url="/api/users/{selectedUserId}/profile" 17 | when="{selectedUserId}" /> 18 | ``` 19 | 20 | 21 | ## Structural Sharing 22 | 23 | `DataSource` uses a technique called "structural sharing" to ensure that as many data references as possible will be kept intact and not cause extra UI refresh. If data is fetched from an API endpoint, you'll usually get a completely new reference by json parsing the response. However, `DataSource` will keep the original reference if *nothing* has changed in the data. If a subset has changed, `DataSource` will keep the unchanged parts and only replace the changed parts. 24 | 25 | When you initiate the refetching of data (e.g., with the `refetch` method or setting the `pollIntervalInSeconds` property) and you retrieve data structurally equal with the cached data instance, `DataSource` will not fire the `loaded` event. 26 | 27 | By default, structural sharing is turned on. If you do not need this behavior, set the `structuralSharing` property to `false`. 28 | 29 | %-DESC-END 30 | 31 | %-PROP-START completedNotificationMessage 32 | 33 | This property customizes the success message displayed in a toast after the finished API invocation. The `$result` context variable can refer to the response body. For example, you can use the following code snippet to display the first 100 characters in the completed operation's response body: 34 | 35 | ```xmlui copy 36 | <DataSource 37 | id="ds" 38 | url="/api/shopping-list" 39 | completedNotificationMessage="Result: {JSON.stringify($result).substring(0, 100)}" /> 40 | ``` 41 | 42 | %-PROP-END 43 | 44 | %-PROP-START errorNotificationMessage 45 | 46 | This property customizes the message displayed in a toast when the API invocation results in an error. The `$error.statusCode` context variable can refer to the response's status code, while `$error. details` to the response body. For example, you can use the following code snippet to display the status code and the details: 47 | 48 | ```xmlui copy 49 | <DataSource 50 | id="ds" 51 | method="post" 52 | url="/api/shopping-list" 53 | errorNotificationMessage="${error.statusCode}, {JSON.stringify($error.details)}" /> 54 | ``` 55 | 56 | %-PROP-END 57 | 58 | %-PROP-START resultSelector 59 | 60 | The selector can be a simple dot notation path (e.g., `value.results`) or a JavaScript expression that processes the data (e.g., `results.filter(item => item.type === 'active')`). The selector has access to standard JavaScript functions like `map` and `filter`, and operates on the full response body. 61 | 62 | Here is a sample response from the HubSpot API. 63 | 64 | ```json 65 | { 66 | "results": [ 67 | { 68 | "id": "88903258744", 69 | "properties": { 70 | "company": "HubSpot", 71 | "createdate": "2025-01-03T23:38:47.449Z", 72 | "custom_notes": "Nice guy!", 73 | "email": "[email protected]", 74 | "firstname": "Brian", 75 | "hs_object_id": "88903258744", 76 | "lastmodifieddate": "2025-02-18T23:13:34.759Z", 77 | "lastname": "Halligan (Sample Contact)" 78 | }, 79 | "createdAt": "2025-01-03T23:38:47.449Z", 80 | "updatedAt": "2025-02-18T23:13:34.759Z", 81 | "archived": false 82 | }, 83 | { 84 | "id": "88918034480", 85 | "properties": { 86 | "company": "HubSpot", 87 | "createdate": "2025-01-03T23:38:47.008Z", 88 | "custom_notes": null, 89 | "email": "[email protected]", 90 | "firstname": "Maria", 91 | "hs_object_id": "88918034480", 92 | "lastmodifieddate": "2025-01-03T23:38:59.001Z", 93 | "lastname": "Johnson (Sample Contact)" 94 | }, 95 | "createdAt": "2025-01-03T23:38:47.008Z", 96 | "updatedAt": "2025-01-03T23:38:59.001Z", 97 | "archived": false 98 | } 99 | ] 100 | } 101 | ``` 102 | 103 | This `resultSelector` builds an array of the `properties` objects. 104 | 105 | 106 | ```xmlui copy 107 | <DataSource 108 | id="contacts" 109 | url="http://{DOMAIN}/{CORS_PROXY}/api.hubapi.com/crm/v3/objects/contacts?properties=firstname,lastname,email,company,custom_notes" 110 | resultSelector="results.map(item => item.properties )" 111 | headers='{{"Authorization":"Bearer not-a-real-token"}}' 112 | ``` 113 | 114 | This `List` uses the array. 115 | 116 | ```xmlui copy 117 | <List data="{contacts}" title="Hubspot Contacts"> 118 | <Card gap="0" width="20em"> 119 | <Text fontWeight="bold"> 120 | {$item.firstname} {$item.lastname} 121 | </Text> 122 | <Text> 123 | {$item.company} 124 | </Text> 125 | <Text> 126 | {$item.email} 127 | </Text> 128 | <Text> 129 | {$item.custom_notes} 130 | </Text> 131 | </Card> 132 | </List> 133 | ``` 134 | 135 | This `resultSelector` filters the array of the `properties` objects to include only contacts with non-null `custom_notes`. 136 | 137 | ```xmlui copy 138 | <DataSource 139 | id="contacts" 140 | resultSelector="results.filter(contact => contact.properties.custom_notes !== null).map(contact => contact.properties)" 141 | url="http://{DOMAIN}/{CORS_PROXY}/api.hubapi.com/crm/v3/objects/contacts?properties=firstname,lastname,email,company,custom_notes" 142 | headers='{{"Authorization":"Bearer not-a-real-token"}}' 143 | /> 144 | ```` 145 | 146 | This `Table` uses the filtered array. 147 | 148 | ```xmlui copy 149 | <Table title="HubSpot contacts" data="{contacts}"> 150 | <Column bindTo="firstname" /> 151 | <Column bindTo="lastname" /> 152 | <Column bindTo="company" /> 153 | <Column bindTo="email" /> 154 | <Column bindTo="custom_notes" /> 155 | </Table> 156 | ``` 157 | 158 | 159 | %-PROP-END ```