This is page 83 of 145. Use http://codebase.md/xmlui-org/xmlui/xmlui/tools/vscode/resources/xmlui-markup-syntax-highlighting.png?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── tender-llamas-dress.md
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog-optimized.yml
│ ├── deploy-blog-swa.yml
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs-swa.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
│ │ │ │ ├── an-advanced-codefence.gif
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── codefence-runner.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ ├── lorem-ipsum.png
│ │ │ │ ├── playground-checkbox-source.png
│ │ │ │ ├── playground.png
│ │ │ │ ├── use-xmlui-mcp-to-find-a-howto.png
│ │ │ │ └── xmlui-demo-gallery.png
│ │ │ ├── introducing-xmlui.md
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ ├── xmlui-playground.md
│ │ │ └── xmlui-powered-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ ├── staticwebapp.config.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── Choose.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
│ │ │ │ ├── control-cache-invalidation.md
│ │ │ │ ├── debounce-user-input-for-api-calls.md
│ │ │ │ ├── debounce-with-changelistener.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
│ ├── tsconfig.json
│ ├── 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
│ ├── 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
│ │ └── 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
│ ├── 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
│ ├── 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
│ ├── 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.module.scss
│ │ │ ├── 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.tsx
│ │ │ ├── StandalonePlayground.tsx
│ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ ├── ThemeSwitcher.module.scss
│ │ │ ├── ThemeSwitcher.tsx
│ │ │ └── utils.ts
│ │ ├── providers
│ │ │ ├── Toast.module.scss
│ │ │ └── ToastProvider.tsx
│ │ ├── state
│ │ │ └── store.ts
│ │ ├── themes
│ │ │ └── theme.ts
│ │ └── utils
│ │ └── helpers.ts
│ ├── 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
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Spreadsheet.tsx
│ │ └── SpreadsheetNative.tsx
│ └── 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.spec.ts
│ │ ├── HeroSection.tsx
│ │ └── HeroSectionNative.tsx
│ ├── index.tsx
│ ├── ScrollToTop
│ │ ├── ScrollToTop.module.scss
│ │ ├── ScrollToTop.tsx
│ │ └── ScrollToTopNative.tsx
│ └── vite-env.d.ts
├── playwright.config.ts
├── 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.cjs
│ ├── 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
│ ├── component-metadata.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── theme-variables-refactoring.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── extract-component-metadata.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
│ ├── generate-metadata-markdown.js
│ ├── get-langserver-metadata.js
│ ├── 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.module.scss
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ └── 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
│ │ ├── Choose
│ │ │ ├── Choose.md
│ │ │ ├── Choose.spec.ts
│ │ │ └── Choose.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
│ │ │ └── test-padding.xmlui
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── 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
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.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.json
├── tsdown.config.ts
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
# xmlui
## 0.11.7
### Patch Changes
- de17ae1: Add the Choose component to xmlui
- dc3d889: Fix long text handling in markdown and dialogs
## 0.11.6
### Patch Changes
- f9ac95e: Fix the variant behavior. It uses a React component now.
- 097783b: Fixed Table row deselection if multiple row selections are disabled.
- 0bace0a: Add clearable property to Select
- 07b1a3d: Add a new layout, "desktop", to App
- 0bace0a: Add padding theme vars to ContentSeparator
- 0bace0a: Review Select fontSize and minHeight theme variables
## 0.11.5
### Patch Changes
- 10d755e: refactor: xmlui-playground - design update
- b3a4194: Fixed a case where if the data provided to a Table did not have 'id' attributes, the row selection would not work correctly.
## 0.11.4
### Patch Changes
- e384c59: Change validation status signature from having one dash to two dashes to better reflect that they are status modifiers. Ex. -default -> --default, -error -> --error
- f296904: Splitter now responds the visibility changes of its children
- f296904: Temporarily disable the "variant" behavior on Button
- be73336: fix: Select - use extractValue for controlled component value prop
## 0.11.3
### Patch Changes
- 4a311e2: improve: charts - better domain configuration
- f8a75ce: Tiny Splitter updates
- bbc421b: Text strong variant style fixed
## 0.11.2
### Patch Changes
- c1f306f: update package dependencies for tsx usage
## 0.11.1
### Patch Changes
- 7bbbb1d: Add the debounce function to globals
## 0.11.0
### Minor Changes
- 19145d2: xmlui builds with ESM
- 5109dce: Migrate from CommonJs to ESM internally. Lays the groundwork for exporting testing capabilities.
### Patch Changes
- fe503eb: Add enableSubmit to Form
- 06bb966: Fix TableOfContents styling
- e6b5810: fix: playground - app reset
- db94656: improve: bar/line chart spacing
- fe503eb: Queue now passes $queuedItems and $completedItems context variables to its event handlers
- 82ddbe7: Fix codefence first line indent style issue
- 75b701b: Extend form with hideButtonRow and FormItem with noSubmit
## 0.10.26
### Patch Changes
- e1b8d58: Heading now accepts "H1"..."H6", "1"..."6", too. Invalid values fall back to "h1".
- 1ad832c: Remove the showNavPanelIf property from AppHeader (fix NavPanel's "when" usage)
## 0.10.25
### Patch Changes
- e7c503e: refactor: Select - remove radix select
- 5fe3052: Fix the NavGroup click behavior in responsive view
- 5fe3052: Fix the $item access issue within a ModalDialog inside a Column
- 250647b: Fix the APICall becomes non-functional after first error when used with DataSource + Items + $item context issue
## 0.10.24
### Patch Changes
- 3e361c4: The xmlui-pg codefence now accepts emojies
- 3e361c4: Exend the formatDate, formatDateTime, and formatTime functions with optional format strings
## 0.10.23
### Patch Changes
- bf18444: Experiment with the "variant" behavior
- 6d3bb89: Form now has a willSubmit event (it can cancel the submit event)
- 89c69af: Fix the boolean conversion issue with showAnchor in Heading
- 4cfebf0: Fix loading code-behind files in standalone mode
- 145cd68: fix: pointer-events:none when using sibling Dialog
## 0.10.22
### Patch Changes
- 501f60a: The behavior infrastructure now uses ComponentProvider and allows adding custom behaviors
- 1020f1c: Extend Tab with the tabAlignment and accordionView properties
## 0.10.21
### Patch Changes
- 6fd4d62: Add custom Text variant styling
## 0.10.20
### Patch Changes
- 26eac90: fix: Autocomplete handles animations correctly
- f53edff: Add margin-related theme variables to ContentSeparator
- 1840916: Add applyIf property to Theme
- c6be7a3: fix: external animation is now correctly applied to ModalDialogs as well
- 6aaefaf: Added better error text when rendering FormItem outside of a Form.
- 28d2585: refactor: Select and AutoComplete components
- e29a231: The itemLabelWidth value of Form now supports theme variables ($space-\* values).
- 22162c0: AppState now uses a merge operation to set initialValue
- e90232b: fix: itemWithLabel - layout issue
## 0.10.19
### Patch Changes
- facb257: Add checkboxTolerance property to Table
- 6084c14: test: review onFocus, onBlur e2e tests
- e1fa9d7: Renamed the following properties in DatePicker: minValue -> startDate, maxValue -> endDate. Also updated component documentation.
## 0.10.18
### Patch Changes
- 202f2b2: refactor: use labelBehavior instead of ItemWithLabel
- 6650ee8: Add back removed RadioItem
- da98994: Fixed FormItem validation indicators to use a relaxed validation indication strategy.
- 8394663: fix: labelBehavior, input components - styling issue
## 0.10.17
### Patch Changes
- 07dae0b: fix: AccordionItem produces the right error outside of Accordion component
## 0.10.16
### Patch Changes
- 0ba6612: Undust and improve the Tree component
- 7b78052: Fixed Slider ranged version where only the first thumb is interactable.
- 314b429: improve: remove cmdk from autocomple, add keywords prop to option
- a1dea8f: fix: NumberBox initialValue ignores non-convertible string values, minValue and maxValue now applies to typed-in input as well as to increments / decrements with spinner buttons.
- cff754c: refactor: move behavior application earlier in ComponentAdapter render flow
## 0.10.15
### Patch Changes
- 3c8ad14: Add the data property to the Pdf component
- 5502fea: Add the "transform" layout property
- e08f0ba: Add syncWithAppState and initiallySelected properties to Table
- 5502fea: Fix MenuSeparator and SubMenuitem (forwardRef)
- db618b5: fix: NavGroup componenet's iconVertical{Expanded,Collapsed} properties now apply based on it's 'open' state. Only the Expanded one was present before the fix.
- a795b3d: Allow event handlers to use nested action components recursively
- 5851c02: feat: introducing behaviors - tooltip, animation, label
## 0.10.14
### Patch Changes
- 618049b: fix: Modal dialog scrolling issue
- 215a142: Allow image to accept binary data (and use it instead of src)
- 65b52e1: Allow user-defined components in extension packages
- 0cc2178: Fixed Slider input type, label focus, readOnly property, as well as min & max value issues.
- 53d4ed9: Fixed feature to add custom icons for the Carousel prev/next page buttons.
## 0.10.13
### Patch Changes
- 9401ee0: Added short debounce to ColorPicker to make changing color values with slider a bit smoother.
- eb62858: fix: assigning new properties to objects in xmlui script
- eb62858: fix: stricter empty body detection in RestApiProxy
- eb62858: fix: TextArea autofocus
- eb62858: fix: dropdownMenu overflow
- eb62858: fix: ability to use user defined components in triggerTemplate (dropdownMenu)
- eb62858: select: use focus-visible instead of focus for outline
- 243b7fa: fix: modal dialog/toast issue
- eb62858: form: hideButtonRowUntilDirty
## 0.10.12
### Patch Changes
- f12a042: fix: report errors in script tag
- 8731eb8: Avatar does not issue a resource URL warning when "url" is not specified
- eb6454f: refactor: change LineChart/BarChart property names
- 1210852: Fix the layout property usage of ModalDialog
## 0.10.11
### Patch Changes
- 8c76c8d: feat: style the error report colors and spaces in the browser for xmlui syntax errors
- d56c3e5: RadioGroup now correctly handles different types of initialValue property values, applies readOnly property, and places necessary ARIA tags if the required property is set. Clarified component reference description on how RadioGroup and Option handles value types.
- e42d367: Add FancyButton to xmlui-website-blocks
- f539526: feat: BarChart - add tooltip position tracking
- 19ce234: Review Option handling in Select and RadioGroup
- 455b6c0: feat: add animation support to all relevant components via animation and animationOptions props
- e90dc73: feat: add support for 'uses' property
- 819b563: Update fontSize and lineHeight themes and style (may break existing xmlui code)
- b57dfa2: Add the autoDetectTone property to App
- 9dd0f97: Update Checkbox and Switch with click event metadata
- 19ce234: Select accepts null as an option value; it converts a value to a string no longer
- 898346d: Extend Text and Heading APIs with hasOverflow
- 705dd04: Fix RestApiProxy to deliver response status when no error body specified
## 0.10.10
### Patch Changes
- fff80c5: Bump package version
## 0.10.9
### Patch Changes
- 879c09d: Component part handling and testing refactored
- 3ad8514: Added tooltip value display to Slider thumbs. Removed value display from Slider label.
- 0c69245: fix: virtualized list/table in ModalDialog
- 4ad31fc: refactor: rename dataKeys/nameKey to xKeys/yKey and layout to orientation in chart components - BarChart, LineChart
- c99f184: Fix ExpandableItem focus issue
- 5032e4a: Experimenting with HeroSection
- 2394f36: Enhance DateInput and TimeInput
## 0.10.8
### Patch Changes
- a4d62c4: Add experimental Timer component
- 7ed2918: Add the appendBlob function to the ApiInterceptor backend
## 0.10.7
### Patch Changes
- 664ea4f: Fixed BarChart hideTickY property to not remove the Y axis when set to true.
- a739a26: Fixed Checkbox and Switch visual issue. Fixed Line- and BarChart visual glitch in Table.
- bdb54dd: Small fixes for tiny bugs found during MyWorkDrive update
- 81724c6: Fixed BarChart tick labels not appearing.
## 0.10.6
### Patch Changes
- 6464ec8: fix ssr
## 0.10.5
### Patch Changes
- d38351d: fix missing dependency
## 0.10.4
### Patch Changes
- 43fd8c5: small fixes: Avatar, FileUploadDropzone, auto xsrf token handling
- 1df8e5d: Autocomplete: initiallyOpen prop
- 0d5d9d1: Reworked Pagination layout strategy. Provided props to better control layout: pageSizeSelectorPosition, pageInfoPosition, buttonRowPosition. These props are available in Table pagination as well.
- 3def673: DropdownMenu doesn't cooperate with Fragment triggerTemplate
- 428ebea: include themes.scss file in lib dist
- a12ce66: FileUploadDropZone fixes (dropPlaceholder disappeared)
## 0.10.3
### Patch Changes
- 2e512bb: Add solid overflow handling modes to Text
- 46d1d18: Remove the "codefence" Text variant
- 6bc9ed1: feat: support aligning cells in a Table row vertically
- 0b1f983: Add new, compound layout property name parsing
- a2637f3: Text is displayed as inline (you can nest Text into Text)
- eb4d592: Adding the "part" concept to native components
## 0.10.2
### Patch Changes
- ff14e15: fix: LineChart - sizing issue
- 1451a94: feat: make input padding configurable via theme variables
## 0.10.1
### Patch Changes
- 442416b: Refactor visual components to allow tooltip
- a018431: feat: add custom tooltip template support for Bar and Line charts
- 33cb547: Pagination component now handles itemCount being undefined/null. Introduced hasPrevPage and hasNextPage props to toggle button disabled state.
- b5d7537: Enhance the disabledDates property of DatePicker
## 0.10.0
### Minor Changes
- 000a311: Add tooltip behavior to visible components
- eb8b958: Rework inline styling system, prepare for responsive styling
### Patch Changes
- 6d0ce52: Added features to the Pagination component: page size selector dropdown control. Also added the following props: layout orientation and layout order reversal.
- 8c98f33: feat: add theme variable support for LineChart stroke width
- ef86593: feat: add didChange event handler to Tabs component
- da5f4e7: test: create e2e tests for chart components
- 47c7a2d: Integrated the new Pagination component with Table.
- 740f904: Add "activated" event to TabItem
- 5009c52: Add "parts" to component metadata
- 2f5ec32: Remove "from" from the list or reserved script keywords, as no longer used
## 0.9.101
### Patch Changes
- 791b0be: Experimenting with issuing release on larger GitHub machines
## 0.9.100
### Patch Changes
- 2dbf6d2: Added accessibility features, enabled prop and defaultThemeVars to Pagination. Also created E2E test cases for Pagination
## 0.9.99
### Patch Changes
- e5a09fb: Added a separate Pagination component with events and API methods for custom pagination.
- 36360f6: improve: add tickFormatterY to LineChart, create e2e tests
## 0.9.98
### Patch Changes
- ff781f3: new internal react component for integrating into existing react applications (StandaloneComponent)
- 377f0f2: Fix image animation issue in Carousel
- ce0ff76: Added hover & active styles for Slider on thumb. FileInput opens file browser on label focus.
- 208768a: Fixed input adornments not changing color on setting their respective theme variable. Spinbox buttons in NumberBox now have role=spinbutton.
## 0.9.97
### Patch Changes
- f7e8019: Implement simple IFrame APIs
## 0.9.96
### Patch Changes
- 3196156: Add IFrame component (first prototype)
- cfee78a: NumberBox tweaks: fixed missing padding theme var, fixed incorrect label association.
- f51002a: fix: Tabs - descendant button warning
- 3fa52d9: fix: Table sortBy now works as expected
## 0.9.95
### Patch Changes
- af6a7a0: fix: Tabs - fixed the inconsistency in the headerTemplate.
- 69a2a8f: Fix the useEventHaddler hook
- 29c68fe: fix: H1 ... H6 now ignores the level property
## 0.9.94
### Patch Changes
- 1d9365c: feat: Tabs component - use headerTemplate instead of labelTemplate/tabTemplate
## 0.9.93
### Patch Changes
- af17117: feat:add labelTemplate prop to TabItem component
- 44da3d9: The transformation of Checkbox and Switch values (to Booleans) are now documented and tested
- b7a6b9a: Fix formatHumanElapsedTime unit tests, make the local-independent
- bc95844: improve: Select and AutoComplete components
- 52d94a2: Fix the ComponentWrapper childrenAsTemplate issue
- 6629ce5: New end-to-end tests reviewed
- 0254471: Fixed the initialValue issue with TextArea
- 3318cfb: feat: provide context in browser error reports
## 0.9.92
### Patch Changes
- 347cda1: Review component e2e tests
## 0.9.91
### Patch Changes
- 6a7d779: Review Slot implementation
## 0.9.90
### Patch Changes
- 4b57f7e: Remove Spinner tests
## 0.9.89
### Patch Changes
- 2968eb9: fix initialValue handling in selects in forms
- 94f4eb5: safari regexp error workaround for optimized build, revert select inside form fix
- 8364c03: add new TextBox and TextArea test cases
## 0.9.88
### Patch Changes
- b79d7d8: Fix flaky Checkbox e2e tests
## 0.9.87
### Patch Changes
- 33846c2: Fix ios regex failure
## 0.9.86
### Patch Changes
- 48af60d: Temporarily suspend new checkbox e2e tests
## 0.9.85
### Patch Changes
- ee8d6ad: Fix "required" validation issue with "integer" and "number" FormItem
- 9ca7572: Extend the component API metadata with method signature and parameter descriptions
- 6944d2f: Add a scrollIntoView method to Heading
- c0c10e7: Added missing autoFocus feature and aria labels to Checkbox
- cbe1ef2: Use grammar and syntax highlight files straight form the xmlui package, instead of duplicating them in every app.
## 0.9.84
### Patch Changes
- c54abf3: update deps
## 0.9.83
### Patch Changes
- 8e3d6a3: Prevent the xmlui-optimizer to raise error on ShadowRoot
- 8644010: Add a scrollIntoView api to Bookmark
## 0.9.82
### Patch Changes
- 3bc29ae: fix: account for events with components inside them (like APICall) in a way that more syntax highlighters understand. VSCode worked fine, Shiki did not.
- 1101bf5: Fix a React warning in MarkdownNative (headingRef)
- cd8db58: Fixed ModalDialog overlay and fullScreen in nested apps. Now dialogs defined in nested apps stay inside them.
- 13beb58: Fixed ModalDialog context error when dialog is called from ApiCall or components using "confirm" in XMLUI code.
- 79c1d8a: fix: allow the playground to use the same tone as its source
## 0.9.81
### Patch Changes
- 59680b7: Allow configuring the initiallyShowCode flag in ComponentViewer
## 0.9.80
### Patch Changes
- 4598566: NumberBox and FromItem type="number" accepts numeric string as initialValue
- 14e6a7d: feat: add splitView to code inspection
- cf05bd2: Fix non-fatal StandaloneApp.tsx issue
## 0.9.79
### Patch Changes
- ad21a31: enhance treeshaking
## 0.9.78
### Patch Changes
- 94a68f0: Toggle password visibility in PasswordInput
- 94a68f0: Extend markdown to render compound headings with code spans and anchors
- 163a45c: Add ToneSwitch with icon customization
- 7ce528b: fix: BarChart - size management
- c6eb9a8: Fixed scrolling to specific Bookmarks inside nested apps.
## 0.9.77
### Patch Changes
- c867f38: Change split view startup animation
## 0.9.76
### Patch Changes
- aa08a8c: introducing ApiInterceptor->useWorker: true/false
- 15bf622: fix: add escaped \{ to textmate syntax, eliminate double extraction of props in FormItem causing bugs with escaped open curly brace being parsed as start of binding expression.
- 5761868: improve: BarChart - add tick formatter for X and Y axes
## 0.9.75
### Patch Changes
- c876be8: Turn docs deploy to standard routing
## 0.9.74
### Patch Changes
- 0043c5d: NestedApp new prop: withSplashScreen
## 0.9.73
### Patch Changes
- 88bf4f6: extend formatHumanElapsedTime with a short format flag
- fef53db: Allow specifying href targets with the markdown link tag
- 6167648: Fix the useMouseEventHandlers hook
- b2f4483: Fix missing code fence display
- e9040c6: Make the nested app's header smaller
## 0.9.72
### Patch Changes
- 4ab3b8a: add omitH1 to TableOfContents
- ac4a283: remove the AppWithCodeView component
- 38454c9: fix ApiInterceptor race conditions (inside NestedApps)
## 0.9.71
### Patch Changes
- 5774c53: fix ssr issues with Theme components
## 0.9.70
### Patch Changes
- 1da7847: Adjust CodeBlock theme variables for dark tone
## 0.9.69
### Patch Changes
- 9b36621: fix flaky Checkbox tests
## 0.9.68
### Patch Changes
- 9b1f718: change: add back the logo and the buttons to the xmlui-pg split view
- c79ced7: fix ssr hydration warn in AppWithCodeView
- d030ac2: A few theme variable defaults updated
- 21c4fd6: fix: mocked apis should work with multiple NestedApps
## 0.9.67
### Patch Changes
- 51a5b05: Small changes in a few component's metadata representation
- 9048af1: Remove the header logo and buttons from the AppWithCodeViewNative component
- 94f0e66: Accounted for some bad inputs in code fences when highlighting rows & substrings
- 3f0e6b0: fix memoization for tabs, pageInfo
## 0.9.66
### Patch Changes
- eae8145: Fixed Switch indicator positioning
- b6c64de: improve: charts - improved tick rendering
- 459bd3c: improve: Logo - add inline, alt props
- 96be435: feat: CodeBlock - add new themeVariables
## 0.9.65
### Patch Changes
- c17fc0d: fix the NestedAppNative.tsx issue introduced in #1547
## 0.9.64
### Patch Changes
- 5ad3ffc: Refactored the usage of theme variables in RadioGroup
- da3c8bc: Add a "noHeader" option to the xmlui-pg codefence
- 301cb39: Allow YAML (.yml) theme files in standalone apps
- d5d3f4d: Fixed Bar- & LineChart sizing in the Table component
## 0.9.63
### Patch Changes
- b9c0881: Fix: add a workaround to ListNative to avoid issues coming from undefined row values
## 0.9.62
### Patch Changes
- 832f31d: fix: nestedApp fills the available space in AppWithCode component
- 4f9ff06: Fix the build issue with FormSection
## 0.9.61
### Patch Changes
- 4ef5f3f: This version does not contain any real changes; it's just for bumping the version number.
## 0.9.60
### Patch Changes
- f37ed8c: Fine tune AppWithCodeView header
- 736dbc8: improve: AppWithCode - center the XML/UI buttons
- e2a6e1a: Add a popOutUrl="<url>" option to xmlui-pg to allow pop out to a custom playground location
## 0.9.59
### Patch Changes
- 2a07157: Rename Pages property 'defaultRoute' to 'fallbackPath'
- 97b3241: improve: expanding the styles of the components responsible for code display with new theme variables.
- c4abb20: Fixed RadioGroup disabled and validation indicator states. Also fixed an issue where the checked indicator was not aligned to center if the RadioGroup Option was resized in some way.
- f19720c: Added 0 min width to PieChart, Fixed focus error when one checkbox's state change depended on another
- 66c2288: Fixed NavLink indentation in horizontal App layout, if in a nested NavGroup in the NavPanel
- 2d27204: Fixed a number of color & visual state representations of the components: DatePicker, Switch, Select, TextBox, TextArea, NumberBox, AutoComplete
## 0.9.58
### Patch Changes
- dc43275: Fixed Pie- & DonutChart height property.
- f9562b5: make flowLayout auto-responsive behavior a bit smarter
- 1af11af: fix: eliminating the duplication of toast messages
- de570c2: Fixed number of small issues: Colorpicker now gets correct initial value, Options in Select now get correct keys, removed Tabs tabTemplate prop because of a bigger bug.
- 7d255a9: Changed open in new window button tooltip label for all occurrences.
- 69a7a1f: Fixed NavLink label break if overflowing available space.
- 873348c: new form properties: onSuccess, inProgressNotificationMessage, completedNotificationMessage, errorNotificationMessage
- 46bfe72: default style tweaks
## 0.9.57
### Patch Changes
- 93a1e70: fix: NavPanel - use layoutCss
## 0.9.56
### Patch Changes
- 9a3c3b6: feat: xmlui-devtools - start dialog animation from the click, use exit animation as well
## 0.9.55
### Patch Changes
- d507ea8: Add AppWithCodeView component to display code and running app side-by-side
## 0.9.54
### Patch Changes
- 2688a95: Change TreeDisplay theme variable defaults
## 0.9.53
### Patch Changes
- c64fa25: Allow turning on/off heading anchors in appGlobals
- 73c2c21: wip: code inspector buttons - label change, devtools - animation update
## 0.9.52
### Patch Changes
- d079208: The Footer component no provides a themeable gap between its children.
- 2a461d8: feat: NestedApp works with ApiInterceptor
- ad6d81e: fix NestedApp apiUrl overwrite
- f5b9f15: feat: xmlui-devtools - use it in a modal dialog
- 88e4741: fix: Table columns do not allow (and indicate) sorting when bindTo is not set
- 7af4b4e: change default borderColor
- 851ae21: fix table styling
- 7872ed0: Default theme variables changed for App, NestedApp, TableOfContents, and Text
- bf00dce: enhance xmlui parser error tolerance, recovering from unclosed tags
- 38180ce: merge xmlui-charts into core
## 0.9.51
### Patch Changes
- ef7add9: Added theme variable for setting the horizontal alignment of the logo in the NavPanel.
- ba2b5cd: Moved Drawer logo position to left.
- 96273bf: fix: Slider - min/max value validation
- 1a81bcf: fix: Markdown renders inline/block images correclty
## 0.9.50
### Patch Changes
- e6c3b39: standalone usage: explicit codeBehind reference
- 85031c8: Make the "marked" Text variant have lighter background color in dark mode.
- d349036: Tweaked Search dropdown panel styles. Corrected Link component text and decoration hover and active colors
## 0.9.49
### Patch Changes
- 9afd588: fix: XmluiCodeHighlighter - update token colorizing (light/dark tone)
- Updated dependencies [3b5e820]
- [email protected]
## 0.9.48
### Patch Changes
- b5104b0: feat: Icon component now handles the click event
- 30d5c58: feat: Badge supports theme variable names in colormap
- 2e7f51f: change: the canSort property of Column defaults to true
- 4dd6d7f: feat: chart extension included by default
- f7f0571: fix theme component
## 0.9.47
### Patch Changes
- a5bef5d: feat: add "inherit" variant to Text
- ecc52d1: XMLUIExtensions namespace is optional
- 4322e1b: fix: search context scope
- 391927c: feat: add xmlui-tree codefence (displays a tree) to Markdown
## 0.9.46
### Patch Changes
- e20e867: improve: DatePicker - change chevrons, Slider - design updates, change drawer icon's padding
- 1f83bb2: Tables in Markdown scroll horizontally if there's not enough space.
- c433512: Removed close button from TextBox if type="search". Move the Search package from internal, add arrow key selection in search results and add use it in navigation drawer on small screens.
- bc68330: tweak search indexer
- ef3d208: improve: DatePicker - update chevrons
## 0.9.45
### Patch Changes
- de8d63c: Fixed small issues in CodeBlocks: adjusted row highlight length, substring highlight now works with '=' signs, corrected minor vertical positioning of code without syntax highlight, temporarily removed row numbering.
- bd6d1b4: experimental: runtime search indexing
- db5a5f4: fix: Allow APICall as an event handler
- 69b4402: improve: docs - footer logo, FormItem - labelBreak
## 0.9.44
### Patch Changes
- 3eab4a3: improve: design updates - devtools, playground, docs
- 411cd34: fix: inbound links to headers in markdown (anchor scroll slightly off)
- cdf96bb: fix: table inside flowlayout, horizontal scrollbar
- 121c55c: Changed the background color of the `marked` Text variant.
- f1092fe: Added emphasized substring highlights to CodeBlocks. Use it in markdown the following way: ```xmlui !/substr/
## 0.9.43
### Patch Changes
- e2324bb: fix prefetchedContent handling
- cacbf26: improve: AutoComplete - updating the selection logic, improved handling of readOnly and multi states, and removing unused or redundant code, improving tests
- 05c8dfe: test: DatePicker - fix e2e "disabled state prevents interaction"
- 42571db: test: create tests for the AutoComplete component, fix bugs
- 05205c7: Add diagnostics to language server
- 0a3d059: fix initial offset calculation for virtualized table/list
## 0.9.42
### Patch Changes
- 1ab3881: ssr fixes, experimental prefetchedContent
- 3d9729d: test: add tests for the DatePicker component
## 0.9.41
### Patch Changes
- 42416ba: test change for CI #2
## 0.9.40
### Patch Changes
- 34282f0: chage to test CI
## 0.9.39
### Patch Changes
- b79ca46: improve: DatePicker - design update, XmluiCodeHighlighter - use layoutCss
- bbec7a9: Added implicit code highlighter identification by Markdown if one is exposed under the name "codeHighlighter" in the appGlobals in config.
- e67762b: Replaced Admonition emojis with icons
## 0.9.38
### Patch Changes
- d314bad: msw update
## 0.9.37
### Patch Changes
- 1c33896: ssr fixes
- 8d662f3: Added anchor links for headings in markdown. Showing anchors is disabled by default, use the showHeadingAnchors prop on a Markdown component to use it.
## 0.9.36
### Patch Changes
- 6b0f2c1: fix: itemWithLabel fills the available width if no other value is specified
## 0.9.35
### Patch Changes
- ef3cd6e: Reworked NavLink & NavGroup styling: added disabled, hover & active states to button version
## 0.9.34
### Patch Changes
- bae8280: export NestedApp component
- 415aa66: Added color palette colors for CodeBlock, vertical NavPanel now has fixed scrollbar gutter, fixed vertical collapsed icon for NavGroup.
## 0.9.33
### Patch Changes
- dabeb53: Fixed NavPanel background color
## 0.9.32
### Patch Changes
- 4019d82: xmlui-playground, new exports from xmlui
- 450e1ee: feat: add aria-placeholder to Select component
## 0.9.31
### Patch Changes
- ed53215: test release
- ed53215: another testing
## 0.9.30
### Patch Changes
- b0ae113: testing
## 0.9.29
### Patch Changes
- f15c018: another testing
- f15c018: testing
## 0.9.28
### Patch Changes
- 421968b: testing
## 0.9.27
### Patch Changes
- 99bba69: testing
## 0.9.26
### Patch Changes
- bcf162c: testing changesets
```
--------------------------------------------------------------------------------
/xmlui/conventions/testing-conventions.md:
--------------------------------------------------------------------------------
```markdown
# XMLUI Component Testing Conventions
This document outlines the testing conventions and standards for XMLUI components using Playwright for end-to-end testing.
## Test Categories
XMLUI components must be tested across four categories:
1. **Basic Functionality** - Core behavior but also test cases for each property, event, exposed method of the component. Each such item can be it's own category, if there are sufficiently numerous test cases for it (like >= 5, but that's not a hard requirement). It might make sense to combine multiple properties into the group, like `icon` and `iconPosition` under the `icon prop` group. For the props, also handle edge cases like null, undefined, unexpected input types like objects where text is expected for props, invalid values (like -5 pixels for a width prop), specially long unicode characters like 👨👩👧👦, or chinese characters. For interactive componenets, test interactivity with keyboard and mouse. For Layout Components, test arrangement, spacing
```typescript
test("renders with basic props", async ({ initTestBed, page }) => {
await initTestBed(`<ComponentName size="sm" variant="primary"/>`);
await expect(page.getByTestId("component")).toBeVisible();
});
```
2. **Accessibility**
Has ARIA attributes, keyboard navigation and conforms to Accessibility standards.
These tests are partially to conform to WCAG and to reach a better score on benchmarks for them.
```typescript
test("has correct accessibility attributes", async ({ initTestBed, page }) => {
await initTestBed(`<ComponentName label="Test Label"/>`);
const component = page.getByLabel("Test Label");
await expect(component).toHaveRole("button");
});
```
3. **Theme Variables** - CSS custom properties and fallback behavior. Use exact browser values: `"rgb(255, 0, 0)"` not `"red"`. **Only include this category for components that support theme variables** (check component metadata for theme variable documentation).
```typescript
test("applies theme variables", async ({ initTestBed, page }) => {
await initTestBed(`<ComponentName/>`, {
testThemeVars: { "backgroundColor-ComponentName": "rgb(255, 0, 0)" },
});
await expect(page.getByTestId("component")).toHaveCSS("background-color", "rgb(255, 0, 0)");
});
```
4. **Other Edge Cases** - These are the test cases that fall into the edge cases category, but not within one particular property. So a button with an object variant should still live in the basic functionality category, within the variant prop group. Here could be a test for a datepicker stating that it covers the text below it (the text is not visible), when the user clicks on it, because the detepicker menu appears. This doesn't belong to a property, but is still an edge case.
```typescript
test("handles no props gracefully", async ({ initTestBed, page }) => {
await initTestBed(`<ComponentName/>`);
await expect(page.getByTestId("component")).toBeVisible();
});
```
## File Organization
- **Location**: Test files MUST be in the same directory as the component's implementation.
- **Naming**: Use `ComponentName.spec.ts` format
- **Import**: `import { test, expect } from "../../testing/fixtures";`
## Context Information
Xmlui is a declarative, reactive, component based web framework.
Checkbox.spec.ts is an excellent testing file with good examples, and it's a great resource on how to write tests.
### Documentation location
There's documentation for the components, which we call metadata. They lives in the component's file, like `Button.tsx` next to the testing file `Button.spec.ts`, but not in the native implementation file `ButtonNative.tsx`. Generally, you should not read or rely on the native implementation details.
There's also documentation in the `.md` files, next to the component's file.
#### Component Metadata (Required Reading)
**ALWAYS read the component's `.tsx` file before creating tests.** The component files contain essential metadata that documents:
- **Properties**: All available props with their types and descriptions
- **Events**: Available event handlers and their parameters
- **Theme Variables**: Default values and names for CSS custom properties used in theme testing
Use the documented theme variable names when creating theme tests instead of guessing:
```typescript
// ✅ CORRECT - Use documented theme variable names from metadata
test("applies theme variables", async ({ initTestBed, page }) => {
await initTestBed(`<Button/>`, {
testThemeVars: { "backgroundColor-Button": "rgb(255, 0, 0)" }, // From metadata
});
await expect(page.getByTestId("component")).toHaveCSS("background-color", "rgb(255, 0, 0)");
});
```
#### Documentation Files (Highly Recommended)
**ALWAYS check for and read the component's `.md` documentation file** in the same directory. This documentation:
- **Merges with metadata** to provide complete component information
- **Contains comprehensive examples** showing real usage patterns
- **Documents advanced features** and edge cases
- **Provides context** for proper testing scenarios
Example documentation locations:
- `Button.tsx` - Contains metadata
- `Button.md` - Contains examples and detailed usage
- `Button.spec.ts` - Your test file
**Best Practice**: Read both the `.tsx` metadata AND the `.md` documentation before writing any tests to ensure comprehensive coverage of all documented features.
### Commands
These commands should be ran inside the npm package named `xmlui`, which is inside the directory called `xmlui`.
There might be a root level directory called `xmlui` (a monorepo) which contains the `xmlui` subdirectory. You want to run the commands from the `xmlui` subdirectory.
```bash
# Standard execution
npx playwright test ComponentName.spec.ts
# Category-specific
npx playwright test ComponentName.spec.ts --grep "accessibility"
# Development (recommended during creation) - prevents HTML report auto-opening
npx playwright test ComponentName.spec.ts --reporter=line
# Single worker for debugging (prevents race conditions)
npx playwright test ComponentName.spec.ts --workers=1
# Fast feedback during development (single worker + line reporter)
npx playwright test ComponentName.spec.ts --workers=1 --reporter=line
```
### Timeout Configuration
XMLUI uses optimized timeout settings for faster feedback during development:
- **Expect timeout**: 1000ms (1 second) - How long to wait for assertions like `expect.poll()`
- **Test timeout**: 5 seconds - Maximum time for entire test execution
- **Global timeout**: Configured in `playwright.config.ts`
These settings ensure tests fail quickly when conditions aren't met, providing rapid feedback during development. The shorter expect timeout helps identify issues faster than the default 5-second timeout.
**Important**: Never manually show the HTML report or wait for Ctrl+C during test development. Use `--reporter=line` to get immediate console feedback without browser interference.
### Development Testing Commands
For comprehensive debugging and development, use these command combinations:
```bash
# Best practice during test development - single worker + line reporter
npx playwright test ComponentName.spec.ts --workers=1 --reporter=line
# Debug specific failing tests only
npx playwright test ComponentName.spec.ts --grep "test name pattern" --reporter=line
# Run specific test categories during development
npx playwright test ComponentName.spec.ts --grep "Basic Functionality" --reporter=line
```
The line reporter provides detailed progress information and immediate feedback about test failures without opening browser windows or HTML reports, making it ideal for iterative test development.
### Event Handler Naming
**ALWAYS use "on" prefix for event handlers:**
```typescript
// ✅ CORRECT
onClick = "testState = 'clicked'";
onWillOpen = "testState = 'opening'";
// ❌ INCORRECT
click = "testState = 'clicked'";
willOpen = "testState = 'opening'";
```
**Event vs Handler distinction:**
- **Event names** (no "on"): Used in `<event name="click">` tags
- **Event handlers** (with "on"): Used as attributes `onClick="..."`
### Event Handler Parameters
**ALWAYS use arrow function syntax:**
```typescript
// ✅ CORRECT
onExpandedChange = "arg => testState = arg";
onClick = "event => testState = event.type";
// ❌ INCORRECT - arguments object doesn't work
onExpandedChange = "testState = arguments[0]";
```
most of the time (when the event handler does not need to access the event object),
you can omit the arrow function and write the handler body directly.
### XMLUI Script Limitations
XMLUI scripts have important JavaScript syntax limitations that must be followed:
**NO "new" operator support:**
```typescript
// ❌ INCORRECT - "new" operator not supported
throw new Error("test error");
const items = new Set([1, 2, 3]);
const date = new Date();
const regex = new RegExp("pattern");
// ✅ CORRECT - Use alternatives
throw "test error"; // String-based error throwing
// Manual uniqueness check instead of Set
const uniqueCheck = {};
let allUnique = true;
for (let i = 0; i < items.length; i++) {
if (uniqueCheck[items[i]]) {
allUnique = false;
break;
}
uniqueCheck[items[i]] = true;
}
// Use string literals for dates
const dateStr = "2025-08-07";
// Use string patterns instead of regex literals
const pattern = "test";
```
### Non-Visual Component Testing
For non-visual components (like Queue, DataStore), use Button click handlers to access APIs:
```typescript
// ✅ CORRECT - Access APIs through Button onClick
const { testStateDriver } = await initTestBed(`
<Fragment>
<Queue id="testQueue" />
<Button onClick="testState = testQueue.enqueueItem('test')" />
</Fragment>
`);
// ❌ INCORRECT - Script tags don't provide API access
const { testStateDriver } = await initTestBed(`
<Queue id="testQueue" />
<script>testState = testQueue.enqueueItem('test')</script>
`);
```
**Important**: Non-visual components often require event handlers (like `onProcess`) to exhibit expected behavior. Without processing handlers, queue-like components may not retain items as expected.
### Template Properties
**ALWAYS wrap template properties in `<property>` tags:**
```typescript
// ✅ CORRECT
<ComponentName>
<property name="triggerTemplate">
<Button>Custom Trigger</Button>
</property>
</ComponentName>
// ❌ INCORRECT
<ComponentName>
<triggerTemplate>
<Button>Custom Trigger</Button>
</triggerTemplate>
</ComponentName>
```
## Mandatory Test Structure
Use `test.describe("category name")` to group test cases.
Don't need a top level group that encompuses every test case.
```typescript
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test.describe("Basic Functionality", () => {
test("component renders with basic props", async ({ initTestBed, createComponentDriver }) => {
// Test implementation
});
});
// =============================================================================
// ACCESSIBILITY TESTS
// =============================================================================
test.describe("Accessibility", () => {
test("component has correct accessibility attributes", async ({ initTestBed, page }) => {
// Test implementation
});
});
// =============================================================================
// THEME VARIABLE TESTS (Only for components that support theme variables)
// =============================================================================
test.describe("Theme Variables", () => {
test("component applies theme variables", async ({ initTestBed, createComponentDriver }) => {
// Test implementation
});
});
// =============================================================================
// OTHER EDGE CASE TESTS
// =============================================================================
test.describe("Other Edge Cases", () => {
test("component handles null props gracefully", async ({ initTestBed }) => {
// Test implementation
});
});
```
## Testing Framework
### Test Initialization
**Always use XMLUI test function from `fixtures.ts`:**
```typescript
import { test, expect } from "../../testing/fixtures";
test("component renders correctly", async ({ initTestBed }) => {
await initTestBed(`<ComponentName prop="value"/>`, {});
// This will create a webpage where the whole content of it is the top level xmlui component,
// specified in the initTestBed's first parameter as source code.
// Test implementation
});
```
### initTestBed Usage
```typescript
// Basic usage
await initTestBed(`<ComponentName prop="value"/>`, {});
// With theme variables
await initTestBed(`<ComponentName/>`, {
testThemeVars: { "backgroundColor-ComponentName": "rgb(255, 0, 0)" },
});
// With test state for events
const { testStateDriver } = await initTestBed(`
<ComponentName onClick="testState = 'clicked'"/>
`);
```
## Testing Approaches
### Obtaining a locator (element)
Locate elements in a way a user would locate them guided by their intention. For example `const checkboxLocator = await page.getByRole('checkbox')`, rather than relying on internal structure like `page.locator("input").nth(2)`. If and only if there are more elements of the same type and it does not make sense to use text (because the test is not about labels or content text, or because the layout would change due to the presence of the content), prefer to use `testId`-s in the source code and use those test ids to locate the elements. You should aviod using `page.locator()` in test cases.
If it is possible to test something at the time of locationg the element, prefer that. `page.getByRole` is the prime example of this, because it also tests for accessibility and intent (the user is not looking for a div with a certain class on it, they are looking for an image or a button). Avoid long selectors, that could be more readable by adding some assertions. For example, this is better:
```ts
const cb = page.getByRole("checkbox");
await expect(cb).toBeDisabled();
```
and this is worse:
```ts
const cb = page.getByRole("checkbox", { disabled: true });
await expect(cb).toBeVisible();
```
However, if there are multiple elements of the same type and an option could differentiate them and assertions would need to be written out after locating the element anyway, this approach is better than using test ids. For example, this is a good use of the `{disabled: true}` option:
```ts
await initTestBed(`
<Fragment>
<Checkbox enabled="false"/>
<Checkbox />
</Fragment>`);
const cb = page.getByRole("checkbox", { disabled: true });
// do something with the disabled checkbox
```
### Event testing
Sometimes you need to test events. The easiest way is to do something like this:
Don't worry about how the testState is actually obtained. Just know that inside an event handler, you can write javascript and access the `testState` variable, which you can make assertions against later by polling that value. Just make it obvious that testState is actually changed, so use an obvious name like 'changed', or if you need to test the handler multiple times, use a counter like `onDidChange="testState = testState == null ? 1 : testState + 1"` Test state is initialized to be null.
```typescript
test("click event fires on click", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`<Button onClick="testState = 'clicked'"/>`);
await page.getByRole("button").click();
await expect.poll(testStateDriver.testState).toEqual("clicked");
});
```
**Testing API Returns vs Component State**: For non-visual components, focus on testing API return values rather than internal component state, as the latter may not behave as expected without proper event handlers:
```typescript
// ✅ CORRECT - Test API return values
test("enqueueItem returns valid ID", async ({ initTestBed, createButtonDriver }) => {
const { testStateDriver } = await initTestBed(`
<Fragment>
<Queue id="testQueue" />
<Button onClick="testState = { id: testQueue.enqueueItem('test'), hasId: true }" />
</Fragment>
`);
const buttonDriver = await createButtonDriver("button");
await buttonDriver.component.click();
const result = await testStateDriver.testState();
expect(result.id).toBeTruthy();
expect(result.hasId).toBe(true);
});
// ❌ POTENTIALLY INCORRECT - Component state may not persist without handlers
test("queue length increases", async ({ initTestBed, createButtonDriver }) => {
// Without onProcess handler, queue might not retain items
const { testStateDriver } = await initTestBed(`
<Fragment>
<Queue id="testQueue" />
<Button onClick="testQueue.enqueueItem('test'); testState = testQueue.getQueueLength()" />
</Fragment>
`);
// This test might fail if Queue doesn't retain items without processing
});
```
### Writing actions and drivers
Not all tests need actions. Some are just instantiating the webpage with a given component and then making assertions on that.
Actions are the most varried part of writing component tests. If and only if the action is super simple and consists of one step, and the action's name gives clarity on what it is doing, should you use actions directly on the locators. For example, this is good, because it is clear what the actions are doing, as the `check` and `uncheck` actions are complete in themselves:
```ts
const cb = page.getByRole("checkbox");
await cb.check();
await expect(cb).toBeChecked();
await cb.uncheck();
await expect(cb).not.toBeChecked();
```
However, in any other case, you should use a component driver to encapsulate the logic of the action. For example:
```ts
test("search filters option labels", async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select searchable="true">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</FormItem>
`);
const select = page.getByRole("combobox");
const driver = await createSelectDriver(select);
await driver.searchFor("second");
await driver.chooseFirstOption();
await expect(select).toHaveValue("opt2");
});
```
In this case, if the `searchFor` method were to be substituted with clicking on the locator and typing into the input field the label text, that would not convey the intent of the action, which is searching. Actions should have componenet-specific names. For example, a Dropdown can have a `toggleDropdownVisibility` method, which is pretty much just a click, but with a more meaningful name. You would not call it `click`.
Drivers are derived from a base ComponentDriver class. Not every component needs a driver, so don't just create one for each component. In fact, if you can avoid it, you should, as they are a layer of abstraction.
### Drivers for internal component structure
In extremely rare cases, you need to access the DOM structure of the component. This is usually a sign that the component is not well designed, and lacking accessibility features. In these cases, if you are an AI express the best approach that has accessability baked in. For example, you might be asked to test a spinner's border. You cound use the internal structure of the top level component, but it's better to encourage the programmer to implement role="status" on the component, so you can use `page.getByRole("status")`.
In some RARE cases, they are the right approach though.
For example, if the component is purely visual and has no accessibility attributes, or we actually want to assert something about a particular DOM element (like an internal element's border has a certain width): you can use the driver to obtain the locator, but only for that.
Drivers should NOT:
- have expectations and assertions in them. Those belong to the test case. Instead, return the locator from a method (in case driver is used for obtaining internal structure) and assert against it in the test case.
- reimplement the playwright API, such as having a `click` or `hover` method which just wraps the playwright API and executes it on one of it's internal element'.
### Writing assertions
Use web-first assertions, like `await expect(checkboxLocator).not.toBeChecked()`. If there is a built-in assertion, prefer that over an assertion checking against a the dom attributes. This is an antipattern: `await expect(checkboxLocator).toHaveAttribute("type", "checkbox");` because it relies on the element being an input element.
## Test Naming & Patterns
### Naming Standards
- Avoid using the "component" word in names, it's redundant
- Use concrete property, event, api, attribute, etc.
- ✅ `"renders with 'variant' property"`
- ✅ `"has correct 'aria-clickable'"`
- ✅ `"handles null and undefined 'variant' property"`
- ❌ `"test component"` or `"basic test"`
## Best Practices
### Avoid Frontend Code in E2E Tests
It is crucial to avoid importing frontend code into E2E tests, especially if it transitively imports stylesheets. This can lead to unexpected issues with the test runner and slow down test execution.
For example, importing `defaultProps` from a component file like `ButtonNative.tsx` into a test file like `Button.spec.ts` is an anti-pattern. The component file likely imports SCSS or CSS files, which should not be part of the test environment.
Instead, if you need to share data between your component and your tests, define it in a separate file that can be imported safely by both.
### Skipping tests
#### Skipping For coverage
Use `test.skip` for comprehensive coverage, when you know which tests you want to write, but you haven't gotten around to implementing them yet.
```typescript
test.skip("placeholder defaults to 'example.com'", async ({ initTestBed, page }) => {});
```
#### Skipping for any other reason
There are other reasons to skip tests, such as when a feature is not yet implemented or when a bug is present.
After becoming certain the test is well written, skip the test with the appropriate skip reason.
```typescript
test.fixme(
`label displayed for type 'autocomplete'`,
SKIP_REASON.XMLUI_BUG(
"There are two labels in Autocomplete: one is ours, the other comes from the wrapped component -> this results in an error",
),
async ({ initTestBed, page }) => {
await initTestBed(`<Autocomplete label="test" />`);
await expect(page.getByLabel("test"));
// ...rest of the test is not relevant
},
);
```
### Systematic Testing
In case there would be a lot of duplication for testing a property that has the exact same structure, use parameterized tests.
```typescript
// Data type testing
[
{ label: "null", value: "'{null}'", expected: "" },
{ label: "string", value: "'test'", expected: "test" },
{ label: "integer", value: "'{123}'", expected: "123" },
// more cases...
].forEach(({ label, value, expected }) => {
test(`handles ${label} correctly`, async ({ initTestBed, page }) => {
await initTestBed(`<ComponentName value=${value} testId="component"/>`);
await expect(page.getByTestId("component")).toHaveText(expected);
});
});
```
## Good test case patterns
### Layout/Positioning Tests
Components that support layout properties (like `labelPosition`, `direction`, positioning, sizing) should include tests that verify visual arrangement using the `getBounds` utility function.
#### Best Practices for Layout Testing
- **Import getBounds**: Import from `"../../testing/component-test-helpers"`
- **Use descriptive coordinates**: Destructure specific properties like `{ left, right, top, bottom }`
- **Test both directions**: Include RTL tests when direction affects layout
- **Verify invalid values**: Test graceful handling of invalid layout properties
#### Testing Element Positioning
Use `getBounds()` to get element coordinates and verify relative positioning:
```typescript
test("ComponentName appears at the correct side of ComponentName2", async ({
initTestBed,
page,
}) => {
await initTestBed(`
<Fragment>
<ComponentName testId="comp1" />
<ComponentName2 testId="comp2" />
<Fragment>
`);
const { left: comp1Left } = await getBounds(page.getByTestId("comp1"));
const { right: comp2Right } = await getBounds(page.getByTestId("comp2"));
expect(comp1Left).toBeLessThan(comp2Right);
});
```
#### Testing Directional Layout (RTL/LTR)
Test layout behavior in both directions when applicable:
```typescript
test("startText displays at beginning of input (rtl)", async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="input" direction="rtl" startText="$" />`);
const { left: compLeft, right: compRight } = await getBounds(page.getByTestId("input"));
const { left: textLeft, right: textRight } = await getBounds(page.getByText("$"));
await expect(page.getByTestId("input")).toContainText("$");
expect(textRight - compLeft).toBeGreaterThanOrEqual(compRight - textLeft);
});
```
#### Testing Size Properties
Verify width, height, and other sizing properties:
```typescript
test("labelWidth applies custom label width", async ({ initTestBed, page }) => {
const expected = 200;
await initTestBed(`<InputComponent label="test test" labelWidth="${expected}px" />`);
const { width } = await getBounds(page.getByText("test test"));
expect(width).toEqual(expected);
});
```
#### Testing Complex Layout Arrangements
For components with multiple positioned elements, test their relative arrangement:
```typescript
test("all adornments appear in the right place", async ({ initTestBed, page }) => {
await initTestBed(`
<TextBox testId="input" startText="$" endText="USD" startIcon="search" endIcon="search" direction="ltr" />
`);
const { left: compLeft, right: compRight } = await getBounds(page.getByTestId("input"));
const { left: startTextLeft, right: startTextRight } = await getBounds(page.getByText("$"));
const { left: endTextLeft, right: endTextRight } = await getBounds(page.getByText("USD"));
const { left: startIconLeft, right: startIconRight } = await getBounds(
page.getByRole("img").first(),
);
const { left: endIconLeft, right: endIconRight } = await getBounds(page.getByRole("img").last());
// Check order of adornments relative to their container component bounds
expect(startTextRight - compLeft).toBeLessThanOrEqual(compRight - startTextLeft);
expect(startIconRight - compLeft).toBeLessThanOrEqual(compRight - startIconLeft);
expect(endTextRight - compLeft).toBeGreaterThanOrEqual(compRight - endTextLeft);
expect(endIconRight - compLeft).toBeGreaterThanOrEqual(compRight - endIconLeft);
});
```
### Testing Input Component API
Test the following in a `test.describe("Api", () => {...})` block for input components (such as TextBox, Checkbox, Slider, etc.):
- value
- setValue
- focus
#### Example
```typescript
test("component setValue API updates state", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<TextBox id="myTextBox" />
<Button testId="setBtn" onClick="myTextBox.setValue('api value')" />
</Fragment>
`);
await page.getByTestId("setBtn").click();
await expect(page.getByRole("textbox")).toHaveValue("api value");
});
```
## Bad test case patterns
fill in later
```
--------------------------------------------------------------------------------
/docs/public/pages/howto.md:
--------------------------------------------------------------------------------
```markdown
# How To
These examples answer common questions of the form "How do I do SOMETHING with XMLUI?" The [XMLUI MCP server](https://github.com/xmlui-org/xmlui-mcp) provides two related tools. Agents can call `xmlui-list-howto` to list the entries here and `xmlui-search-howto` to search them.
## Expose a method from a component
```xmlui-pg
---app display
<App height="300px" >
<UsingInternalModal id="component"/>
<Button label="Open the internal dialog" onClick="component.openDialog()" />
</App>
---comp display
<Component name="UsingInternalModal">
<ModalDialog id="dialog" title="Example Dialog">
<Button label="Close Dialog" onClick="dialog.close()" />
</ModalDialog>
<H1>Using an Internal Modal Dialog</H1>
<method name="openDialog">
console.log('internal method called')
dialog.open();
</method>
</Component>
```
## React to button click not keystrokes
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---comp display
<!-- Use two different variables -->
<Component name="Test" var.searchText="" var.triggerSearch="{false}">
<TextBox
id="searchInput"
placeholder="Type something..."
width="300px"
/>
<Button
label="Search"
onClick="searchText = searchInput.value; triggerSearch = true"
/>
<DataSource
id="searchResults"
url="https://httpbin.org/post"
body="{JSON.stringify({query: searchText})}"
method="POST"
when="{triggerSearch}"
onDidLoad="triggerSearch = false"
/>
<Fragment when="{searchResults.loaded}">
<Text>Search results for: {searchText}</Text>
<Text>Response received: {searchResults.value.json ? 'Yes' : 'No'}</Text>
</Fragment>
</Component>
```
## Modify a value reported in a Column
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---comp display
<Component name="Test">
<DataSource
id="invoices_with_badges"
url="/resources/files/invoices.json"
transformResult="{data => data.slice(0,5)}"
/>
<Table data="{invoices_with_badges}">
<Column bindTo="invoice_number" /> <!-- empty tag for bound column -->
<Column bindTo="client" />
<Column bindTo="issue_date" />
<Column bindTo="due_date" />
<Column bindTo="paid_date" />
<Column>
${$item.total} <!-- unbound column, prepend $ to the $item value -->
</Column>
<Column>
<StatusBadge status="{$item.status}" /> <!-- embed component, pass value -->
</Column>
</Table>
</Component>
---comp display
<Component
name="StatusBadge"
var.statusColors="{{
draft: { background: '#f59e0b', label: 'white' },
sent: { background: '#3b82f6', label: 'white' },
paid: { background: '#10b981', label: 'white' }
}}"
>
<Badge
value="{$props.status}"
colorMap="{statusColors}"
variant="pill"
/>
</Component>
```
## Filter and transform data from an API
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.people = [
{ id: 1, name: 'Alice', active: true, group: 'A' },
{ id: 2, name: 'Bob', active: false, group: 'B' },
{ id: 3, name: 'Carol', active: true, group: 'A' },
{ id: 4, name: 'Dave', active: true, group: 'B' }
]",
"operations": {
"get-people": {
"url": "/people",
"method": "get",
"handler": "return { status: 'ok', data: { items: $state.people } }"
}
}
}
---comp display
<Component name="Test">
<!--
{
items:
[
{ id: 1, name: 'Alice', active: true, group: 'A' },
{ id: 2, name: 'Bob', active: false, group: 'B' },
{ id: 3, name: 'Carol', active: true, group: 'A' },
{ id: 4, name: 'Dave', active: true, group: 'B' }
]
}
-->
<!-- Use resultSelector to select the items array -->
<DataSource
id="allPeople"
url="/api/people"
resultSelector="data.items"
/>
<!-- Use resultSelector to filter the items array -->
<DataSource
id="activePeople"
url="/api/people"
resultSelector="data.items.filter(p => p.active)"
/>
<!-- Use transformResult -->
<!--
window.transformPeople = function(data) {
console.log(data);
const items = data.data.items;
const itemMap = {
A: 'Austin',
B: 'Boston'
};
return items.map(item => ({
...item,
city: itemMap[item.group]
}));
};
-->
<DataSource
id="transformedPeople"
url="/api/people"
transformResult="{window.transformPeople}"
/>
<Text>All people:</Text>
<List data="{allPeople}">
<Text>{$item.name} ({$item.group})</Text>
</List>
<Text>Active people:</Text>
<List data="{activePeople}">
<Text>{$item.name} ({$item.group})</Text>
</List>
<Text>Transformed people:</Text>
<List data="{transformedPeople}">
<Text>{$item.name} ({$item.city})</Text>
</List>
</Component>
```
## Group items in List by a property
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api display
{
"apiUrl": "/api",
"initialize": "$state.people_groupby = [
{ id: 1, name: 'Alice', active: true, group: 'A' },
{ id: 2, name: 'Bob', active: false, group: 'B' },
{ id: 3, name: 'Carol', active: true, group: 'A' },
{ id: 4, name: 'Dave', active: true, group: 'B' }
]",
"operations": {
"get-people-groupby": {
"url": "/people_groupby",
"method": "get",
"handler": "return { status: 'ok', data: { items: $state.people_groupby } }"
}
}
}
---comp display
<Component name="Test">
<!--
{
items:
[
{ id: 1, name: 'Alice', active: true, group: 'A' },
{ id: 2, name: 'Bob', active: false, group: 'B' },
{ id: 3, name: 'Carol', active: true, group: 'A' },
{ id: 4, name: 'Dave', active: true, group: 'B' }
]
}
-->
<DataSource
id="allPeopleGroupBy"
url="/api/people_groupby"
resultSelector="data.items"
/>
<List data="{allPeopleGroupBy}" groupBy="group">
<property name="groupHeaderTemplate">
<Text variant="subtitle">Group {$group.key}</Text>
</property>
<Text>{$item.name}</Text>
</List>
</Component>
```
## Delay a DataSource until another DataSource is ready
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.users_for_ds_dependency =
[
{ id: 1, name: 'Alice', departmentId: 1 },
{ id: 2, name: 'Bob', departmentId: 2 }
];
$state.departments_with_ds_dependency = [
{ id: 1, name: 'Engineering' },
{ id: 2, name: 'Marketing' }
]",
"operations": {
"get_users_for_ds_dependency": {
"url": "/users_for_ds_dependency",
"method": "get",
"handler": "delay(1000); return $state.users_for_ds_dependency"
},
"get_departments_with_ds_dependency": {
"url": "/departments_with_ds_dependency",
"method": "get",
"handler": "delay(1000); return $state.departments_with_ds_dependency"
}
}
}
---comp display
<Component name="Test" var.selectedId="" var.nonce="{0}">
<DataSource
id="users_for_ds_dependency"
url="/api/users_for_ds_dependency?nonce"
inProgressNotificationMessage="Loading users..."
when="{ nonce > 0 }"
/>
<DataSource
id="departments_with_ds_dependency"
url="/api/departments_with_ds_dependency"
when="{ users_for_ds_dependency.loaded }"
inProgressNotificationMessage="Loading departments..."
/>
<Select
id="usersForDsDepencency"
data="{users_for_ds_dependency}"
when="{departments_with_ds_dependency.loaded}"
onDidChange="(newVal) => selectedId = newVal"
>
<Items data="{users_for_ds_dependency}">
<Option
value="{$item.id}"
label="{$item.name} ({departments_with_ds_dependency.value.find(d => d.id === $item.departmentId)?.name})"
/>
</Items>
</Select>
<Button
label="Run"
onClick="{nonce++}"
/>
</Component>
```
## Hide an element until its DataSource is ready
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.fruits = [
{ id: 1, name: 'Orange' },
{ id: 2, name: 'Apple' },
{ id: 3, name: 'Pear' },
]",
"operations": {
"get-fruits": {
"url": "/fruits",
"method": "get",
"handler": "delay(3000); return $state.fruits;"
}
}
}
---comp display
<Component name="Test" var.nonce="{0}">
<DataSource
id="fruits"
url="/api/fruits?{nonce}"
inProgressNotificationMessage="Loading fruits"
when="{nonce > 0}"
/>
<Button
label="Run"
onClick="{nonce++}"
/>
<Fragment when="{fruits.loaded}">
<Text>Fruits: {fruits.value.length} found</Text>
</Fragment>
</Component>
```
## Use built-in form validation
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{}
---comp display
<Component name="Test">
<Form
data="{{ password: '' }}"
onSubmit="(data) => console.log('Submitted:', data)"
>
<FormItem
label="Password"
bindTo="password"
type="password"
minLength="8"
lengthInvalidMessage="Password must be at least 8 characters"
/>
</Form>
</Component>
```
## Do custom form validation
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{}
---comp display
<Component name="Test" var.limit="{100}">
<Form
data="{{ spending: 0 }}"
onSubmit="(data) => console.log('Submitted:', data)"
>
<FormItem
label="Requested Amount (limit {limit})"
bindTo="total"
type="integer"
onValidate="{ (value) => value > 0 && value <= limit }"
/>
</Form>
</Component>
```
## Assign a complex JSON literal to a component variable
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{}
---comp display
<Component name="Test"
<!-- double curly braces inside double quote -->
var.appConfig="{{
name: 'Photo Gallery',
version: '1.2.0',
isPublic: true,
photos: [
{ id: 1, title: 'Sunset Beach', likes: 42 },
{ id: 2, title: 'Mountain View', likes: 38 },
{ id: 3, title: 'City Lights', likes: 55 }
],
authors: [
{ name: 'Alice Johnson', role: 'Photographer' },
{ name: 'Bob Smith', role: 'Editor' }
]
}}">
<!-- double curly braces inside double quote -->
<Text>{appConfig.name} v{appConfig.version}</Text>
<Text>Photos ({appConfig.photos.length})</Text>
<Items data="{appConfig.photos}">
<Text>{$item.title} - {$item.likes} likes</Text>
</Items>
<Text>Team</Text>
<Items data="{appConfig.authors}">
<Text>{$item.name} ({$item.role})</Text>
</Items>
</Component>
```
## Make a set of equal-width cards
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.dashboard_stats = {
\"draft_invoices\":6,
\"outstanding\":3502.9,
\"paid_invoices\":79,
\"paid_this_year\":1745.18,
\"sent_invoices\":43,
\"total_clients\":30,
\"total_invoices\":91
}",
"operations": {
"get-dashboard-stats": {
"url": "/dashboard_stats",
"method": "get",
"handler": "$state.dashboard_stats"
}
}
}
---comp display
<Component name="Test" >
<DataSource id="dashboard_stats" url="/api/dashboard_stats" method="GET" />
<FlowLayout>
<InfoCard
width="*" <!-- use star sizing -->
title="Outstanding"
value="{ dashboard_stats.value.outstanding }"
/>
<InfoCard
width="*"
title="Paid This Year"
value="{ dashboard_stats.value.paid_this_year }"
/>
<InfoCard
width="*"
title="Draft"
value="{ dashboard_stats.value.draft_invoices }"
/>
<InfoCard
width="*"
title="Sent"
value="{ dashboard_stats.value.sent_invoices }"
/>
</FlowLayout>
</Component>
---comp display
<Component name="InfoCard">
<Card width="{$props.width}" borderRadius="8px" boxShadow="$boxShadow-spread">
<Text>{$props.title}</Text>
<Text fontWeight="$fontWeight-extra-bold" fontSize="larger">
{ $props.value }
</Text>
</Card>
</Component>
```
## Set the initial value of a Select from fetched data
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.users_initial_value = [
{
id: 1,
username: 'Coder Gal',
},
{
id: 2,
username: 'Tech Ninja',
},
{
id: 3,
username: 'Design Diva',
},
]",
"operations": {
"get_users_initial_value": {
"url": "/users_initial_value",
"method": "get",
"handler": "$state.users_initial_value"
}
}
}
---comp display
<Component name="Test" var.selectedValue="">
<DataSource
id="myData"
url="/api/users_initial_value"
onLoaded="(data) => { selectedValue = data[0].id }"
/>
<Select initialValue="{selectedValue}">
<Items data="{myData}">
<Option value="{$item.id}" label="{$item.username}" />
</Items>
</Select>
</Component>
```
## Pass data to a Modal Dialog
```xmlui-pg name="Click on a team member to edit details"
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.team_members = [
{ id: 1, name: 'Sarah Chen', role: 'Product Manager', email: '[email protected]', avatar: 'https://i.pravatar.cc/100?u=sarah', department: 'Product', startDate: '2022-03-15' },
{ id: 2, name: 'Marcus Johnson', role: 'Senior Developer', email: '[email protected]', avatar: 'https://i.pravatar.cc/100?u=marcus', department: 'Engineering', startDate: '2021-08-20' },
{ id: 3, name: 'Elena Rodriguez', role: 'UX Designer', email: '[email protected]', avatar: 'https://i.pravatar.cc/100?u=elena', department: 'Design', startDate: '2023-01-10' }
]",
"operations": {
"get_team_members": {
"url": "/team_members",
"method": "get",
"handler": "return $state.team_members"
}
}
}
---comp display
<Component name="Test">
<DataSource
id="team_members"
url="/api/team_members"
/>
<ModalDialog id="memberDetailsDialog" title="Team Member Details">
<Theme backgroundColor-overlay="$color-surface-900">
<VStack gap="1rem" padding="1rem">
<!-- Avatar and Basic Info -->
<HStack gap="1rem" alignItems="center">
<Avatar
url="{$param.avatar}"
size="lg"
name="{$param.name}"
/>
<VStack gap="0.25rem" alignItems="start">
<Text variant="strong" fontSize="1.2rem">{$param.name}</Text>
<Text variant="caption">{$param.role}</Text>
<Text variant="caption" color="blue">{$param.email}</Text>
</VStack>
</HStack>
<!-- Details Card -->
<Card padding="1rem">
<VStack gap="0.5rem">
<HStack>
<Text variant="strong">Department:</Text>
<Text>{$param.department}</Text>
</HStack>
<HStack>
<Text variant="strong">Start Date:</Text>
<Text>{$param.startDate}</Text>
</HStack>
<HStack>
<Text variant="strong">Employee ID:</Text>
<Text>#{$param.id}</Text>
</HStack>
</VStack>
</Card>
<!-- Actions -->
<HStack gap="0.5rem">
<Button
label="Send Email"
size="sm"
onClick="console.log('Email to:', $param.email)"
/>
<Button
label="View Calendar"
size="sm"
variant="secondary"
onClick="console.log('Calendar for:', $param.name)"
/>
</HStack>
</VStack>
</Theme>
</ModalDialog>
<Text variant="strong" marginBottom="1rem">Team Directory</Text>
<VStack gap="0.5rem">
<Items data="{team_members}">
<Card
padding="1rem"
cursor="pointer"
onClick="{
memberDetailsDialog.open({
id: $item.id,
name: $item.name,
role: $item.role,
email: $item.email,
avatar: $item.avatar,
department: $item.department,
startDate: $item.startDate
})
}"
>
<HStack gap="1rem" alignItems="center">
<Avatar
url="{$item.avatar}"
size="sm"
name="{$item.name}"
/>
<VStack gap="0.25rem" alignItems="start">
<Text variant="strong">{$item.name}</Text>
<Text variant="caption">{$item.role} - {$item.department}</Text>
</VStack>
</HStack>
</Card>
</Items>
</VStack>
</Component>
```
## Debug a component
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.user_data = {
id: 42,
name: 'John Doe',
preferences: { theme: 'dark', notifications: true },
recentItems: ['item1', 'item2', 'item3']
}",
"operations": {
"get_user_data": {
"url": "/user_data",
"method": "get",
"handler": "console.log('API called:', $state.user_data); return $state.user_data"
}
}
}
---comp display
<Component name="Test"
var.localState="{{
currentStep: 2,
errors: ['Invalid email', 'Password too short'],
formData: { email: '[email protected]', age: 25 }
}}">
<DataSource
id="userData"
url="/api/user_data"
/>
<Text>User Debug Info</Text>
<!-- Method 1: JSON.stringify with preserveLinebreaks -->
<Text preserveLinebreaks="true">
{JSON.stringify(userData.value, null, 2)}
</Text>
<!-- Method 2: Console.log in handler -->
<Button
label="Log to Console"
onClick="console.log('Button clicked, userData:', userData.value)"
/>
<!-- Method 3: Window function for component variables -->
<Button
label="Debug Local State"
onClick="window.debugLog(localState, 'Local component state')"
/>
<!-- Method 4: Unwrapping Proxy objects -->
<Button
label="Debug Unwrapped Data"
onClick="console.log('Unwrapped userData:', JSON.parse(JSON.stringify(userData.value)))"
/>
</Component>
```


