This is page 25 of 182. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ ├── config.json │ ├── cool-queens-look.md │ ├── twelve-guests-care.md │ └── wise-towns-dance.md ├── .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 │ ├── 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 │ │ ├── netlify.toml │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── 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 │ │ │ ├── Debug.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ ├── Main.xmlui.xs │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.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 │ ├── containers.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 │ ├── state-management.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 │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.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 │ │ │ ├── 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/dev-docs/next/xmlui-wcag-accessibility-report.md: -------------------------------------------------------------------------------- ```markdown 1 | # XMLUI WCAG 2.1 AA Accessibility Audit Report 2 | 3 | **Audit Date:** July 26, 2025 4 | **Tool Used:** Pa11y (WCAG2AA standard) 5 | **URL Tested:** http://localhost:5173 (XMLUI Documentation Site) 6 | **Total Issues Found:** 24 Errors 7 | 8 | ## Executive Summary 9 | 10 | The accessibility audit of the XMLUI documentation site revealed several WCAG 2.1 AA compliance issues that need to be addressed. The primary concerns are: 11 | 12 | 1. **Duplicate ID Attributes** - Critical HTML validation issues 13 | 2. **Missing Button Labels** - Accessibility API naming problems 14 | 3. **Color Contrast Issues** - Multiple instances of insufficient contrast ratios 15 | 4. **Anchor Link Contrast** - Link visibility concerns 16 | 17 | ## Detailed Findings 18 | 19 | ### 1. Critical Issues (Must Fix) 20 | 21 | #### Duplicate ID Attributes (3 instances) 22 | **WCAG Guideline:** 4.1.1 - Parsing 23 | **Severity:** Critical 24 | 25 | - `id=":rbd:"` - Search input element 26 | - `id=":rp:"` - Checkbox/switch element 27 | - `id="introduction"` - Anchor span element 28 | 29 | **Impact:** These duplicate IDs break HTML validity and can cause assistive technology confusion. 30 | 31 | **Recommendation:** Ensure all ID attributes are unique across the page. 32 | 33 | #### Missing Button Names (2 instances) 34 | **WCAG Guideline:** 4.1.2 - Name, Role, Value 35 | **Severity:** High 36 | 37 | Two button elements lack accessible names for screen readers: 38 | - Header button elements with only SVG content 39 | - No aria-label, title, or text content provided 40 | 41 | **Recommendation:** Add `aria-label` attributes to describe button functionality. 42 | 43 | ### 2. Color Contrast Issues (18 instances) 44 | 45 | #### Code Syntax Highlighting Contrast Problems 46 | **WCAG Guideline:** 1.4.3 - Contrast (Minimum) 47 | **Required Ratio:** 4.5:1 for normal text, 3:1 for large text 48 | 49 | Multiple code elements have insufficient contrast: 50 | 51 | - **Color #66748E:** 4.06:1 ratio (needs 4.5:1) 52 | - Recommendation: Change background to #f7faff 53 | - **Color #0074A9:** 4.44:1 ratio (needs 4.5:1) 54 | - Recommendation: Change background to #ebf0fb 55 | - **Color #F07100:** 2.56:1 ratio (severely insufficient) 56 | - Recommendation: Change text color to #070300 57 | - **Color #2F86D2:** 3.31:1 ratio (insufficient) 58 | - Recommendation: Change text color to #000306 59 | 60 | #### Anchor Link Contrast 61 | - **Anchor hash links:** 2.8:1 ratio (needs 3:1 minimum) 62 | - Recommendation: Change text color to #8196b4 63 | 64 | ## Priority Recommendations 65 | 66 | ### Immediate Actions (High Priority) 67 | 1. **Fix Duplicate IDs** - Generate unique IDs for all elements 68 | 2. **Add Button Labels** - Implement aria-label for all unlabeled buttons 69 | 3. **Fix Severe Contrast Issues** - Address colors with ratios below 3:1 70 | 71 | ### Short-term Actions (Medium Priority) 72 | 1. **Improve Code Syntax Colors** - Adjust syntax highlighting color scheme 73 | 2. **Review Theme Colors** - Audit all theme colors for WCAG compliance 74 | 3. **Test with Screen Readers** - Validate fixes with assistive technology 75 | 76 | ### Long-term Actions (Lower Priority) 77 | 1. **Implement Automated Testing** - Add accessibility testing to CI/CD pipeline 78 | 2. **Create Accessibility Guidelines** - Document color contrast requirements 79 | 3. **Regular Audits** - Schedule periodic accessibility reviews 80 | 81 | ## Technical Implementation Notes 82 | 83 | ### Code Syntax Highlighting Fix 84 | ```css 85 | /* Recommended color adjustments for syntax highlighting */ 86 | .syntax-punctuation { color: #66748E; background: #f7faff; } 87 | .syntax-string { color: #0074A9; background: #ebf0fb; } 88 | .syntax-keyword { color: #070300; } /* was #F07100 */ 89 | .syntax-property { color: #000306; } /* was #2F86D2 */ 90 | ``` 91 | 92 | ### Button Accessibility Fix 93 | ```html 94 | <!-- Before --> 95 | <button class="_headerButton_1b3x4_231" data-state="closed"> 96 | <svg>...</svg> 97 | </button> 98 | 99 | <!-- After --> 100 | <button class="_headerButton_1b3x4_231" data-state="closed" aria-label="Toggle menu"> 101 | <svg>...</svg> 102 | </button> 103 | ``` 104 | 105 | ## Compliance Status 106 | 107 | - **WCAG 2.1 A:** ❌ Non-compliant (duplicate IDs, missing labels) 108 | - **WCAG 2.1 AA:** ❌ Non-compliant (contrast issues) 109 | - **WCAG 2.1 AAA:** ❌ Non-compliant (multiple issues) 110 | 111 | ## Next Steps 112 | 113 | 1. Address critical duplicate ID issues immediately 114 | 2. Add proper button labeling for screen reader support 115 | 3. Implement color contrast fixes in the theme system 116 | 4. Test fixes with automated tools and manual testing 117 | 5. Document accessibility standards for the project 118 | 119 | ## Tools and Testing Environment 120 | 121 | - **Testing Tool:** Pa11y v6.x with WCAG2AA ruleset 122 | - **Browser:** Chrome (headless via Pa11y) 123 | - **Testing Date:** July 26, 2025 124 | - **Base URL:** http://localhost:5173 125 | 126 | --- 127 | 128 | *This report provides a baseline accessibility assessment. Regular testing and monitoring are recommended to maintain compliance as the application evolves.* ``` -------------------------------------------------------------------------------- /xmlui/src/components/Slot/Slot.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | ## Using Slot 4 | 5 | You can add `Slot` to a user-defined component as a placeholder. When you refer to the particular component in the markup, the children are transposed to the `Slot`. 6 | 7 | ```xmlui-pg name="Using Slot" 8 | ---app copy display {3-5} 9 | <App name="XMLUI Hello World"> 10 | <ActionBar> 11 | <Button label="Create" onClick="window.alert('Create clicked')" /> 12 | <Button label="Edit" onClick="window.alert('Edit clicked')" /> 13 | <Button label="Delete" onClick="window.alert('Delete clicked')" /> 14 | </ActionBar> 15 | </App> 16 | ---desc 17 | The app flows down three buttons to the `ActionBar` to render. 18 | ---comp copy display {5} 19 | <Component name="ActionBar"> 20 | <Card> 21 | <H3>Use these actions</H3> 22 | <HStack> 23 | <Slot /> 24 | </HStack> 25 | </Card> 26 | </Component> 27 | ---desc 28 | `ActionBar` renders the passed children by replacing `Slot` with them. 29 | ``` 30 | 31 | ## Default Slot content 32 | 33 | You can provide default content for the `Slot`. If the user-defined component does not have any children, XMLUI will render the default content. 34 | 35 | ```xmlui-pg 36 | ---app copy display name="Define default Slot content" 37 | <App> 38 | <ActionBar /> 39 | </App> 40 | ---comp copy display {6} 41 | <Component name="ActionBar"> 42 | <Card> 43 | <H3>Use these actions</H3> 44 | <HStack> 45 | <Slot> 46 | <Button label="Default" onClick="window.alert('Default clicked')" /> 47 | </Slot> 48 | </HStack> 49 | </Card> 50 | </Component> 51 | ``` 52 | 53 | ## Named Slots 54 | 55 | You can add multiple slots to a user-defined component; you can have a default slot and several *named* slots. Slot names should end with `template`, and you can use the `<property>` markup syntax to declare their values. 56 | 57 | ```xmlui-pg 58 | ---app copy display name="Using named Slots" {4, 7, 9-11} 59 | <App> 60 | <ActionBar> 61 | <property name="headerTemplate"> 62 | <H2>Click one of these actions</H2> 63 | </property> 64 | <property name="footerTemplate"> 65 | <Text>Footer content goes here</Text> 66 | </property> 67 | <Button label="Create" onClick="window.alert('Create clicked')" /> 68 | <Button label="Edit" onClick="window.alert('Edit clicked')" /> 69 | <Button label="Delete" onClick="window.alert('Delete clicked')" /> 70 | </ActionBar> 71 | </App> 72 | ---desc 73 | This app passes a header template and a footer template slot to the `ActionBar` component and also declares buttons to render. 74 | ---comp copy display {3-5, 7-9, 11} 75 | <Component name="ActionBar"> 76 | <Card> 77 | <Slot name="headerTemplate"> 78 | <H3>Use these actions</H3> 79 | </Slot> 80 | <HStack> 81 | <Slot> 82 | <Button label="Default" onClick="window.alert('Default clicked')" /> 83 | </Slot> 84 | </HStack> 85 | <Slot name="footerTemplate" /> 86 | </Card> 87 | </Component> 88 | ---desc 89 | XMLUI finds the appropriate slots by their name and transposes their content received from the app. Just like the default slot, named slots can have default content. 90 | ``` 91 | 92 | > [!WARN] XMLUI will display an error message when the `Slot` name does not end with "Template". 93 | 94 | 95 | ## Template properties 96 | 97 | The user-defined component can provide properties for the actual template. 98 | 99 | ```xmlui-pg 100 | ---app copy display name="Using template properties" /header/ /name="headerTemplate"/ /$processedHeader/ 101 | <App> 102 | <ActionBar header="Action Bar Example"> 103 | <property name="headerTemplate"> 104 | <Text variant="title">{$processedHeader}</Text> 105 | </property> 106 | <Button label="Create" onClick="window.alert('Create clicked')" /> 107 | <Button label="Edit" onClick="window.alert('Edit clicked')" /> 108 | <Button label="Delete" onClick="window.alert('Delete clicked')" /> 109 | </ActionBar> 110 | </App> 111 | ---desc 112 | The app passes a `header` property value to the `ActionBar` component. `Actionbar` utilizes this property, transforms it, and passes it back to the template in the `$processedHeader` context variable so that the app can use it. `$processHeader` is available only within the `headerTemplate` slot. 113 | ---comp copy display /transformedHeader/ /processedHeader="{transformedHeader}"/ 114 | <Component name="ActionBar"> 115 | <Card var.transformedHeader="*** {$props.header.toUpperCase()} ***"> 116 | <Slot name="headerTemplate" processedHeader="{transformedHeader}" > 117 | <H3>{transformedHeader}</H3> 118 | </Slot> 119 | <HStack> 120 | <Slot> 121 | <Button label="Default" onClick="window.alert('Default clicked')" /> 122 | </Slot> 123 | </HStack> 124 | </Card> 125 | </Component> 126 | ---desc 127 | `Actionbar` transforms the `header` property and stores it internally in the `transformedHeader` variable. It utilizes the value in the default header definition and also passes it back to the actual template definition with the `processedHeader` name. XMLUI creates the `$processedHeader` context variable from this name. 128 | ``` 129 | 130 | %-DESC-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/FlowLayout/FlowLayout.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | For details on how to work with \`FlowLayout\` (like sizing children), see [this guide](/layout#flowlayout). 4 | 5 | ## Using `SpaceFiller` with `FlowLayout` 6 | 7 | The `SpaceFiller` component can be used as a line break. 8 | See the [reference docs](/components/SpaceFiller) for details. 9 | 10 | %-DESC-END 11 | 12 | %-PROP-START gap 13 | 14 | The `gap` property defines the gap between items in the same row and between rows. The `FlowLayout` component creates a new row when an item is about to overflow the current row. 15 | 16 | ```xmlui-pg copy display name="Example: gap" 17 | ---app copy display 18 | <App> 19 | <FlowLayout gap="$space-12"> 20 | <Stack width="25%" height="32px" backgroundColor="red" /> 21 | <Stack width="25%" height="32px" backgroundColor="blue" /> 22 | <Stack width="25%" height="32px" backgroundColor="green" /> 23 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 24 | <Stack width="25%" height="32px" backgroundColor="maroon" /> 25 | <Stack width="25%" height="32px" backgroundColor="teal" /> 26 | <Stack width="25%" height="32px" backgroundColor="seagreen" /> 27 | <Stack width="25%" height="32px" backgroundColor="olive" /> 28 | </FlowLayout> 29 | </App> 30 | ---desc 31 | In this markup, only four items fit in a single row. 32 | The `gap` property sets the same gaps within and between rows. 33 | ``` 34 | 35 | This markup demonstrates different `gap` values: 36 | 37 | ```xmlui-pg copy display name="Example: different size units" 38 | ---app copy display 39 | <App> 40 | <FlowLayout> 41 | <Stack width="25%" height="32px" backgroundColor="red" /> 42 | <Stack width="25%" height="32px" backgroundColor="blue" /> 43 | <Stack width="25%" height="32px" backgroundColor="green" /> 44 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 45 | </FlowLayout> 46 | <FlowLayout gap="10px"> 47 | <Stack width="25%" height="32px" backgroundColor="red" /> 48 | <Stack width="25%" height="32px" backgroundColor="blue" /> 49 | <Stack width="25%" height="32px" backgroundColor="green" /> 50 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 51 | </FlowLayout> 52 | <FlowLayout gap="1rem"> 53 | <Stack width="25%" height="32px" backgroundColor="red" /> 54 | <Stack width="25%" height="32px" backgroundColor="blue" /> 55 | <Stack width="25%" height="32px" backgroundColor="green" /> 56 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 57 | </FlowLayout> 58 | <FlowLayout gap="4ch"> 59 | <Stack width="25%" height="32px" backgroundColor="red" /> 60 | <Stack width="25%" height="32px" backgroundColor="blue" /> 61 | <Stack width="25%" height="32px" backgroundColor="green" /> 62 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 63 | </FlowLayout> 64 | </App> 65 | ---desc 66 | All items within a `FlowLayout` instance fit in a single row, so `gap` affects only the space between items. The space between rows comes from the outermost `Stack`. 67 | ``` 68 | 69 | %-PROP-END 70 | 71 | %-PROP-START columnGap 72 | 73 | The `columnGap` property specifies the space between items in a single row; it overrides the `gap` value. 74 | 75 | ```xmlui-pg copy display name="Example: columnGap" 76 | ---app copy display 77 | <App> 78 | <FlowLayout columnGap="$space-8"> 79 | <Stack width="25%" height="32px" backgroundColor="red" /> 80 | <Stack width="25%" height="32px" backgroundColor="blue" /> 81 | <Stack width="25%" height="32px" backgroundColor="green" /> 82 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 83 | <Stack width="25%" height="32px" backgroundColor="maroon" /> 84 | <Stack width="25%" height="32px" backgroundColor="teal" /> 85 | <Stack width="25%" height="32px" backgroundColor="seagreen" /> 86 | <Stack width="25%" height="32px" backgroundColor="olive" /> 87 | </FlowLayout> 88 | </App> 89 | ---desc 90 | You can observe no gap between the rows of the `FlowLayout`, as `columnGap` keeps the space between rows intact: 91 | ``` 92 | 93 | %-PROP-END 94 | 95 | %-PROP-START rowGap 96 | 97 | The `rowGap` property specifies the space between the `FlowLayout` rows; it overrides the `gap` value. 98 | 99 | ```xmlui-pg copy display name="Example: rowGap" 100 | ---app copy display 101 | <App> 102 | <FlowLayout rowGap="2px"> 103 | <Stack width="25%" height="32px" backgroundColor="red" /> 104 | <Stack width="25%" height="32px" backgroundColor="blue" /> 105 | <Stack width="25%" height="32px" backgroundColor="green" /> 106 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 107 | <Stack width="25%" height="32px" backgroundColor="maroon" /> 108 | <Stack width="25%" height="32px" backgroundColor="teal" /> 109 | <Stack width="25%" height="32px" backgroundColor="seagreen" /> 110 | <Stack width="25%" height="32px" backgroundColor="olive" /> 111 | </FlowLayout> 112 | </App> 113 | ---desc 114 | You can observe no gap between the items in a single row of the `FlowLayout`, as `rowGap` keeps the gap within a row intact: 115 | ``` 116 | 117 | %-PROP-END 118 | ``` -------------------------------------------------------------------------------- /xmlui/tests-e2e/context-vars-regression.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { expect, test } from "../src/testing/fixtures"; 2 | 3 | test("context vars dont get resolved multiple times", async ({ page, initTestBed }) => { 4 | await initTestBed( 5 | ` 6 | <Fragment> 7 | <DataSource url="/data1" id="stringsLoader"/> 8 | <Items items="{stringsLoader.value}"> 9 | <Text testId="text-{$itemIndex}">{$item}</Text> 10 | </Items> 11 | </Fragment> 12 | `, 13 | { 14 | apiInterceptor: { 15 | operations: { 16 | "load-api-data1": { 17 | url: "/data1", 18 | method: "get", 19 | handler: `()=>{ 20 | return ['text0', 'text1', 'text{ 1 + 2 }']; 21 | }`, 22 | }, 23 | }, 24 | }, 25 | }, 26 | ); 27 | await expect(page.getByTestId("text-0")).toHaveText("text0"); 28 | await expect(page.getByTestId("text-1")).toHaveText("text1"); 29 | await expect(page.getByTestId("text-2")).toHaveText("text{ 1 + 2 }"); 30 | }); 31 | 32 | test("can use context vars in simple var declarations", async ({ page, initTestBed }) => { 33 | await initTestBed( 34 | ` 35 | <Fragment> 36 | <DataSource url="/data1" id="stringsLoader"/> 37 | <Items items="{stringsLoader.value}"> 38 | <script> 39 | var something = $item + '_modified'; 40 | </script> 41 | <Text testId="text-{$itemIndex}">{something}</Text> 42 | </Items> 43 | </Fragment> 44 | `, 45 | { 46 | apiInterceptor: { 47 | operations: { 48 | "load-api-data1": { 49 | url: "/data1", 50 | method: "get", 51 | handler: `()=>{ 52 | return ['text0', 'text1', 'text{ 1 + 2 }']; 53 | }`, 54 | }, 55 | }, 56 | }, 57 | }, 58 | ); 59 | 60 | await expect(page.getByTestId("text-0")).toHaveText("text0_modified"); 61 | await expect(page.getByTestId("text-1")).toHaveText("text1_modified"); 62 | await expect(page.getByTestId("text-2")).toHaveText("text{ 1 + 2 }_modified"); 63 | }); 64 | 65 | test("can use context vars in combination with vars and calculated props", async ({ 66 | page, 67 | initTestBed, 68 | }) => { 69 | await initTestBed( 70 | ` 71 | <Fragment> 72 | <script> 73 | var something = 'modified'; 74 | </script> 75 | <DataSource url="/data1" id="stringsLoader"/> 76 | <Items items="{stringsLoader.value}"> 77 | <Text testId="text-{$itemIndex}">{$item}_{something}</Text> 78 | </Items> 79 | </Fragment> 80 | `, 81 | { 82 | apiInterceptor: { 83 | operations: { 84 | "load-api-data1": { 85 | url: "/data1", 86 | method: "get", 87 | handler: `()=>{ 88 | return ['text0', 'text1', 'text{ 1 + 2 }']; 89 | }`, 90 | }, 91 | }, 92 | }, 93 | }, 94 | ); 95 | 96 | await expect(page.getByTestId("text-0")).toHaveText("text0_modified"); 97 | await expect(page.getByTestId("text-1")).toHaveText("text1_modified"); 98 | await expect(page.getByTestId("text-2")).toHaveText("text{ 1 + 2 }_modified"); 99 | }); 100 | 101 | test("context vars in formItems", async ({ page, initTestBed }) => { 102 | await initTestBed( 103 | ` 104 | <Fragment> 105 | <Form data="{{ customText: 'hello {1 + 2}' }}"> 106 | <FormItem bindTo="customText"> 107 | <TextBox initialValue="{$value}" testId="textBox"/> 108 | </FormItem> 109 | </Form> 110 | </Fragment> 111 | `, 112 | { 113 | apiInterceptor: { 114 | operations: { 115 | "load-api-data1": { 116 | url: "/data1", 117 | method: "get", 118 | handler: `()=>{ 119 | return ['text0', 'text1', 'text{ 1 + 2 }']; 120 | }`, 121 | }, 122 | }, 123 | }, 124 | }, 125 | ); 126 | await expect(page.getByTestId("textBox").getByRole("textbox")).toHaveValue("hello {1 + 2}"); 127 | }); 128 | 129 | test("$data context var in form event handlers", async ({ page, initTestBed }) => { 130 | await initTestBed(` 131 | <Fragment var.dataFromSubmit="" var.dataFromCancel="" var.dataFromReset=""> 132 | <Form testId="form"> 133 | <FormItem bindTo="customText" testId="textBox"/> 134 | <event name="submit" value="dataFromSubmit = $data.customText"/> 135 | <event name="cancel" value="dataFromCancel = $data.customText"/> 136 | <event name="reset" value="dataFromReset = $data.customText"/> 137 | </Form> 138 | <Text testId="dataFromSubmit">{dataFromSubmit}</Text> 139 | <Text testId="dataFromCancel">{dataFromCancel}</Text> 140 | <Text testId="dataFromReset">{dataFromReset}</Text> 141 | </Fragment> 142 | `); 143 | await page.getByTestId("textBox").getByRole("textbox").fill("hello {1 + 2}"); 144 | await page.getByTestId("form").locator("button[type=button]").click(); 145 | await expect(page.getByTestId("dataFromCancel")).toHaveText("hello {1 + 2}"); 146 | await page.getByTestId("form").locator("button[type=submit]").click(); 147 | await expect(page.getByTestId("dataFromSubmit")).toHaveText("hello {1 + 2}"); 148 | //reset is called after submit 149 | await expect(page.getByTestId("dataFromReset")).toHaveText("hello {1 + 2}"); 150 | }); 151 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Theme/Theme.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **No CSS required**: Change component appearance using theme variables instead of custom stylesheets 5 | - **Brand consistency**: Maintain design system compliance while allowing contextual variations 6 | - **Scoped styling**: Apply theme changes only to nested components without affecting the global design 7 | - **Variable overrides**: Modify colors, spacing, typography, and other design variables declaratively 8 | - **Nested contexts**: Stack multiple `Theme` components for granular control with automatic specificity rules 9 | 10 | See [this guide](/themes-intro) and [these references](/styles-and-themes/layout-props) for details. 11 | 12 | ## Using `Theme` 13 | 14 | In contrast to other components, `Theme` accepts theme variables as properties. 15 | You can define specific styles for components nested in `Theme` using these theme variables. 16 | 17 | The following example specifies a dark tone for the current theme 18 | and sets several theme variables to style the `ProgressBar` component: 19 | 20 | ```xmlui-pg copy {3-8} display name="Example: using Theme" 21 | <App> 22 | <Theme 23 | tone="dark" 24 | backgroundColor-ProgressBar="cyan" 25 | color-indicator-ProgressBar="purple" 26 | thickness-ProgressBar="12px" 27 | borderRadius-indicator-ProgressBar="12px" 28 | borderRadius-Progressbar="4px" 29 | > 30 | <VStack backgroundColor="$backgroundColor-primary"> 31 | <ProgressBar value="0"/> 32 | <ProgressBar value="0.2"/> 33 | <ProgressBar value="0.6"/> 34 | <ProgressBar value="1.0"/> 35 | </VStack> 36 | </Theme> 37 | </App> 38 | ``` 39 | 40 | %-DESC-END 41 | 42 | %-PROP-START themeId 43 | 44 | ```xmlui-pg copy {2, 9, 16} display name="Example: themeId" 45 | <App> 46 | <Theme themeId="xmlui"> 47 | <VStack backgroundColor="$backgroundColor-primary"> 48 | <H3>Use 'xmlui' theme:</H3> 49 | <ProgressBar value="0"/> 50 | <ProgressBar value="0.6"/> 51 | </VStack> 52 | </Theme> 53 | <Theme themeId="xmlui-green"> 54 | <VStack backgroundColor="$backgroundColor-primary"> 55 | <H3>Use 'xmlui-green' theme:</H3> 56 | <ProgressBar value="0"/> 57 | <ProgressBar value="0.6"/> 58 | </VStack> 59 | </Theme> 60 | <Theme themeId="xmlui-red"> 61 | <VStack backgroundColor="$backgroundColor-primary"> 62 | <H3>Use the 'xmlui-red' theme:</H3> 63 | <ProgressBar value="0"/> 64 | <ProgressBar value="0.6"/> 65 | </VStack> 66 | </Theme> 67 | </App> 68 | ``` 69 | 70 | %-PROP-END 71 | 72 | %-PROP-START tone 73 | 74 | ```xmlui-pg copy {2,9} display name="Example: tone" 75 | <App> 76 | <Theme tone="light"> 77 | <VStack backgroundColor="$backgroundColor-primary" > 78 | <H3>Use the light tone of the base theme:</H3> 79 | <ProgressBar value="0"/> 80 | <ProgressBar value="0.6"/> 81 | </VStack> 82 | </Theme> 83 | <Theme tone="dark"> 84 | <VStack backgroundColor="$backgroundColor-primary"> 85 | <H3>Use the dark tone of the base theme:</H3> 86 | <ProgressBar value="0"/> 87 | <ProgressBar value="0.6"/> 88 | </VStack> 89 | </Theme> 90 | </App> 91 | ``` 92 | 93 | %-PROP-END 94 | 95 | %-PROP-START root 96 | 97 | If so, it will set a number of important settings for the app: 98 | - what favicon to use 99 | - sets up font links 100 | - specifies the base css 101 | - sets up the root for the toast notification system 102 | 103 | Otherwise, the `Theme` component will just provide the theme context to its children. 104 | 105 | %-PROP-END 106 | 107 | %-PROP-START applyIf 108 | 109 | The `applyIf` property controls whether the theme is conditionally applied to its children. When set to `false`, the children are rendered without the theme wrapper, effectively bypassing the theme styling. 110 | 111 | ```xmlui-pg copy {2,9,16} display name="Example: applyIf" 112 | <App var.apply="{false}"> 113 | <Theme backgroundColor-Button="rgb(255, 100, 100)" applyIf="true"> 114 | <VStack> 115 | <H3>Theme Applied (applyIf="true"):</H3> 116 | <Button>Themed Button</Button> 117 | </VStack> 118 | </Theme> 119 | <Theme backgroundColor-Button="rgb(255, 100, 100)" applyIf="false"> 120 | <VStack> 121 | <H3>Theme Not Applied (applyIf="false"):</H3> 122 | <Button>Default Button</Button> 123 | </VStack> 124 | </Theme> 125 | <Theme backgroundColor-Button="rgb(100, 192, 100)" applyIf="{apply}"> 126 | <VStack> 127 | <H3>Conditional Theme (dynamic):</H3> 128 | <Button onClick="apply = !apply"> 129 | {apply ? 'Themed' : 'Default'} - Click to Toggle 130 | </Button> 131 | </VStack> 132 | </Theme> 133 | </App> 134 | ``` 135 | 136 | This property is particularly useful for: 137 | - **Conditional styling**: Apply themes based on user preferences, feature flags, or application state 138 | - **Theme debugging**: Temporarily disable themes during development 139 | - **Progressive enhancement**: Provide fallback styling when themes fail to load 140 | - **Dynamic theming**: Switch themes on and off based on user interactions or data conditions 141 | 142 | %-PROP-END 143 | 144 | %-STYLE-START 145 | 146 | The `Theme` component is a styling wrapper that influences the nested components' visual appearance. It cannot be styled. 147 | 148 | %-STYLE-END 149 | ``` -------------------------------------------------------------------------------- /packages/xmlui-os-frames/src/IPhoneFrame.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" 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 | 11 | .device { 12 | position: relative; 13 | transform: scale(1); 14 | z-index: 1 15 | } 16 | 17 | .device .deviceFrame { 18 | z-index: 1 19 | } 20 | 21 | .device .deviceScreen { 22 | background-color: t.$backgroundColor; 23 | background-position: center center; 24 | background-size: cover; 25 | object-fit: cover; 26 | position: relative; 27 | padding: t.$space-4; 28 | gap: t.$space-4; 29 | display: flex; 30 | flex-direction: column; 31 | } 32 | 33 | .deviceIphone14Pro { 34 | height: 868px; 35 | width: 428px; 36 | flex-shrink: 0; 37 | } 38 | 39 | .deviceIphone14Pro .deviceFrame { 40 | background: #010101; 41 | border: 1px solid #1b1721; 42 | border-radius: 68px; 43 | box-shadow: inset 0 0 4px 2px #c0b7cd, inset 0 0 0 6px #342c3f; 44 | height: 100%; 45 | padding: 19px; 46 | width: 100%; 47 | } 48 | 49 | .deviceIphone14Pro .deviceScreen { 50 | border-radius: 49px; 51 | height: 100%; 52 | width: 100%; 53 | padding-top: calc(44px + #{t.$space-4}); 54 | } 55 | 56 | .deviceIphone14Pro .deviceStripe::after, .deviceIphone14Pro .deviceStripe::before { 57 | border: solid rgba(1, 1, 1, .25); 58 | border-width: 0 7px; 59 | content: ""; 60 | height: 7px; 61 | left: 0; 62 | position: absolute; 63 | width: 100%; 64 | z-index: 9; 65 | } 66 | 67 | .deviceIphone14Pro .deviceStripe::after { 68 | top: 85px; 69 | } 70 | 71 | .deviceIphone14Pro .deviceStripe::before { 72 | bottom: 85px; 73 | } 74 | 75 | .deviceIphone14Pro .deviceHeader { 76 | background: #010101; 77 | border-radius: 20px; 78 | height: 35px; 79 | left: 50%; 80 | margin-left: -60px; 81 | position: absolute; 82 | top: 29px; 83 | width: 120px; 84 | } 85 | 86 | .deviceIphone14Pro .deviceSensors::after, .deviceIphone14Pro .deviceSensors::before { 87 | content: ""; 88 | position: absolute; 89 | } 90 | 91 | .deviceIphone14Pro .deviceSensors::after { 92 | background: #010101; 93 | border-radius: 17px; 94 | height: 33px; 95 | left: 50%; 96 | margin-left: -60px; 97 | top: 30px; 98 | width: 74px; 99 | } 100 | 101 | .deviceIphone14Pro .deviceSensors::before { 102 | background: radial-gradient(farthest-corner at 20% 20%, #6074bf 0, transparent 40%), radial-gradient(farthest-corner at 80% 80%, #513785 0, #24555e 20%, transparent 50%); 103 | border-radius: 50%; 104 | box-shadow: 0 0 1px 1px rgba(255, 255, 255, .05); 105 | height: 9px; 106 | left: 50%; 107 | margin-left: 36px; 108 | top: 42px; 109 | width: 9px; 110 | } 111 | 112 | .deviceIphone14Pro .deviceBtns { 113 | background: #1b1721; 114 | border-radius: 2px; 115 | height: 32px; 116 | left: -2px; 117 | position: absolute; 118 | top: 115px; 119 | width: 3px; 120 | } 121 | 122 | .deviceIphone14Pro .deviceBtns::after, .deviceIphone14Pro .deviceBtns::before { 123 | background: #1b1721; 124 | border-radius: 2px; 125 | content: ""; 126 | height: 62px; 127 | left: 0; 128 | position: absolute; 129 | width: 3px; 130 | } 131 | 132 | .deviceIphone14Pro .deviceBtns::after { 133 | top: 60px; 134 | } 135 | 136 | .deviceIphone14Pro .deviceBtns::before { 137 | top: 140px; 138 | } 139 | 140 | .deviceIphone14Pro .devicePower { 141 | background: #1b1721; 142 | border-radius: 2px; 143 | height: 100px; 144 | position: absolute; 145 | right: -2px; 146 | top: 200px; 147 | width: 3px; 148 | } 149 | 150 | .deviceIphone14Pro .deviceHome::after, .deviceIphone14Pro .deviceHome::before { 151 | border: solid rgba(1, 1, 1, .25); 152 | border-width: 6px 0; 153 | content: ""; 154 | height: 6px; 155 | position: absolute; 156 | width: 6px; 157 | z-index: 9; 158 | } 159 | 160 | .deviceIphone14Pro .deviceHome::after { 161 | right: 86px; 162 | top: 0; 163 | } 164 | 165 | .deviceIphone14Pro .deviceHome::before { 166 | bottom: 0; 167 | left: 86px; 168 | } 169 | 170 | .deviceIphone14Pro.deviceSilver .deviceFrame { 171 | border-color: #c8cacb; 172 | box-shadow: inset 0 0 4px 2px #fff, inset 0 0 0 6px #e2e3e4; 173 | } 174 | 175 | .deviceIphone14Pro.deviceSilver .deviceBtns { 176 | background: #c8cacb; 177 | } 178 | 179 | .deviceIphone14Pro.deviceSilver .deviceBtns::after, .deviceIphone14Pro.deviceSilver .deviceBtns::before { 180 | background: #c8cacb; 181 | } 182 | 183 | .deviceIphone14Pro.deviceSilver .devicePower { 184 | background: #c8cacb; 185 | } 186 | 187 | .deviceIphone14Pro.deviceBlack .deviceFrame { 188 | border-color: #5c5956; 189 | box-shadow: inset 0 0 4px 2px #fff, inset 0 0 0 6px #76726f; 190 | } 191 | 192 | .deviceIphone14Pro.deviceBlack .deviceBtns { 193 | background: #5c5956; 194 | } 195 | 196 | .deviceIphone14Pro.deviceBlack .deviceBtns::after, .deviceIphone14Pro.deviceBlack .deviceBtns::before { 197 | background: #5c5956; 198 | } 199 | 200 | .deviceIphone14Pro.deviceBlack .devicePower { 201 | background: #5c5956; 202 | } 203 | 204 | .deviceIphone14Pro.deviceGold .deviceFrame { 205 | border-color: #e7d19e; 206 | box-shadow: inset 0 0 4px 2px #fff, inset 0 0 0 6px #d2ab4c; 207 | } 208 | 209 | .deviceIphone14Pro.deviceGold .deviceBtns { 210 | background: #e7d19e; 211 | } 212 | 213 | .deviceIphone14Pro.deviceGold .deviceBtns::after, .deviceIphone14Pro.deviceGold .deviceBtns::before { 214 | background: #e7d19e; 215 | } 216 | 217 | .deviceIphone14Pro.deviceGold .devicePower { 218 | background: #e7d19e; 219 | } 220 | 221 | 222 | // --- We export the theme variables to add them to the component renderer 223 | :export{ 224 | themeVars: t.json-stringify($themeVars) 225 | } ``` -------------------------------------------------------------------------------- /xmlui/src/components/ValidationSummary/ValidationSummary.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { useMemo } from "react"; 2 | import { useAutoAnimate } from "@formkit/auto-animate/react"; 3 | import classnames from "classnames"; 4 | 5 | import styles from "./ValidationSummary.module.scss"; 6 | 7 | import { EMPTY_ARRAY, EMPTY_OBJECT } from "../../components-core/constants"; 8 | import type { 9 | SingleValidationResult, 10 | ValidationResult, 11 | ValidationSeverity, 12 | } from "../Form/FormContext"; 13 | import { Stack } from "../Stack/StackNative"; 14 | import { Icon } from "../Icon/IconNative"; 15 | import { Text } from "../Text/TextNative"; 16 | import { SpaceFiller } from "../SpaceFiller/SpaceFillerNative"; 17 | import { Button } from "../Button/ButtonNative"; 18 | 19 | export type ValidationSummaryProps = { 20 | fieldValidationResults?: Record<string, ValidationResult>; 21 | generalValidationResults: Array<SingleValidationResult>; 22 | }; 23 | 24 | export const defaultProps: Pick<ValidationSummaryProps, "fieldValidationResults" | "generalValidationResults"> = { 25 | fieldValidationResults: EMPTY_OBJECT, 26 | generalValidationResults: EMPTY_ARRAY, 27 | }; 28 | 29 | type ValidationDisplayProps = { 30 | heading?: string; 31 | issues: Array<ValidationIssue>; 32 | severity?: ValidationSeverity | "info"; 33 | onClose?: (...args: any[]) => any; 34 | }; 35 | 36 | type ValidationIssue = { field?: string; message: string }; 37 | 38 | export function ValidationSummary({ 39 | fieldValidationResults = defaultProps.fieldValidationResults, 40 | generalValidationResults = defaultProps.generalValidationResults, 41 | }: ValidationSummaryProps) { 42 | const [animateContainerRef] = useAutoAnimate({ duration: 100 }); 43 | const groupedInvalidResults = useMemo(() => { 44 | const ret: Record<ValidationSeverity | string, Array<ValidationIssue>> = {}; 45 | Object.entries(fieldValidationResults).forEach(([field, validationResult]) => { 46 | validationResult.validations.forEach((singleValidationResult) => { 47 | if (!singleValidationResult.isValid) { 48 | ret[singleValidationResult.severity] = ret[singleValidationResult.severity] || []; 49 | ret[singleValidationResult.severity].push({ 50 | field, 51 | message: singleValidationResult.invalidMessage || "", 52 | }); 53 | } 54 | }); 55 | }); 56 | generalValidationResults.forEach((singleValidationResult) => { 57 | ret[singleValidationResult.severity] = ret[singleValidationResult.severity] || []; 58 | ret[singleValidationResult.severity].push({ 59 | message: singleValidationResult.invalidMessage || "", 60 | }); 61 | }); 62 | return ret; 63 | }, [fieldValidationResults, generalValidationResults]); 64 | 65 | return ( 66 | <div 67 | ref={animateContainerRef} 68 | className={styles.summaryContainer} 69 | data-validation-summary 70 | > 71 | <ValidationDisplay 72 | issues={groupedInvalidResults.warning} 73 | severity={"warning"} 74 | heading={"Validation warnings"} 75 | /> 76 | <ValidationDisplay 77 | issues={groupedInvalidResults.error} 78 | severity={"error"} 79 | heading={"Validation errors"} 80 | /> 81 | </div> 82 | ); 83 | } 84 | 85 | const ValidationDisplay = ({ 86 | heading, 87 | issues = EMPTY_ARRAY, 88 | severity = "error", 89 | onClose, 90 | }: ValidationDisplayProps) => { 91 | const [animateContainerRef] = useAutoAnimate({ duration: 100 }); 92 | if (issues.length === 0) { 93 | return null; 94 | } 95 | return ( 96 | <div 97 | className={classnames(styles.validationContainer, { 98 | [styles.valid]: severity === "valid", 99 | [styles.info]: severity === "info", 100 | [styles.warning]: severity === "warning", 101 | [styles.error]: severity === "error", 102 | })} 103 | style={{ paddingTop: !onClose ? "0.5rem" : undefined }} 104 | data-validation-display-severity={severity} 105 | > 106 | <Stack orientation="horizontal" verticalAlignment="center" style={{ gap: "0.5rem" }}> 107 | <Icon className={styles.heading} name={severity} size="md" /> 108 | <div className={styles.heading}> 109 | <Text>{heading}</Text> 110 | </div> 111 | {!!onClose && ( 112 | <> 113 | <SpaceFiller /> 114 | <Button 115 | onClick={onClose} 116 | variant={"ghost"} 117 | themeColor={"secondary"} 118 | icon={<Icon name={"close"} size={"sm"} />} 119 | orientation={"vertical"} 120 | /> 121 | </> 122 | )} 123 | </Stack> 124 | <ul ref={animateContainerRef}> 125 | {issues.map((issue, i) => ( 126 | <ValidationEntry key={i} issue={issue} /> 127 | ))} 128 | </ul> 129 | </div> 130 | ); 131 | }; 132 | 133 | // --- ValidationEntry 134 | const ValidationEntry = ({ issue }: { issue: ValidationIssue }) => { 135 | const { field, message } = issue; 136 | return ( 137 | <li> 138 | <span style={{ display: "inline-flex", gap: field ? "0.25rem" : undefined }}> 139 | {field && <Text variant="small" fontWeight="bold">{`${field}:`}</Text>} 140 | <Text variant="small" preserveLinebreaks={true}> 141 | {message} 142 | </Text> 143 | </span> 144 | </li> 145 | ); 146 | }; 147 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Button/ButtonNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React, { type CSSProperties, useRef, useEffect } from "react"; 2 | import classnames from "classnames"; 3 | 4 | import styles from "./Button.module.scss"; 5 | 6 | import { 7 | isSizeType, 8 | type SizeType, 9 | type AlignmentOptions, 10 | type ButtonAria, 11 | type ButtonThemeColor, 12 | type ButtonType, 13 | type ButtonVariant, 14 | type IconPosition, 15 | type OrientationOptions, 16 | } from "../abstractions"; 17 | import { composeRefs } from "@radix-ui/react-compose-refs"; 18 | import { VisuallyHidden } from "../VisuallyHidden"; 19 | 20 | type Props = { 21 | id?: string; 22 | type?: ButtonType; 23 | variant?: ButtonVariant; 24 | themeColor?: ButtonThemeColor; 25 | size?: SizeType; 26 | disabled?: boolean; 27 | children?: React.ReactNode | React.ReactNode[]; 28 | icon?: React.ReactNode; 29 | iconPosition?: IconPosition; 30 | contentPosition?: AlignmentOptions; 31 | orientation?: OrientationOptions; 32 | formId?: string; 33 | style?: CSSProperties; 34 | gap?: string | number; 35 | autoFocus?: boolean; 36 | contextualLabel?: string; 37 | } & Pick< 38 | React.HTMLAttributes<HTMLButtonElement>, 39 | | "onClick" 40 | | "onFocus" 41 | | "onBlur" 42 | | "onMouseEnter" 43 | | "onMouseLeave" 44 | | ButtonAria 45 | | "tabIndex" 46 | | "className" 47 | | "role" 48 | >; 49 | 50 | export const defaultProps: Pick< 51 | Props, 52 | | "type" 53 | | "iconPosition" 54 | | "contentPosition" 55 | | "orientation" 56 | | "variant" 57 | | "themeColor" 58 | | "size" 59 | | "autoFocus" 60 | > = { 61 | type: "button", 62 | iconPosition: "start", 63 | contentPosition: "center", 64 | orientation: "horizontal", 65 | variant: "solid", 66 | themeColor: "primary", 67 | size: "sm", 68 | autoFocus: false, 69 | }; 70 | 71 | export const Button = React.forwardRef(function Button( 72 | { 73 | id, 74 | type = defaultProps.type, 75 | icon, 76 | iconPosition = defaultProps.iconPosition, 77 | contentPosition = defaultProps.contentPosition, 78 | orientation = defaultProps.orientation, 79 | variant = defaultProps.variant, 80 | themeColor = defaultProps.themeColor, 81 | size = defaultProps.size, 82 | disabled, 83 | children, 84 | formId, 85 | onClick, 86 | onFocus, 87 | onBlur, 88 | style, 89 | gap, 90 | className, 91 | autoFocus = defaultProps.autoFocus, 92 | contextualLabel, 93 | ...rest 94 | }: Props, 95 | ref: React.ForwardedRef<HTMLButtonElement>, 96 | ) { 97 | const innerRef = useRef<HTMLButtonElement>(null); 98 | const composedRef = ref ? composeRefs(ref, innerRef) : innerRef; 99 | useEffect(() => { 100 | if (autoFocus) { 101 | setTimeout(() => { 102 | innerRef.current?.focus(); 103 | }, 0); 104 | } 105 | }, [autoFocus]); 106 | 107 | const iconToLeft = iconPosition === "start"; 108 | 109 | if (!isSizeType(size)) { 110 | size = defaultProps.size; 111 | } 112 | return ( 113 | <button 114 | {...rest} 115 | id={id} 116 | type={type} 117 | ref={composedRef} 118 | className={classnames( 119 | styles.button, 120 | { 121 | [styles.buttonHorizontal]: orientation === "horizontal", 122 | [styles.buttonVertical]: orientation === "vertical", 123 | [styles.xs]: size === "xs", 124 | [styles.sm]: size === "sm", 125 | [styles.md]: size === "md", 126 | [styles.lg]: size === "lg", 127 | [styles.solidPrimary]: variant === "solid" && themeColor === "primary", 128 | [styles.solidSecondary]: variant === "solid" && themeColor === "secondary", 129 | [styles.solidAttention]: variant === "solid" && themeColor === "attention", 130 | [styles.outlinedPrimary]: variant === "outlined" && themeColor === "primary", 131 | [styles.outlinedSecondary]: variant === "outlined" && themeColor === "secondary", 132 | [styles.outlinedAttention]: variant === "outlined" && themeColor === "attention", 133 | [styles.ghostPrimary]: variant === "ghost" && themeColor === "primary", 134 | [styles.ghostSecondary]: variant === "ghost" && themeColor === "secondary", 135 | [styles.ghostAttention]: variant === "ghost" && themeColor === "attention", 136 | [styles.alignStart]: contentPosition === "start", 137 | [styles.alignEnd]: contentPosition === "end", 138 | }, 139 | className, 140 | )} 141 | autoFocus={autoFocus} 142 | disabled={disabled} 143 | form={formId} 144 | style={style} 145 | onClick={onClick} 146 | onFocus={onFocus} 147 | onBlur={onBlur} 148 | > 149 | {icon && iconToLeft && <>{icon}</>} 150 | {children} 151 | {icon && !children && <IconLabel icon={icon} accessibleName={contextualLabel} />} 152 | {icon && !iconToLeft && <>{icon}</>} 153 | </button> 154 | ); 155 | }); 156 | 157 | type IconLabelProps = { 158 | icon: React.ReactNode; 159 | accessibleName?: string; 160 | }; 161 | 162 | const IconLabel = ({ icon, accessibleName = "" }: IconLabelProps) => { 163 | // NOTE: the icon object provided is a React object with accessible props attribute. 164 | // Typing might be off, because TS thinks props is not accessible. 165 | const iconProps: Record<string, any> | undefined = (icon as any).props; 166 | return ( 167 | <VisuallyHidden> 168 | <span>{accessibleName || iconProps?.name || iconProps?.alt}</span> 169 | </VisuallyHidden> 170 | ); 171 | }; 172 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/NavLink/NavLink.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Custom actions**: Execute JavaScript code instead of navigation when using onClick handlers 5 | - **Visual customization**: Support for icons, labels, and completely custom nested content 6 | - **Accessibility support**: Proper focus management and keyboard navigation 7 | 8 | ## Using NavLink 9 | 10 | ### `NavLink` Appearance 11 | 12 | You can use the `label` and `icon` properties of a `NavLink` to set its text and icon to display. 13 | If you want a custom appearance, you can nest define custom visuals for the `NavLink` by nesting: 14 | 15 | ```xmlui-pg copy {6-14} display name="Example: NavLink appearance" height="250px" 16 | <App layout="horizontal"> 17 | <AppHeader> 18 | <H1>MyApp</H1> 19 | </AppHeader> 20 | <NavPanel> 21 | <NavLink to="/"> 22 | <Stack width="16px" height="16px" backgroundColor="purple" /> 23 | Home 24 | </NavLink> 25 | <NavLink to="/about"> 26 | <Stack width="16px" height="16px" backgroundColor="green" /> 27 | About 28 | </NavLink> 29 | </NavPanel> 30 | <Pages> 31 | <Page url="/">Home</Page> 32 | <Page url="/about">About</Page> 33 | </Pages> 34 | </App> 35 | ``` 36 | 37 | ### Actions 38 | 39 | By default, activating (clicking) a link navigates to the target URL. 40 | However, you can create a link that executes an explicit action responding to the `click` event instead of the default navigation: 41 | 42 | ```xmlui-pg copy {7} display name="Example: custom NavLink action" height="250px" 43 | <App layout="horizontal"> 44 | <AppHeader> 45 | <H1>MyApp</H1> 46 | </AppHeader> 47 | <NavPanel> 48 | <NavLink to="/" label="Home" /> 49 | <NavLink label="Danger!" onClick="toast('Be careful with this action!')" /> 50 | </NavPanel> 51 | <Pages> 52 | <Page url="/">Home</Page> 53 | </Pages> 54 | </App> 55 | ``` 56 | 57 | %-DESC-END 58 | 59 | %-PROP-START label 60 | 61 | ```xmlui-pg copy display name="Example: label" height="250px" 62 | <App layout="horizontal"> 63 | <NavPanel> 64 | <NavLink to="/" label="Home" /> 65 | </NavPanel> 66 | <Pages> 67 | <Page url="/">Home</Page> 68 | </Pages> 69 | </App> 70 | ``` 71 | 72 | %-PROP-END 73 | 74 | %-PROP-START vertical 75 | 76 | Usually, you do not need to use this property. 77 | However, if you create a custom navigation menu component that runs vertically, 78 | you need to manually set this property for the active state to be displayed properly. 79 | 80 | The default value for this property is `false`. 81 | 82 | ```xmlui-pg copy display name="Example: vertical" height="250px" 83 | <App layout="horizontal"> 84 | <NavPanel> 85 | <NavLink to="/" label="Home" vertical="true" /> 86 | </NavPanel> 87 | <Pages> 88 | <Page url="/">Home</Page> 89 | </Pages> 90 | </App> 91 | ``` 92 | 93 | %-PROP-END 94 | 95 | %-PROP-START icon 96 | 97 | ```xmlui-pg copy {6-7} display name="Example: icon" height="250px" 98 | <App layout="horizontal"> 99 | <AppHeader> 100 | <H1>MyApp</H1> 101 | </AppHeader> 102 | <NavPanel> 103 | <NavLink label="Home" to="/" icon="home" /> 104 | <NavLink label="Drives" to="/drives" icon="drive" /> 105 | </NavPanel> 106 | <Pages> 107 | <Page url="/">Home</Page> 108 | <Page url="/drives">Drives Page</Page> 109 | </Pages> 110 | </App> 111 | ``` 112 | 113 | %-PROP-END 114 | 115 | %-PROP-START displayActive 116 | 117 | ```xmlui-pg copy display name="Example: displayActive" height="250px" 118 | <App layout="horizontal"> 119 | <NavPanel> 120 | <NavLink to="/" label="Home" displayActive="false" /> 121 | </NavPanel> 122 | <Pages> 123 | <Page url="/">Home</Page> 124 | </Pages> 125 | </App> 126 | ``` 127 | 128 | %-PROP-END 129 | 130 | %-PROP-START enabled 131 | 132 | In the following app, the "Hotels" link is disabled: 133 | 134 | ```xmlui-pg copy {8} display name="Example: enabled" height="250px" 135 | <App layout="horizontal"> 136 | <AppHeader> 137 | <H1>MyTravel App</H1> 138 | </AppHeader> 139 | <NavPanel> 140 | <NavLink label="Home" to="/" /> 141 | <NavLink label="Flights" to="/flights" /> 142 | <NavLink label="Hotels" to="/hotels" enabled="false" /> 143 | </NavPanel> 144 | <Pages> 145 | <Page url="/">Home</Page> 146 | <Page url="/flights">Flights Page</Page> 147 | <Page url="/hotels">Hotels Page</Page> 148 | </Pages> 149 | </App> 150 | ``` 151 | 152 | %-PROP-END 153 | 154 | %-PROP-START target 155 | 156 | The following example opens the "About XMLUI" link in a new tab: 157 | 158 | ```xmlui-pg copy {7} display name="Example: target" height="250px" 159 | <App layout="horizontal"> 160 | <AppHeader> 161 | <H1>MyApp</H1> 162 | </AppHeader> 163 | <NavPanel> 164 | <NavLink label="Home" to="/" /> 165 | <NavLink label="About XMLUI" to="https://docs.xmlui.org/" target="_blank" /> 166 | </NavPanel> 167 | <Pages> 168 | <Page url="/">Home</Page> 169 | <Page url="/drives">Drives Page</Page> 170 | </Pages> 171 | </App> 172 | ``` 173 | 174 | %-PROP-END 175 | 176 | %-EVENT-START click 177 | 178 | The following example shows a message and navigates to the "/status" link after closing the message window: 179 | 180 | ```xmlui-pg copy {7} display name="Example: click" height="250px" 181 | <App layout="horizontal"> 182 | <AppHeader> 183 | <H1>MyApp</H1> 184 | </AppHeader> 185 | <NavPanel> 186 | <NavLink to="/" label="Home" /> 187 | <NavLink label="Check my status" onClick=" 188 | toast('You will be redirected'); 189 | Actions.navigate('/status'); 190 | " /> 191 | </NavPanel> 192 | <Pages> 193 | <Page url="/">Home</Page> 194 | <Page url="/status">My Status</Page> 195 | </Pages> 196 | </App> 197 | ``` 198 | 199 | %-EVENT-END 200 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/NumberBox/NumberBox.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 | ); 6 | 7 | @function createThemeVar($componentVariable) { 8 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 9 | @return t.getThemeVar($themeVars, $componentVariable); 10 | } 11 | 12 | $componentName: "NumberBox"; 13 | $themeVars: t.composePaddingVars($themeVars, $componentName); 14 | 15 | // --- CSS properties of a particular NumberBox variant 16 | @mixin variant($variantName) { 17 | border-radius: createThemeVar("Input:borderRadius-#{$componentName}-#{$variantName}"); 18 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}"); 19 | border-width: createThemeVar("Input:borderWidth-#{$componentName}-#{$variantName}"); 20 | border-style: createThemeVar("Input:borderStyle-#{$componentName}-#{$variantName}"); 21 | font-size: createThemeVar("Input:fontSize-#{$componentName}-#{$variantName}"); 22 | 23 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}"); 24 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}"); 25 | color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}"); 26 | 27 | &:hover { 28 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--hover"); 29 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}--hover" 30 | ); 31 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--hover"); 32 | color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}--hover"); 33 | } 34 | 35 | &:focus-within { 36 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--focus"); 37 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}--focus" 38 | ); 39 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--focus"); 40 | color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}--focus"); 41 | } 42 | 43 | &:has(.input:focus-visible) { 44 | outline-width: createThemeVar("Input:outlineWidth-#{$componentName}-#{$variantName}--focus"); 45 | outline-color: createThemeVar("Input:outlineColor-#{$componentName}-#{$variantName}--focus"); 46 | outline-style: createThemeVar("Input:outlineStyle-#{$componentName}-#{$variantName}--focus"); 47 | outline-offset: createThemeVar("Input:outlineOffset-#{$componentName}-#{$variantName}--focus"); 48 | } 49 | 50 | .input { 51 | &::placeholder { 52 | color: createThemeVar("Input:textColor-placeholder-#{$componentName}-#{$variantName}"); 53 | font-size: createThemeVar("Input:fontSize-placeholder-#{$componentName}-#{$variantName}"); 54 | } 55 | } 56 | 57 | .adornment { 58 | color: createThemeVar("Input:color-adornment-#{$componentName}-#{$variantName}"); 59 | 60 | * { 61 | color: inherit; 62 | } 63 | } 64 | } 65 | 66 | @layer components { 67 | .inputRoot { 68 | display: flex; 69 | align-items: center; 70 | gap: createThemeVar("Input:gap-adornment-#{$componentName}"); 71 | width: 100%; 72 | border-style: solid; 73 | border-width: 1px; 74 | transition: background-color ease-in 0.1s; 75 | overflow: hidden; 76 | @include t.paddingVars($themeVars, $componentName); 77 | 78 | @include variant("default"); 79 | 80 | &.error { 81 | @include variant("error"); 82 | } 83 | 84 | &.warning { 85 | @include variant("warning"); 86 | } 87 | 88 | &.valid { 89 | @include variant("success"); 90 | } 91 | 92 | &:has(.input:is(:disabled)) { 93 | cursor: not-allowed; 94 | background-color: createThemeVar("Input:backgroundColor-NumberBox--disabled"); 95 | color: createThemeVar("Input:textColor-NumberBox--disabled"); 96 | border-color: createThemeVar("Input:borderColor-NumberBox--disabled"); 97 | } 98 | 99 | &.rtl { 100 | flex-direction: row-reverse; 101 | } 102 | 103 | .input { 104 | font-size: inherit; 105 | color: inherit; 106 | border: 0; 107 | outline: none; 108 | background-color: transparent; 109 | padding: 0; 110 | width: 100%; 111 | cursor: inherit; 112 | 113 | /* Remove default spinners */ 114 | /* Chrome, Safari, Edge, Opera */ 115 | &::-webkit-outer-spin-button, 116 | &::-webkit-inner-spin-button { 117 | -webkit-appearance: none; 118 | margin: 0; 119 | } 120 | 121 | /* Firefox */ 122 | -webkit-appearance: textfield; 123 | -moz-appearance: textfield; 124 | appearance: textfield; 125 | } 126 | 127 | .spinnerBox { 128 | display: flex; 129 | flex-direction: column; 130 | height: 0; 131 | align-items: center; 132 | justify-content: center; 133 | } 134 | 135 | .spinnerButton { 136 | padding: 0 !important; 137 | width: t.$space-6; 138 | border: none; 139 | 140 | // &:hover { 141 | // background-color: transparent !important; 142 | // } 143 | } 144 | } 145 | 146 | .readOnly { 147 | cursor: text; 148 | } 149 | } 150 | 151 | // --- We export the theme variables to add them to the component renderer 152 | :export { 153 | themeVars: t.json-stringify($themeVars); 154 | } ``` -------------------------------------------------------------------------------- /xmlui/src/components/TextBox/TextBox.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Visual enhancements**: Add icons and text at start/end positions for context and branding 5 | - **Validation states**: Built-in visual indicators for valid, warning, and error states 6 | - **Input control**: Support for initial values, programmatic focus, and value setting 7 | 8 | Often used in forms, see [this guide](/forms) for details. 9 | 10 | %-DESC-END 11 | 12 | %-PROP-START placeholder 13 | 14 | ```xmlui-pg copy display name="Example: placeholder" 15 | <App> 16 | <TextBox placeholder="This is a placeholder" /> 17 | </App> 18 | ``` 19 | 20 | %-PROP-END 21 | 22 | %-PROP-START initialValue 23 | 24 | ```xmlui-pg copy display name="Example: initialValue" 25 | <App> 26 | <TextBox initialValue="Example text" /> 27 | </App> 28 | ``` 29 | 30 | %-PROP-END 31 | 32 | %-PROP-START maxLength 33 | 34 | Try to enter a longer value into the input field below. 35 | 36 | ```xmlui-pg copy display name="Example: maxLength" 37 | <App> 38 | <TextBox maxLength="16" /> 39 | </App> 40 | ``` 41 | 42 | %-PROP-END 43 | 44 | %-PROP-START readOnly 45 | 46 | ```xmlui-pg copy display name="Example: readOnly" 47 | <App> 48 | <TextBox initialValue="Example text" readOnly="true" /> 49 | </App> 50 | ``` 51 | 52 | %-PROP-END 53 | 54 | %-PROP-START enabled 55 | 56 | ```xmlui-pg copy display name="Example: enabled" 57 | <App> 58 | <TextBox enabled="false" /> 59 | </App> 60 | ``` 61 | 62 | %-PROP-END 63 | 64 | %-PROP-START validationStatus 65 | 66 | ```xmlui-pg copy display name="Example: validationStatus" 67 | <App> 68 | <TextBox /> 69 | <TextBox validationStatus="valid" /> 70 | <TextBox validationStatus="warning" /> 71 | <TextBox validationStatus="error" /> 72 | </App> 73 | ``` 74 | 75 | %-PROP-END 76 | 77 | %-PROP-START startText 78 | 79 | ```xmlui-pg copy display name="Example: startText" 80 | <App> 81 | <TextBox startText="www." /> 82 | </App> 83 | ``` 84 | 85 | It is possible to set the other adornments as well: [`endIcon`](#endicon), [`startIcon`](#starticon) and [`endText`](#endtext). 86 | 87 | ```xmlui-pg copy display name="Example: all adornments" 88 | <App> 89 | <TextBox startIcon="hyperlink" startText="www." endIcon="email" endText=".com" /> 90 | </App> 91 | ``` 92 | 93 | %-PROP-END 94 | 95 | %-PROP-START startIcon 96 | 97 | ```xmlui-pg copy display name="Example: startIcon" 98 | <App> 99 | <TextBox startIcon="hyperlink" /> 100 | </App> 101 | ``` 102 | 103 | It is possible to set the other adornments as well: [`endText`](#endtext), [`startIcon`](#starticon) and [`startText`](#starttext). 104 | 105 | ```xmlui-pg copy display name="Example: all adornments" 106 | <App> 107 | <TextBox startIcon="hyperlink" startText="www." endIcon="email" endText=".com" /> 108 | </App> 109 | ``` 110 | 111 | %-PROP-END 112 | 113 | %-PROP-START endText 114 | 115 | ```xmlui-pg copy display name="Example: endText" 116 | <App> 117 | <TextBox endText=".com" /> 118 | </App> 119 | ``` 120 | 121 | It is possible to set the other adornments as well: [`endIcon`](#endicon), [`startIcon`](#starticon) and [`startText`](#starttext). 122 | 123 | ```xmlui-pg copy display name="Example: all adornments" 124 | <App> 125 | <TextBox startIcon="hyperlink" startText="www." endIcon="email" endText=".com" /> 126 | </App> 127 | ``` 128 | 129 | %-PROP-END 130 | 131 | %-PROP-START endIcon 132 | 133 | ```xmlui-pg copy display name="Example: endIcon" 134 | <App> 135 | <TextBox endIcon="email" /> 136 | </App> 137 | ``` 138 | 139 | It is possible to set the other adornments as well: [`endText`](#endtext), [`startIcon`](#starticon) and [`startText`](#starttext). 140 | 141 | ```xmlui-pg copy display name="Example: all adornments" 142 | <App> 143 | <TextBox startIcon="hyperlink" startText="www." endIcon="email" endText=".com" /> 144 | </App> 145 | ``` 146 | 147 | %-PROP-END 148 | 149 | %-EVENT-START didChange 150 | 151 | Write in the input field and see how the `Text` underneath it is updated in parallel. 152 | 153 | ```xmlui-pg copy {3} display name="Example: didChange" 154 | <App var.field=""> 155 | <TextBox initialValue="{field}" onDidChange="(val) => field = val" /> 156 | <Text value="{field}" /> 157 | </App> 158 | ``` 159 | 160 | %-EVENT-END 161 | 162 | %-EVENT-START gotFocus 163 | 164 | Clicking on the `TextBox` in the example demo changes the label text. 165 | Note how clicking elsewhere resets the text to its original. 166 | 167 | ```xmlui-pg copy {4-5} display name="Example: gotFocus/lostFocus" 168 | <App> 169 | <TextBox 170 | initialValue="{focused === true ? 'I got focused!' : 'I lost focus...'}" 171 | onGotFocus="focused = true" 172 | onLostFocus="focused = false" 173 | var.focused="{false}" 174 | /> 175 | </App> 176 | ``` 177 | 178 | %-EVENT-END 179 | 180 | %-API-START value 181 | 182 | In the example below, typing in the `TextBox` will also display the length of the text typed into it above the field: 183 | 184 | ```xmlui-pg copy {2-3} display name="Example: value" 185 | <App> 186 | <Text value="TextBox content length: {inputComponent.value.length}" /> 187 | <TextBox id="inputComponent" /> 188 | </App> 189 | ``` 190 | 191 | %-API-END 192 | 193 | %-API-START setValue 194 | 195 | ```xmlui-pg copy {10} display name="Example: setValue" 196 | <App var.changes=""> 197 | <TextBox 198 | id="inputField" 199 | readOnly="true" 200 | onDidChange="changes++" 201 | /> 202 | <HStack> 203 | <Button 204 | label="Check" 205 | onClick="inputField.setValue('example ')" 206 | /> 207 | <Text value="Number of changes: {changes}" /> 208 | </HStack> 209 | </App> 210 | ``` 211 | 212 | %-API-END 213 | 214 | %-API-START focus 215 | 216 | ```xmlui-pg copy {2-3} display name="Example: focus" 217 | <App> 218 | <Button label="Trigger Focus" onClick="inputComponent.focus()" /> 219 | <TextBox id="inputComponent" /> 220 | </App> 221 | ``` 222 | 223 | %-API-END 224 | ``` -------------------------------------------------------------------------------- /tools/create-app/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | #!/usr/bin/env node 2 | import { bold, cyan, green, red, yellow } from "picocolors"; 3 | import Commander from "commander"; 4 | import path from "path"; 5 | import prompts from "prompts"; 6 | import checkForUpdate from "update-check"; 7 | import { createApp } from "./create-app"; 8 | import { validateNpmName } from "./helpers/validate-pkg"; 9 | import packageJson from "./package.json"; 10 | import { isFolderEmpty } from "./helpers/is-folder-empty"; 11 | import fs from "fs"; 12 | 13 | let projectPath: string = ""; 14 | 15 | const handleSigTerm = () => process.exit(0); 16 | 17 | process.on("SIGINT", handleSigTerm); 18 | process.on("SIGTERM", handleSigTerm); 19 | 20 | const onPromptState = (state: any) => { 21 | if (state.aborted) { 22 | // If we don't re-enable the terminal cursor before exiting 23 | // the program, the cursor will remain hidden 24 | process.stdout.write("\x1B[?25h"); 25 | process.stdout.write("\n"); 26 | process.exit(1); 27 | } 28 | }; 29 | 30 | const program = new Commander.Command(packageJson.name) 31 | .version(packageJson.version) 32 | .arguments("<project-directory>") 33 | .usage(`${green("<project-directory>")} [options]`) 34 | .action((name) => { 35 | projectPath = name; 36 | }) 37 | .option( 38 | "--use-git", 39 | `Explicitly tell the CLI to initialize a git repository` 40 | ) 41 | .allowUnknownOption() 42 | .parse(process.argv); 43 | 44 | const packageManager = "npm"; 45 | 46 | async function run(): Promise<void> { 47 | if (typeof projectPath === "string") { 48 | projectPath = projectPath.trim(); 49 | } 50 | 51 | if (!projectPath) { 52 | const res = await prompts({ 53 | onState: onPromptState, 54 | type: "text", 55 | name: "path", 56 | message: "What is your project named?", 57 | initial: "my-app", 58 | validate: (name) => { 59 | const validation = validateNpmName(path.basename(path.resolve(name))); 60 | if (validation.valid) { 61 | return true; 62 | } 63 | return "Invalid project name: " + validation.problems![0]; 64 | }, 65 | }); 66 | 67 | if (typeof res.path === "string") { 68 | projectPath = res.path.trim(); 69 | } 70 | } 71 | 72 | if (!projectPath) { 73 | console.log( 74 | "\nPlease specify the project directory:\n" + 75 | ` ${cyan(program.name())} ${green("<project-directory>")}\n` + 76 | "For example:\n" + 77 | ` ${cyan(program.name())} ${green("my-xmlui-app")}\n\n` + 78 | `Run ${cyan(`${program.name()} --help`)} to see all options.` 79 | ); 80 | process.exit(1); 81 | } 82 | 83 | const resolvedProjectPath = path.resolve(projectPath); 84 | const projectName = path.basename(resolvedProjectPath); 85 | 86 | const { valid, problems } = validateNpmName(projectName); 87 | if (!valid) { 88 | console.error(`Could not create a project called ${red(`"${projectName}"`)} because of npm naming restrictions:`); 89 | 90 | problems!.forEach((p) => console.error(` ${red(bold("*"))} ${p}`)); 91 | process.exit(1); 92 | } 93 | 94 | /** 95 | * Verify the project dir is empty or doesn't exist 96 | */ 97 | const root = path.resolve(resolvedProjectPath); 98 | const appName = path.basename(root); 99 | const folderExists = fs.existsSync(root); 100 | 101 | if (folderExists && !isFolderEmpty(root, appName)) { 102 | process.exit(1); 103 | } 104 | 105 | if (program.useGit === undefined) { 106 | const { useGit } = await prompts( 107 | { 108 | type: 'toggle', 109 | name: 'useGit', 110 | message: `Would you like to initialize a git repository?`, 111 | initial: false, 112 | active: 'Yes', 113 | inactive: 'No', 114 | }, 115 | { 116 | /** 117 | * User inputs Ctrl+C or Ctrl+D to exit the prompt. We should close the 118 | * process and not write to the file system. 119 | */ 120 | onCancel: () => { 121 | console.error('Exiting.') 122 | process.exit(1) 123 | }, 124 | } 125 | ) 126 | /** 127 | * Depending on the prompt response, set the appropriate program flags. 128 | */ 129 | program.useGit = Boolean(useGit) 130 | } 131 | 132 | await createApp({ 133 | appPath: resolvedProjectPath, 134 | packageManager, 135 | useGit: !!program.useGit 136 | }); 137 | } 138 | 139 | const update = checkForUpdate(packageJson).catch(() => null); 140 | 141 | async function notifyUpdate(): Promise<void> { 142 | try { 143 | const res = await update; 144 | if (res?.latest) { 145 | const updateMessage = "npm i -g create-xmlui-app"; 146 | 147 | console.log( 148 | yellow(bold("A new version of `create-xmlui-app` is available!")) + 149 | "\n" + 150 | "You can update by running: " + 151 | cyan(updateMessage) + 152 | "\n" 153 | ); 154 | } 155 | process.exit(); 156 | } catch { 157 | // ignore error 158 | } 159 | } 160 | 161 | run() 162 | .then(notifyUpdate) 163 | .catch(async (reason) => { 164 | console.log(); 165 | console.log("Aborting installation."); 166 | if (reason.command) { 167 | console.log(` ${cyan(reason.command)} has failed.`); 168 | } else { 169 | console.log(red("Unexpected error. Please report it as a bug:") + "\n", reason); 170 | } 171 | console.log(); 172 | 173 | await notifyUpdate(); 174 | 175 | process.exit(1); 176 | }); 177 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/admonition_danger.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <path d="M18 6C11.3831 6 6 11.3832 6 18C6 24.6168 11.3831 30 18 30C24.6169 30 30 24.6168 30 18C30 11.3832 24.6169 6 18 6ZM18 8.32256C20.2505 8.32256 22.324 9.09473 23.9696 10.3881L10.3881 23.9696C9.09478 22.324 8.32256 20.2505 8.32256 18C8.32256 12.6639 12.6639 8.32256 18 8.32256ZM18 27.6774C15.7495 27.6774 13.676 26.9053 12.0304 25.6119L25.6119 12.0304C26.9053 13.676 27.6774 15.7495 27.6774 18C27.6774 23.3361 23.3361 27.6774 18 27.6774Z" fill="url(#paint0_linear_11264_27095)"/> 3 | <path d="M29.9167 18C29.9167 11.4292 24.5709 6.08333 18 6.08333C11.4291 6.08333 6.08333 11.4292 6.08333 18C6.08333 24.5708 11.4291 29.9167 18 29.9167C24.5709 29.9167 29.9167 24.5708 29.9167 18ZM26.527 18C26.527 16.4591 26.1141 15.0148 25.3961 13.7661L13.7661 25.3961C15.0148 26.1141 16.4591 26.527 18 26.527V27.6104L17.5832 27.6008C15.5096 27.5119 13.6043 26.763 12.0723 25.559L25.559 12.0723C26.8433 13.7064 27.6104 15.7652 27.6104 18L27.5977 18.4941C27.3396 23.5644 23.1334 27.6104 18 27.6104V26.527C22.7007 26.527 26.527 22.7007 26.527 18ZM18.4168 8.39917C20.4904 8.4881 22.3957 9.23699 23.9277 10.441L10.441 23.9277C9.23704 22.3957 8.4881 20.4904 8.39917 18.4168L8.38965 18C8.38965 12.7009 12.7009 8.38965 18 8.38965L18.4168 8.39917ZM9.47298 18C9.47298 19.5404 9.88531 20.9844 10.6029 22.2328L22.2328 10.6029C20.9844 9.8853 19.5404 9.47298 18 9.47298C13.2993 9.47298 9.47298 13.2993 9.47298 18ZM31 18C31 25.1691 25.1692 31 18 31C10.8308 31 5 25.1691 5 18C5 10.8309 10.8308 5 18 5C25.1692 5 31 10.8309 31 18Z" fill="url(#paint1_linear_11264_27095)"/> 4 | <path d="M31 18C31 10.9428 25.35 5.18214 18.335 5.00391L18 5C10.8308 5 5 10.8309 5 18L5.00391 18.335C5.17931 25.2386 10.7613 30.8207 17.665 30.9961L18 31C25.0573 31 30.8179 25.3499 30.9961 18.335L31 18ZM29.6162 18C29.6162 11.5949 24.4052 6.38379 18 6.38379C11.5948 6.38379 6.38379 11.5949 6.38379 18C6.38379 24.4051 11.5948 29.6162 18 29.6162V29.917L17.6934 29.9131C11.3655 29.7525 6.24754 24.6344 6.08691 18.3066L6.08301 18C6.08301 11.4292 11.4291 6.08301 18 6.08301L18.3066 6.08691C24.7364 6.25013 29.917 11.5317 29.917 18L29.9131 18.3066C29.7499 24.7363 24.4684 29.917 18 29.917V29.6162C24.4052 29.6162 29.6162 24.4051 29.6162 18ZM25.7949 11.8867C27.1191 13.5716 27.9102 15.6954 27.9102 18V18.0078L27.8975 18.502V18.5098C27.6311 23.7386 23.2939 27.9102 18 27.9102H17.9932L17.5762 27.9004H17.5703C15.4317 27.8087 13.4663 27.0363 11.8867 25.7949L11.6211 25.5859L25.5859 11.6211L25.7949 11.8867ZM12.0723 25.5586C13.6042 26.7626 15.5095 27.5116 17.583 27.6006L18 27.6104C22.9728 27.6104 27.0751 23.8136 27.5615 18.9668L27.5977 18.4941L27.6104 18C27.6104 15.7652 26.8429 13.7063 25.5586 12.0723L12.0723 25.5586ZM26.2266 18C26.2266 16.6539 25.8993 15.3851 25.3232 14.2627L14.2627 25.3232C15.3851 25.8993 16.6539 26.2266 18 26.2266C22.5351 26.2266 26.2266 22.5351 26.2266 18ZM18.0068 8.08984L18.4238 8.09961H18.4297L18.8281 8.125C20.8122 8.28992 22.6323 9.04117 24.1133 10.2051L24.3789 10.4141L10.4141 24.3789L10.2051 24.1133C8.96372 22.5337 8.19133 20.5683 8.09961 18.4297V18.4238L8.08984 18.0068V18L8.10254 17.4912C8.36824 12.262 12.7059 8.08984 18 8.08984H18.0068ZM18 8.38965C12.7009 8.38965 8.38965 12.7009 8.38965 18L8.39941 18.417C8.48838 20.4905 9.23748 22.3958 10.4414 23.9277L23.9277 10.4414C22.3958 9.23743 20.4905 8.48838 18.417 8.39941L18 8.38965ZM18.2881 9.47754C19.7202 9.52552 21.0622 9.93004 22.2324 10.6025L10.6025 22.2324C9.93005 21.0622 9.52552 19.7202 9.47754 18.2881L9.47266 18C9.47266 13.2993 13.2993 9.47266 18 9.47266L18.2881 9.47754ZM9.77344 18C9.77344 19.3452 10.0995 20.6135 10.6748 21.7354L21.7354 10.6748C20.6135 10.0995 19.3452 9.77344 18 9.77344C13.4649 9.77344 9.77344 13.4649 9.77344 18ZM26.5156 18.4385C26.2865 22.9364 22.5537 26.5273 18 26.5273C16.4593 26.5273 15.0152 26.1143 13.7666 25.3965L25.3965 13.7666C26.1143 15.0152 26.5273 16.4593 26.5273 18L26.5156 18.4385ZM31.2998 18C31.2998 25.3348 25.3349 31.2998 18 31.2998C10.6651 31.2998 4.7002 25.3348 4.7002 18C4.7002 10.6652 10.6651 4.7002 18 4.7002C25.3349 4.7002 31.2998 10.6652 31.2998 18Z" fill="url(#paint2_linear_11264_27095)"/> 5 | <defs> 6 | <linearGradient id="paint0_linear_11264_27095" x1="18" y1="6" x2="18" y2="30" gradientUnits="userSpaceOnUse"> 7 | <stop stop-color="#ED5A3B"/> 8 | <stop offset="1" stop-color="#AF3117"/> 9 | </linearGradient> 10 | <linearGradient id="paint1_linear_11264_27095" x1="18" y1="5" x2="18" y2="31" gradientUnits="userSpaceOnUse"> 11 | <stop stop-color="#EC5A3A"/> 12 | <stop offset="1" stop-color="#AF3117"/> 13 | </linearGradient> 14 | <linearGradient id="paint2_linear_11264_27095" x1="18" y1="5" x2="18" y2="31" gradientUnits="userSpaceOnUse"> 15 | <stop stop-color="#EB5A39"/> 16 | <stop offset="1" stop-color="#B03117"/> 17 | </linearGradient> 18 | </defs> 19 | </svg> 20 | ```