## Share a ModalDialog across components
```xmlui-pg noHeader
---app
<App>
<Test />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.items = [
{ id: 1, title: 'Mountain View' },
{ id: 2, title: 'City Lights' },
{ id: 3, title: 'Ocean Sunset' }
]",
"operations": {
"get-items": {
"url": "/items",
"method": "get",
"handler": "return $state.items"
}
}
}
---comp display
<Component name="Test">
<AppState id="settings" bucket="appSettings" initialValue="{{
itemSize: 'medium',
showDetails: true
}}" />
<!-- Settings modal defined at App level - accessible to all components -->
<ModalDialog id="settingsDialog" title="Settings">
<SettingsPanel />
</ModalDialog>
<DataSource id="items" url="/api/items" />
<AppHeader title="Demo App">
<property name="profileMenuTemplate">
<Icon name="cog" onClick="settingsDialog.open()" />
</property>
</AppHeader>
<VStack gap="1rem">
<HStack gap="1rem">
<Text>Items ({settings.value.itemSize} size)</Text>
<Button
label="Settings"
size="sm"
onClick="settingsDialog.open()"
/>
</HStack>
<List data="{items}">
<Card>
<VStack>
<Text>{$item.title}</Text>
<Fragment when="{settings.value.showDetails}">
<Text variant="caption">ID: {$item.id}</Text>
</Fragment>
</VStack>
</Card>
</List>
</VStack>
</Component>
---comp display
<Component name="SettingsPanel">
<AppState id="settings" bucket="appSettings" />
<VStack gap="1rem">
<Select
label="Item Size"
initialValue="{settings.value.itemSize}"
onDidChange="(value) => settings.update({ itemSize: value })"
>
<Option value="small" label="Small" />
<Option value="medium" label="Medium" />
<Option value="large" label="Large" />
</Select>
<Switch
label="Show details"
initialValue="{settings.value.showDetails}"
onDidChange="(value) => settings.update({ showDetails: value })"
/>
</VStack>
</Component>
```
## Use the same ModalDialog to add or edit
See also the [refactoring](/refactoring) guide. Briefly: props flow down, events flow up.
```xmlui-pg noHeader height="400px"
---app
<App>
<Test />
</App>
---comp display
<Component name="Test" var.editingProductId="{null}" var.showAddModal="{false}">
<DataSource id="products" url="/api/products" />
<HStack alignItems="center">
<Text variant="strong" fontSize="$fontSize-2xl">Product Inventory</Text>
<SpaceFiller />
<Button
label="Add New Product"
size="sm"
onClick="showAddModal = true"
/>
</HStack>
<Table data="{products}">
<Column bindTo="name" />
<Column bindTo="price" width="120px"/>
<Column header="Actions" width="240px">
<HStack>
<Button label="Edit" icon="pencil" size="sm" variant="outlined"
onClick="editingProductId = $item.id"
/>
<Button label="Delete" icon="trash" size="sm" variant="outlined"
themeColor="attention">
<event name="click">
<APICall
method="delete"
url="/api/products/{$item.id}"
confirmMessage="Are you sure you want to delete '{$item.name}'?" />
</event>
</Button>
</HStack>
</Column>
</Table>
<ProductModal
when="{showAddModal}"
mode="add"
onClose="showAddModal = false"
onSaved="products.refetch()"
/>
<ProductModal
when="{editingProductId}"
mode="edit"
productId="{editingProductId}"
onClose="editingProductId = null"
onSaved="products.refetch()"
/>
</Component>
---comp display
<Component name="ProductModal">
<DataSource
id="productDetails"
url="/api/products/{$props.productId}"
when="{$props.mode === 'edit' && $props.productId}"
/>
<ModalDialog
title="{$props.mode === 'edit' ? 'Edit Product' : 'Add Product'}"
when="{$props.mode === 'add' || productDetails.loaded}"
onClose="emitEvent('close')"
>
<Form
data="{$props.mode === 'edit' ? productDetails.value : {}}"
submitUrl="{$props.mode === 'edit' ? '/api/products/' + $props.productId : '/api/products'}"
submitMethod="{$props.mode === 'edit' ? 'put' : 'post'}"
onSuccess="emitEvent('saved')"
>
<FormItem bindTo="name" label="Product Name" required="true" />
<FormItem bindTo="price" label="Price" type="number" required="true" />
</Form>
</ModalDialog>
</Component>
---api
{
"apiUrl": "/api",
"initialize": "$state.products = [
{ id: 1, name: 'Laptop Pro', price: 1299 },
{ id: 2, name: 'Wireless Mouse', price: 29 }
]",
"operations": {
"get-products": {
"url": "/products",
"method": "get",
"handler": "$state.products"
},
"get-product": {
"url": "/products/:id",
"method": "get",
"pathParamTypes": {
"id": "integer"
},
"handler": "return $state.products.find(p => p.id === $pathParams.id)"
},
"insert-product": {
"url": "/products",
"method": "post",
"handler": "
const newId = $state.products.length > 0 ? Math.max(...$state.products.map(p => p.id)) + 1 : 1;
$state.products.push({
id: newId,
name: $requestBody.name,
price: Number($requestBody.price)
});
"
},
"update-product": {
"url": "/products/:id",
"method": "put",
"pathParamTypes": {
"id": "integer"
},
"handler": "
const oldItem = $state.products.find(p => p.id === $pathParams.id);
if (oldItem) {
oldItem.name = $requestBody.name;
oldItem.price = Number($requestBody.price);
}
"
},
"delete-product": {
"url": "/products/:id",
"method": "delete",
"pathParamTypes": {
"id": "integer"
},
"handler": "$state.products = $state.products.filter(p => p.id !== $pathParams.id)"
}
}
}
```
## Paginate a List
XMLUI provides a `Pagination` component that can be used to display visual controls for the pagination feature, no matter whether it is handled inside or outside of a layout component requiring that feature.
The [`Table`](./table) component provides out-of-the-box support for pagination,
so you can access pagination options via the following properties: `isPaginated`, `pageSize`, `pageSizeOptions`, `paginationControlsLocation`.
```xmlui noHeader copy
<Table
data="/api/endpoint"
isPaginated
pageSize="10"
pageSizeOptions="{[5, 10, 20, 30]}"
paginationControlsLocation="both"
>
...
</Table>
```
Other components, such as the `List`, can be hooked up with pagination using a `DataSource` combined with the `Pagination` component. This pattern works as a more generic solution where either the component does not have pagination implemented in the component itself, or you wish to use custom pagination logic.
In this case the `DataSource` component does the heavy lifting by querying the page index, the previous and next page IDs. This can be done using variables and query parameters.
```xmlui-pg
---app display
<App
var.pageSize="{5}"
var.currentPage="{0}"
var.before="{0}"
var.after="{pageSize-1}"
>
<DataSource
id="pagination_ds"
url="/api/pagination_items/{before}/{after}"
/>
<Text>
Page {currentPage + 1}, showing items {before + 1}-{after + 1}
</Text>
<Pagination
id="pagination"
itemCount="20"
pageSize="{pageSize}"
pageIndex="{currentPage}"
onPageDidChange="(page, size, total) => {
currentPage = page;
before = page * size;
after = before + size - 1;
pagination_ds.refetch();
}"
onPageSizeDidChange="(size) => {
pageSize = size;
before = currentPage * size;
after = before + size - 1;
pagination_ds.refetch();
}"
/>
<List data="{pagination_ds}" />
</App>
---api
{
"apiUrl": "/api",
"initialize": "$state.pagination_items = [
{ id: 1, name: 'Laptop Pro', price: 1299 },
{ id: 2, name: 'Wireless Mouse', price: 29 },
{ id: 3, name: 'Mechanical Keyboard', price: 149 },
{ id: 4, name: '4K Monitor', price: 399 },
{ id: 5, name: 'USB-C Hub', price: 79 },
{ id: 6, name: 'Bluetooth Headphones', price: 199 },
{ id: 7, name: 'Webcam HD', price: 89 },
{ id: 8, name: 'Standing Desk', price: 299 },
{ id: 9, name: 'Ergonomic Chair', price: 249 },
{ id: 10, name: 'Desk Lamp', price: 45 },
{ id: 11, name: 'Cable Organizer', price: 15 },
{ id: 12, name: 'Mouse Pad', price: 12 },
{ id: 13, name: 'Laptop Stand', price: 35 },
{ id: 14, name: 'External SSD', price: 129 },
{ id: 15, name: 'Wireless Charger', price: 59 },
{ id: 16, name: 'Smart Speaker', price: 99 },
{ id: 17, name: 'Fitness Tracker', price: 199 },
{ id: 18, name: 'Tablet Pro', price: 799 },
{ id: 19, name: 'Gaming Mouse', price: 89 },
{ id: 20, name: 'Noise Cancelling Headphones', price: 349 }
]",
"operations": {
"get-pagination-items": {
"url": "/pagination_items/:from/:to",
"method": "get",
"pathParamTypes": {
"from": "integer",
"to": "integer"
},
"handler": "$state.pagination_items.slice($pathParams.from, $pathParams.to + 1)"
}
}
}
```
```