This is page 88 of 143. Use http://codebase.md/xmlui-org/xmlui/tools/vscode/resources/%7Bsrc%7D?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── cyan-tools-design.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
│ │ │ ├── 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
│ │ ├── FancyButton.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
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.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/src/components/Slider/Slider.spec.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Testing Notes: the Driver needs to account for the correct positioning of the indicators on the slider
*/
import { validationStatusValues } from "../abstractions";
import { getBounds, SKIP_REASON } from "../../testing/component-test-helpers";
import { expect, test } from "../../testing/fixtures";
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test.describe("Basic Functionality", () => {
test("component renders", async ({ initTestBed, page }) => {
await initTestBed(`<Slider />`);
await expect(page.getByRole("slider")).toBeVisible();
});
test("component renders with label", async ({ initTestBed, page }) => {
await initTestBed(`<Slider label="Volume" />`);
await expect(page.getByRole("slider")).toBeVisible();
await expect(page.getByText("Volume")).toBeVisible();
});
test("sets initialValue of field", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="5" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText("5");
});
test("accepts empty as initialValue", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText("0");
});
[
{ label: "int", value: 5, expected: "5" },
{ label: "float", value: 5.5, expected: "5.5" },
].forEach(({ label, value, expected }) => {
test(`handles ${label} correctly`, async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="${JSON.stringify(value)}" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText(expected);
});
});
[
{ label: "string that resolves to int", value: "5", expected: "5" },
{ label: "string that resolves to float", value: "5.5", expected: "5.5" },
].forEach(({ label, value, expected }) => {
test(`handles ${label} correctly`, async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="${value}" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText(expected);
});
});
[
{ label: "NaN", value: NaN },
{ label: "null", value: null },
{ label: "undefined", value: undefined },
{ label: "empty string", value: "" },
{ label: "string not resolving to number", value: "abc" },
].forEach(({ label, value }) => {
test(`handles ${label} gracefully`, async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="${value}" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText("0");
});
});
test("minValue sets the lower bound", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" minValue="5" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
const slider = page.getByRole("slider");
await expect(slider).toHaveAttribute("aria-valuemin", "5");
});
test("value cannot be lower than minValue", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" minValue="20" maxValue="30" initialValue="10" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText("20");
});
test("maxValue sets the upper bound", async ({ initTestBed, page }) => {
await initTestBed(`<Slider maxValue="50" />`);
const slider = page.getByRole("slider");
await expect(slider).toHaveAttribute("aria-valuemax", "50");
});
test("value cannot be larger than maxValue", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" maxValue="30" initialValue="40" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText("30");
});
test("handles invalid minValue/maxValue gracefully", async ({ initTestBed, page }) => {
await initTestBed(`<Slider minValue="invalid" maxValue="invalid" />`);
const slider = page.getByRole("slider");
await expect(slider).toHaveAttribute("aria-valuemin", "0");
await expect(slider).toHaveAttribute("aria-valuemax", "10");
});
test("step defines increment value", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" step="2" initialValue="0" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
const slider = page.getByRole("slider");
await slider.press("ArrowRight");
await expect(page.getByTestId("slider-value")).toHaveText("2");
});
test("handles fractional step values", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" step="0.1" initialValue="0" />
<Text testId="slider-value" value="{slider.value}" />
</Fragment>`);
const slider = page.getByRole("slider");
await slider.press("ArrowRight");
await expect(page.getByTestId("slider-value")).toHaveText("0.1");
});
test("component handles multiple thumbs", async ({ initTestBed, page }) => {
await initTestBed(`<Slider initialValue="{[2, 4]}" />`);
const thumbs = page.getByRole("slider");
await expect(thumbs).toHaveCount(2);
});
test("all thumbs are interactable via mouse", async ({
initTestBed,
createSliderDriver,
page,
}) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="{[2, 4]}" minValue="0" maxValue="10" />
<Text testId="sliderValue0">{slider.value[0]}</Text>
<Text testId="sliderValue1">{slider.value[1]}</Text>
</Fragment>
`);
const driver = await createSliderDriver("slider");
await driver.dragThumbByMouse("start", 0);
await driver.dragThumbByMouse("end", 1);
await expect(page.getByTestId("sliderValue0")).toHaveText("0");
await expect(page.getByTestId("sliderValue1")).toHaveText("10");
});
test("all thumbs are interactable via keyboard", async ({ initTestBed, createSliderDriver, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="{[2, 4]}" minValue="0" maxValue="10" />
<Text testId="sliderValue0">{slider.value[0]}</Text>
<Text testId="sliderValue1">{slider.value[1]}</Text>
</Fragment>
`);
const driver = await createSliderDriver("slider");
await driver.stepThumbByKeyboard("ArrowLeft", 0);
await driver.stepThumbByKeyboard("ArrowRight", 1);
await expect(page.getByTestId("sliderValue0")).toHaveText("1");
await expect(page.getByTestId("sliderValue1")).toHaveText("5");
});
test("minStepsBetweenThumbs maintains thumb separation", async ({ initTestBed, createSliderDriver, page }) => {
await initTestBed(`
<Fragment>
<Slider id="slider" initialValue="{[0, 5]}" minStepsBetweenThumbs="3" minValue="0" maxValue="10" />
<Text testId="sliderValue1">{slider.value[1]}</Text>
</Fragment>
`);
const driver = await createSliderDriver("slider");
await driver.stepThumbByKeyboard("ArrowLeft", 1, 3); // Try to move left by 3 steps
await expect(page.getByTestId("sliderValue1")).toHaveText("3");
});
test("enabled=false disables control", async ({ initTestBed, page }) => {
await initTestBed(`<Slider enabled="false" />`);
await expect(page.getByRole("slider")).toBeDisabled();
});
test("readOnly prevents interaction", async ({ initTestBed, page, createSliderDriver }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" readOnly="true" />
<Text testId="slider-value" value="{mySlider.value}" />
</Fragment>`);
const driver = await createSliderDriver("mySlider");
await driver.dragThumbByMouse("end");
await expect(page.getByTestId("slider-value")).toHaveText("0"); // Value should remain unchanged
});
test.fixme(
"autoFocus focuses slider on mount",
SKIP_REASON.XMLUI_BUG("autoFocus does not seem to work with radix-ui, need to double-check"),
async ({ initTestBed, page }) => {
await initTestBed(`<Slider autoFocus="true" />`);
await expect(page.getByRole("slider")).toBeFocused();
},
);
test("required shows visual indicator", async ({ initTestBed, page }) => {
await initTestBed(`<Slider required="true" label="Required slider" />`);
await expect(page.getByText("Required slider")).toContainText("*");
});
test("showValues=true displays current value", async ({ initTestBed, page }) => {
await initTestBed(`<Slider showValues="true" initialValue="10" />`);
await page.getByRole("slider").hover();
await expect(page.getByText("10")).toBeVisible();
});
test("showValues=false hides current value", async ({ initTestBed, page }) => {
await initTestBed(`<Slider showValues="false" initialValue="10" />`);
await page.getByRole("slider").hover();
await expect(page.getByText("10")).not.toBeVisible();
});
test("valueFormat customizes value display", async ({ initTestBed, page }) => {
await initTestBed(
`<Slider showValues="true" initialValue="10" valueFormat="{(v) => v + '%'}" />`,
);
await page.getByRole("slider").hover();
await expect(page.getByText("10%")).toBeVisible();
});
test("handles array initialValue for range", async ({ initTestBed, page }) => {
await initTestBed(`<Slider initialValue="{[2, 6]}" />`);
const sliders = page.getByRole("slider");
await sliders.first().hover();
await expect(page.getByText("2")).toBeVisible();
await expect(page.getByText("6")).toBeVisible();
});
test("maintains thumb order in range mode", async ({ initTestBed, page }) => {
await initTestBed(`<Slider initialValue="{[6, 2]}" />`);
const sliders = page.getByRole("slider");
// Should auto-correct to [2, 6]
await sliders.first().hover();
await expect(page.getByText("2")).toBeVisible();
await expect(page.getByText("6")).toBeVisible();
});
});
// =============================================================================
// ACCESSIBILITY TESTS
// =============================================================================
test.describe("Accessibility", () => {
test("has correct ARIA role", async ({ initTestBed, page }) => {
await initTestBed(`<Slider />`);
await expect(page.getByRole("slider")).toBeVisible();
});
test("label is properly associated", async ({ initTestBed, page }) => {
await initTestBed(`<Slider label="Volume Control" />`);
await page.getByText("Volume Control").click();
await expect(page.getByRole("slider")).toBeFocused();
});
test("supports keyboard navigation", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" initialValue="5" />
<Text testId="slider-value">{mySlider.value}</Text>
</Fragment>`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("ArrowRight");
await expect(page.getByTestId("slider-value")).toHaveText("6");
await slider.press("ArrowLeft");
await expect(page.getByTestId("slider-value")).toHaveText("5");
});
test("supports Home/End key navigation", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" minValue="0" maxValue="100" initialValue="50" />
<Text testId="slider-value">{mySlider.value}</Text>
</Fragment>`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("Home");
await expect(page.getByTestId("slider-value")).toHaveText("0");
await slider.press("End");
await expect(page.getByTestId("slider-value")).toHaveText("100");
});
test("disabled slider has proper ARIA attributes", async ({ initTestBed, page }) => {
await initTestBed(`<Slider enabled="false" />`);
await expect(page.locator("span").first()).toHaveAttribute("aria-disabled", "true");
});
test("required slider has proper ARIA attributes", async ({ initTestBed, page }) => {
await initTestBed(`<Slider required="true" />`);
await expect(page.getByRole("slider")).toHaveAttribute("aria-required", "true");
});
test("range slider has multiple slider roles", async ({ initTestBed, page }) => {
await initTestBed(`<Slider initialValue="{[3, 5]}" />`);
const sliders = page.getByRole("slider");
await expect(sliders).toHaveCount(2);
});
});
// =============================================================================
// LABEL POSITIONING TESTS
// =============================================================================
test.describe("Label", () => {
test("labelPosition=top positions label above slider", async ({ initTestBed, page }) => {
await initTestBed(`<Slider label="test" labelPosition="top" />`);
const { top: sliderTop } = await getBounds(page.getByRole("slider"));
const { bottom: labelBottom } = await getBounds(page.getByText("test"));
expect(labelBottom).toBeLessThan(sliderTop);
});
test("labelPosition=start positions label before slider", async ({ initTestBed, page }) => {
await initTestBed(`<Slider label="test" labelPosition="start" />`);
const { left: sliderLeft } = await getBounds(page.getByRole("slider"));
const { right: labelRight } = await getBounds(page.getByText("test"));
expect(labelRight).toBeLessThan(sliderLeft);
});
test("labelWidth applies custom label width", async ({ initTestBed, page }) => {
const expected = 150;
await initTestBed(`<Slider label="test label" labelWidth="${expected}px" />`);
const { width } = await getBounds(page.getByText("test label"));
expect(width).toEqual(expected);
});
test("labelBreak enables label line breaks", async ({ initTestBed, page }) => {
const labelText = "Very long label text that should break";
await initTestBed(`<Slider label="${labelText}" labelWidth="100px" labelBreak="true" />`);
const label = page.getByText(labelText);
const { height } = await getBounds(label);
expect(height).toBeGreaterThan(20); // Assumes multi-line height
});
test("handles invalid labelPosition gracefully", async ({ initTestBed, page }) => {
await initTestBed(`<Slider labelPosition="invalid" label="test" />`);
await expect(page.getByRole("slider")).toBeVisible();
await expect(page.getByText("test")).toBeVisible();
});
});
// =============================================================================
// EVENT HANDLING TESTS
// =============================================================================
test.describe("Event Handling", () => {
test("didChange event fires on value change", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Slider onDidChange="testState = 'changed'" initialValue="0" />
`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("ArrowRight");
await expect.poll(testStateDriver.testState).toEqual("changed");
});
test("didChange event passes new value", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Slider onDidChange="arg => testState = arg" initialValue="1" />
`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("ArrowRight");
await expect.poll(testStateDriver.testState).toEqual(2);
});
test("gotFocus event fires on focus", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Slider onGotFocus="testState = 'focused'" />
`);
await page.getByRole("slider").focus();
await expect.poll(testStateDriver.testState).toEqual("focused");
});
test("gotFocus event fires on label click", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Slider label="Volume" onGotFocus="testState = 'focused'" />
`);
await page.getByText("Volume").click();
await expect.poll(testStateDriver.testState).toEqual("focused");
});
test("lostFocus event fires on blur", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Slider onLostFocus="testState = 'blurred'" />
`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.blur();
await expect.poll(testStateDriver.testState).toEqual("blurred");
});
test("events do not fire when disabled", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Slider enabled="false" onDidChange="testState = 'changed'" onGotFocus="testState = 'focused'" />
`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("ArrowRight");
await expect.poll(testStateDriver.testState).toEqual(null);
});
});
// =============================================================================
// API TESTS
// =============================================================================
test.describe("Api", () => {
test("value API returns current state", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" initialValue="5" />
<Text testId="value">{mySlider.value}</Text>
</Fragment>
`);
await expect(page.getByTestId("value")).toHaveText("5");
});
test("value API returns state after change", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" initialValue="1" />
<Text testId="value">{mySlider.value}</Text>
</Fragment>
`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("ArrowRight");
await expect(page.getByTestId("value")).toHaveText("2");
});
test("setValue API updates state", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" />
<Button testId="setBtn" onClick="mySlider.setValue(5)" label="{mySlider.value}" />
</Fragment>
`);
await page.getByTestId("setBtn").click();
await expect(page.getByTestId("setBtn")).toHaveText("5");
});
test("setValue API triggers events", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Fragment>
<Slider id="mySlider" onDidChange="testState = 'api-changed'" />
<Button testId="setBtn" onClick="mySlider.setValue(75)" />
</Fragment>
`);
await page.getByTestId("setBtn").click();
await expect.poll(testStateDriver.testState).toEqual("api-changed");
});
test("focus API focuses the slider", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" />
<Button testId="focusBtn" onClick="mySlider.focus()" />
</Fragment>
`);
const slider = page.getByRole("slider");
await expect(slider).not.toBeFocused();
await page.getByTestId("focusBtn").click();
await expect(slider).toBeFocused();
});
test("focus API does nothing when disabled", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" enabled="false" />
<Button testId="focusBtn" onClick="mySlider.focus()" />
</Fragment>
`);
await page.getByTestId("focusBtn").click();
await expect(page.getByRole("slider")).not.toBeFocused();
});
test("setValue does not update when disabled", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" enabled="false" initialValue="5" />
<Button testId="setBtn" onClick="mySlider.setValue(10)" label="{mySlider.value}" />
</Fragment>
`);
await page.getByTestId("setBtn").click();
await expect(page.getByTestId("setBtn")).toHaveText("5");
});
test("setValue handles invalid values gracefully", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" initialValue="5" minValue="0" />
<Button testId="setBtn" onClick="mySlider.setValue('invalid')" label="{mySlider.value}" />
</Fragment>
`);
await page.getByTestId("setBtn").click();
await expect(page.getByTestId("setBtn")).toHaveText("0");
});
});
// =============================================================================
// THEME VARIABLE TESTS
// =============================================================================
test.describe("Theme Variables", () => {
test("backgroundColor-track applies correctly", async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="slider" />`, {
testThemeVars: {
"backgroundColor-track-Slider": "rgb(255, 0, 0)",
},
});
const track = page.locator("[data-track]");
await expect(track).toHaveCSS("background-color", "rgb(255, 0, 0)");
});
test("backgroundColor-range applies correctly", async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="slider" initialValue="5" />`, {
testThemeVars: {
"backgroundColor-range-Slider": "rgb(0, 255, 0)",
},
});
const range = page.locator("[data-range]");
await expect(range).toHaveCSS("background-color", "rgb(0, 255, 0)");
});
test("backgroundColor-thumb applies correctly", async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="slider" />`, {
testThemeVars: {
"backgroundColor-thumb-Slider": "rgb(0, 0, 255)",
},
});
const thumb = page.getByRole("slider");
await expect(thumb).toHaveCSS("background-color", "rgb(0, 0, 255)");
});
test("disabled theme variables apply when disabled", async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="slider" enabled="false" />`, {
testThemeVars: {
"backgroundColor-track-Slider--disabled": "rgb(200, 200, 200)",
},
});
const track = page.locator("[data-track]");
await expect(track).toHaveCSS("background-color", "rgb(200, 200, 200)");
});
test("focus theme variables apply on focus", async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="slider" />`, {
testThemeVars: {
"boxShadow-thumb-Slider--focus": "rgb(0, 123, 255) 0px 0px 10px 0px",
},
});
const slider = page.getByRole("slider");
await slider.focus();
await expect(slider).toHaveCSS("box-shadow", "rgb(0, 123, 255) 0px 0px 10px 0px");
});
test("hover theme variables apply on hover", async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="slider" />`, {
testThemeVars: {
"boxShadow-thumb-Slider--hover": "rgb(0, 0, 0) 0px 0px 5px 0px",
},
});
const slider = page.getByRole("slider");
await slider.hover();
await expect(slider).toHaveCSS("box-shadow", "rgb(0, 0, 0) 0px 0px 5px 0px");
});
});
// =============================================================================
// VALIDATION TESTS
// =============================================================================
test.describe("Validation", () => {
test("handles invalid validationStatus gracefully", async ({ initTestBed, page }) => {
await initTestBed(`<Slider validationStatus="invalid" />`);
await expect(page.getByRole("slider")).toBeVisible();
});
[
{ value: "--default", prop: "" },
{ value: "--warning", prop: 'validationStatus="warning"' },
{ value: "--error", prop: 'validationStatus="error"' },
{ value: "--success", prop: 'validationStatus="valid"' },
].forEach((variant) => {
test(`applies correct borderRadius ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider ${variant.prop} />`, {
testThemeVars: { [`borderRadius-Slider${variant.value}`]: "12px" },
});
const sliderTrack = page.locator("[data-track]");
await expect(sliderTrack).toHaveCSS("border-radius", "12px");
});
test(`applies correct borderColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider ${variant.prop} />`, {
testThemeVars: { [`borderColor-Slider${variant.value}`]: "rgb(255, 0, 0)" },
});
const sliderTrack = page.locator("[data-track]");
await expect(sliderTrack).toHaveCSS("border-color", "rgb(255, 0, 0)");
});
test(`applies correct borderWidth ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderWidth-Slider${variant.value}`]: "1px" },
});
const sliderTrack = page.locator("[data-track]");
await expect(sliderTrack).toHaveCSS("border-width", "1px");
});
test(`applies correct borderStyle ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider ${variant.prop} />`, {
testThemeVars: { [`borderStyle-Slider${variant.value}`]: "dashed" },
});
const sliderTrack = page.locator("[data-track]");
await expect(sliderTrack).toHaveCSS("border-style", "dashed");
});
test(`applies correct boxShadow ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider ${variant.prop} />`, {
testThemeVars: {
[`boxShadow-Slider${variant.value}`]: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
});
const sliderTrack = page.locator("[data-track]");
await expect(sliderTrack).toHaveCSS(
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
);
});
test(`applies correct borderColor on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider ${variant.prop} />`, {
testThemeVars: { [`borderColor-Slider${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
const sliderTrack = page.locator("[data-track]");
await sliderTrack.hover();
await expect(sliderTrack).toHaveCSS("border-color", "rgb(0, 0, 0)");
});
test(`applies correct boxShadow on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Slider ${variant.prop} />`, {
testThemeVars: {
[`boxShadow-Slider${variant.value}--hover`]: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
});
const sliderTrack = page.locator("[data-track]");
await sliderTrack.hover();
await expect(sliderTrack).toHaveCSS(
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
);
});
});
});
// =============================================================================
// EDGE CASE TESTS
// =============================================================================
test.describe("Edge Cases", () => {
test("handles extremely large values", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" initialValue="${Number.MAX_SAFE_INTEGER}" maxValue="${Number.MAX_SAFE_INTEGER}" />
<Text testId="slider-value" value="{mySlider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText(Number.MAX_SAFE_INTEGER.toString());
});
test("handles negative values", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" minValue="-100" maxValue="0" initialValue="-50" />
<Text testId="slider-value" value="{mySlider.value}" />
</Fragment>`);
await expect(page.getByTestId("slider-value")).toHaveText("-50");
});
test("handles very small step values", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<Slider id="mySlider" step="0.000000001" initialValue="0" />
<Text testId="slider-value" value="{mySlider.value}" />
</Fragment>`);
const slider = page.getByRole("slider");
await slider.focus();
await slider.press("ArrowRight");
await expect(page.getByTestId("slider-value")).toHaveText((0.000000001).toExponential());
});
test("handles conflicting min/max values", async ({ initTestBed, page }) => {
await initTestBed(`<Slider minValue="100" maxValue="50" />`);
await expect(page.getByRole("slider")).toBeVisible();
});
test("handles zero step value", async ({ initTestBed, page }) => {
await initTestBed(`<Slider step="0" />`);
await expect(page.getByRole("slider")).toBeVisible();
});
test("handles range with identical values", async ({ initTestBed, page }) => {
await initTestBed(`<Slider initialValue="{[50, 50]}" />`);
const sliders = page.getByRole("slider");
await expect(sliders).toHaveCount(2);
});
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test("input has correct width in px", async ({ page, initTestBed }) => {
await initTestBed(`<Slider width="200px" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input with label has correct width in px", async ({ page, initTestBed }) => {
await initTestBed(`<Slider width="200px" label="test" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input has correct width in %", async ({ page, initTestBed }) => {
await page.setViewportSize({ width: 400, height: 300});
await initTestBed(`<Slider width="50%" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input with label has correct width in %", async ({ page, initTestBed }) => {
await page.setViewportSize({ width: 400, height: 300});
await initTestBed(`<Slider width="50%" label="test" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
```
--------------------------------------------------------------------------------
/docs/content/components/Text.md:
--------------------------------------------------------------------------------
```markdown
# Text [#text]
The `Text` component displays textual information in a number of optional styles and variants.
You can learn more about this component in the [Working with Text](/working-with-text) article.
Also note that variants of the `Text` component are also mapped to HtmlTag components.
See the [variant](#variant) section to check which variant maps to which HtmlTag.
## Custom Variants [#custom-variants]
In addition to the predefined variants, the `Text` component supports **custom variant names** that can be styled using theme variables. This allows you to create application-specific text styles without modifying the component itself.
When you use a custom variant name (one not in the predefined list), the component automatically applies theme variables following the naming pattern: `{cssProperty}-Text-{variantName}`.
```xmlui-pg display name="Example: custom variants"
<App>
<Theme
textColor-Text-brandTitle="rgb(41, 128, 185)"
fontSize-Text-brandTitle="28px"
fontWeight-Text-brandTitle="bold"
letterSpacing-Text-brandTitle="2px"
>
<Text variant="brandTitle">
Welcome to Our Application
</Text>
</Theme>
</App>
```
In this example, the custom variant `brandTitle` is styled using theme variables. Any CSS text property can be configured, including `textColor`, `fontSize`, `fontWeight`, `fontFamily`, `textDecoration*`, `lineHeight`, `backgroundColor`, `textTransform`, `letterSpacing`, `wordSpacing`, `textShadow`, and more.
## Properties [#properties]
### `breakMode` (default: "normal") [#breakmode-default-normal]
This property controls how text breaks into multiple lines. `normal` uses standard word boundaries, `word` breaks long words to prevent overflow, `anywhere` breaks at any character, `keep` prevents word breaking, and `hyphenate` uses automatic hyphenation. When not specified, uses the default browser behavior or theme variables.
Available values:
| Value | Description |
| --- | --- |
| `normal` | Uses standard word boundaries for breaking **(default)** |
| `word` | Breaks long words when necessary to prevent overflow |
| `anywhere` | Breaks at any character if needed to fit content |
| `keep` | Prevents breaking within words entirely |
| `hyphenate` | Uses automatic hyphenation when breaking words |
```xmlui-pg copy display name="Example: breakMode"
<App>
<VStack gap="16px">
<VStack gap="8px">
<Text variant="strong">breakMode="normal" (default)</Text>
<Text
width="150px"
backgroundColor="lightblue"
padding="8px"
breakMode="normal">
This text uses standardwordbreaking at natural boundaries
like spaces and hyphens.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="word"</Text>
<Text
width="150px"
backgroundColor="lightgreen"
padding="8px"
breakMode="word">
This text will breakverylongwordswhenneeded to prevent
overflow while preserving readability.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="anywhere"</Text>
<Text
width="150px"
backgroundColor="lightyellow"
padding="8px"
breakMode="anywhere">
Thistext willbreakanywhereif neededtofit thecontainer
eveninthe middleofwords.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="keep"</Text>
<Text
width="150px"
backgroundColor="lightcoral"
padding="8px"
breakMode="keep">
This text will keep verylongwords intact and prevent
breaking within words entirely.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="hyphenate"</Text>
<Text
width="150px"
backgroundColor="lavender"
padding="8px"
breakMode="hyphenate"
lang="en">
This text uses automatic hyphenation for
supercalifragilisticexpialidocious words.
</Text>
</VStack>
</VStack>
</App>
```
### `ellipses` (default: true) [#ellipses-default-true]
This property indicates whether ellipses should be displayed when the text is cropped (`true`) or not (`false`).
```xmlui-pg copy display name="Example: ellipses"
<App>
<VStack width="120px">
<Text
backgroundColor="cyan"
color="black"
maxLines="1"
ellipses="false">
Though this long text does is about to crop!
</Text>
<Text
backgroundColor="cyan"
color="black"
maxLines="1">
Though this long text does is about to crop!
</Text>
</VStack>
</App>
```
### `maxLines` [#maxlines]
This property determines the maximum number of lines the component can wrap to. If there is no space to display all the contents, the component displays up to as many lines as specified in this property. When the value is not defined, there is no limit on the displayed lines.
```xmlui-pg copy display name="Example: maxLines"
<App>
<Text
maxWidth="120px"
backgroundColor="cyan"
color="black"
value="A long text that will likely overflow"
maxLines="2" />
</App>
```
### `overflowMode` (default: "not specified") [#overflowmode-default-not-specified]
This property controls how text overflow is handled. `none` prevents wrapping and shows no overflow indicator, `ellipsis` shows ellipses when text is truncated, `scroll` forces single line with horizontal scrolling, and `flow` allows multi-line wrapping with vertical scrolling when needed (ignores maxLines). When not specified, uses the default text behavior.
Available values:
| Value | Description |
| --- | --- |
| `none` | No wrapping, text stays on a single line with no overflow indicator |
| `ellipsis` | Truncates with an ellipsis (default) |
| `scroll` | Forces single line with horizontal scrolling when content overflows |
| `flow` | Allows text to wrap into multiple lines with vertical scrolling when container height is constrained (ignores maxLines) |
```xmlui-pg copy display name="Example: overflowMode"
<App>
<VStack gap="16px">
<VStack gap="8px">
<Text variant="strong">overflowMode="none"</Text>
<Text
width="200px"
backgroundColor="lightcoral"
padding="8px"
overflowMode="none"
maxLines="2">
This is a very long text that will be clipped cleanly without
any overflow indicator when it exceeds the specified lines.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="ellipsis" (default)</Text>
<Text
width="200px"
backgroundColor="lightblue"
padding="8px"
overflowMode="ellipsis"
maxLines="1">
This is a very long text that will show ellipsis when it
overflows the container width.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="scroll"</Text>
<Text
width="200px"
backgroundColor="lightgreen"
padding="8px"
overflowMode="scroll">
This is a very long text that will enable horizontal scrolling
when it overflows the container width.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="flow"</Text>
<Text
width="200px"
height="100px"
backgroundColor="lightyellow"
padding="8px"
overflowMode="flow">
This is a very long text that will wrap to multiple lines and show
a vertical scrollbar when the content exceeds the container height.
This mode ignores maxLines and allows unlimited text wrapping with
vertical scrolling when needed.
</Text>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="flow" (no height constraint)</Text>
<Text
width="200px"
backgroundColor="lightpink"
padding="8px"
overflowMode="flow">
This is a very long text that demonstrates flow mode without a
height constraint. The text will wrap to multiple lines naturally
and the container will grow to accommodate all the content. No
scrollbar will appear since there's no height limitation - the text
flows freely across as many lines as needed.
</Text>
</VStack>
</VStack>
</App>
```
### `preserveLinebreaks` (default: false) [#preservelinebreaks-default-false]
This property indicates if linebreaks should be preserved when displaying text.
```xmlui-pg copy {7} display name="Example: preserveLinebreaks"
<App>
<HStack>
<Text
width="250px"
backgroundColor="cyan"
color="black"
preserveLinebreaks="true"
value="(preserve) This long text
with several line breaks
does not fit into a viewport with a 200-pixel width." />
<Text
width="250px"
backgroundColor="cyan"
color="black"
preserveLinebreaks="false"
value="(don't preserve) This long text
with several line breaks
does not fit into a viewport with a 200-pixel width." />
</HStack>
</App>
```
> **Note**: Remember to use the `value` property of the `Text`.
> Linebreaks are converted to spaces when nesting the text inside the `Text` component.
### `value` [#value]
The text to be displayed. This value can also be set via nesting the text into the `Text` component.
```xmlui-pg copy display name="Example: value"
<App>
<Text value="An example text" />
<Text>An example text</Text>
</App>
```
### `variant` [#variant]
An optional string value that provides named presets for text variants with a unique combination of font style, weight, size, color, and other parameters. If not defined, the text uses the current style of its context. In addition to predefined variants, you can specify custom variant names and style them using theme variables with the pattern `{cssProperty}-Text-{variantName}` (e.g., `textColor-Text-brandTitle`, `fontSize-Text-highlight`). See the documentation for a complete list of supported CSS properties.
Available values:
| Value | Description |
| --- | --- |
| `abbr` | Represents an abbreviation or acronym |
| `caption` | Represents the caption (or title) of a table |
| `cite` | Is used to mark up the title of a cited work |
| `code` | Represents a line of code |
| `deleted` | Represents text that has been deleted |
| `em` | Marks text to stress emphasis |
| `inherit` | Represents text that inherits the style from its parent element |
| `inserted` | Represents a range of text that has been added to a document |
| `keyboard` | Represents a span of text denoting textual user input from a keyboard or voice input |
| `marked` | Represents text which is marked or highlighted for reference or notation |
| `mono` | Text using a mono style font family |
| `paragraph` | Represents a paragraph |
| `placeholder` | Text that is mostly used as the placeholder style in input controls |
| `sample` | Represents sample (or quoted) output from a computer program |
| `secondary` | Represents a bit dimmed secondary text |
| `small` | Represents side-comments and small print |
| `sub` | Specifies inline text as subscript |
| `strong` | Contents have strong importance |
| `subheading` | Indicates that the text is the subtitle in a heading |
| `subtitle` | Indicates that the text is the subtitle of some other content |
| `sup` | Specifies inline text as superscript |
| `tableheading` | Indicates that the text is a table heading |
| `title` | Indicates that the text is the title of some other content |
| `var` | Represents the name of a variable in a mathematical expression |
```xmlui-pg name="Example: variant"
<App>
<HStack>
<Text width="150px">default:</Text>
<Text>This is an example text</Text>
</HStack>
<HStack>
<Text width="150px">paragraph:</Text>
<Text variant="paragraph">This is an example paragraph</Text>
</HStack>
<HStack>
<Text width="150px">abbr:</Text>
<Text variant="abbr">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">cite:</Text>
<Text variant="cite">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">code:</Text>
<Text variant="code">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">deleted:</Text>
<Text variant="deleted">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">inserted:</Text>
<Text variant="inserted">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">keyboard:</Text>
<Text variant="keyboard">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">marked:</Text>
<Text variant="marked">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">sample:</Text>
<Text variant="sample">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">sup:</Text>
<Text>
This is an example text
<Text variant="sup">(with some additional text)</Text>
</Text>
</HStack>
<HStack>
<Text width="150px">sub:</Text>
<Text>
This is an example text
<Text variant="sub">(with some additional text)</Text>
</Text>
</HStack>
<HStack>
<Text width="150px">var:</Text>
<Text variant="var">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">mono:</Text>
<Text variant="mono">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">strong:</Text>
<Text variant="strong">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">em:</Text>
<Text variant="em">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">title:</Text>
<Text variant="title">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">subtitle:</Text>
<Text variant="subtitle">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">small:</Text>
<Text variant="small">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">caption:</Text>
<Text variant="caption">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">placeholder:</Text>
<Text variant="placeholder">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">subheading:</Text>
<Text variant="subheading">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">tableheading:</Text>
<Text variant="tableheading">
This is an example text
</Text>
</HStack>
<HStack>
<Text width="150px">secondary:</Text>
<Text variant="secondary">
This is an example text
</Text>
</HStack>
</App>
```
**HtmlTag Mappings**
The table below indicates which Text `variant` maps to which HtmlTag component.
| Variant | Component |
| ----------- | --------- |
| `abbr` | abbr |
| `cite` | cite |
| `code` | code |
| `deleted` | del |
| `inserted` | ins |
| `keyboard` | kbd |
| `marked` | mark |
| `sample` | samp |
| `sub` | sub |
| `sup` | sup |
| `var` | var |
| `strong` | strong |
| `em` | em |
| `paragraph` | p |
## Events [#events]
This component does not have any events.
## Exposed Methods [#exposed-methods]
### `hasOverflow` [#hasoverflow]
Returns true when the displayed text overflows its container boundaries.
**Signature**: `hasOverflow(): boolean`
## Styling [#styling]
### Custom Variant Theme Variables [#custom-variant-theme-variables]
When using custom variants, you can style them using theme variables with the naming pattern `{propertyName}-Text-{variantName}`. The following CSS properties are supported:
| Theme Variable Name | Description | Example Value |
|---------------------|-------------|---------------|
| `textColor-Text-{variant}` | Text color | `rgb(255, 0, 0)`, `#ff0000`, `red` |
| `fontFamily-Text-{variant}` | Font family | `"Arial, sans-serif"`, `monospace` |
| `fontSize-Text-{variant}` | Font size | `16px`, `1.5rem`, `large` |
| `fontStyle-Text-{variant}` | Font style | `normal`, `italic`, `oblique` |
| `fontWeight-Text-{variant}` | Font weight | `normal`, `bold`, `700` |
| `fontStretch-Text-{variant}` | Font stretch | `normal`, `expanded`, `condensed` |
| `textDecorationLine-Text-{variant}` | Decoration line type | `none`, `underline`, `overline`, `line-through` |
| `textDecorationColor-Text-{variant}` | Decoration color | `rgb(255, 0, 0)`, `currentColor` |
| `textDecorationStyle-Text-{variant}` | Decoration style | `solid`, `dashed`, `dotted`, `wavy` |
| `textDecorationThickness-Text-{variant}` | Decoration thickness | `2px`, `from-font`, `auto` |
| `textUnderlineOffset-Text-{variant}` | Underline offset | `5px`, `0.2em`, `auto` |
| `lineHeight-Text-{variant}` | Line height | `1.5`, `24px`, `normal` |
| `backgroundColor-Text-{variant}` | Background color | `rgb(255, 255, 0)`, `transparent` |
| `textTransform-Text-{variant}` | Text transformation | `none`, `uppercase`, `lowercase`, `capitalize` |
| `letterSpacing-Text-{variant}` | Space between letters | `1px`, `0.1em`, `normal` |
| `wordSpacing-Text-{variant}` | Space between words | `5px`, `0.2em`, `normal` |
| `textShadow-Text-{variant}` | Text shadow | `2px 2px 4px rgba(0,0,0,0.5)` |
| `textIndent-Text-{variant}` | First line indentation | `20px`, `2em`, `0` |
| `textAlign-Text-{variant}` | Horizontal alignment | `left`, `center`, `right`, `justify` |
| `textAlignLast-Text-{variant}` | Last line alignment | `left`, `center`, `right`, `justify` |
| `wordBreak-Text-{variant}` | Word breaking behavior | `normal`, `break-all`, `keep-all` |
| `wordWrap-Text-{variant}` | Word wrapping | `normal`, `break-word` |
| `direction-Text-{variant}` | Text direction | `ltr`, `rtl` |
| `writingMode-Text-{variant}` | Writing mode | `horizontal-tb`, `vertical-rl`, `vertical-lr` |
| `lineBreak-Text-{variant}` | Line breaking rules | `auto`, `normal`, `strict`, `loose` |
```xmlui-pg display name="Example: custom variant styles" /highlight/
<App>
<Theme
textColor-Text-highlight="rgb(255, 193, 7)"
fontWeight-Text-highlight="bold"
backgroundColor-Text-highlight="rgba(0, 0, 0, 0.8)"
padding-Text-highlight="4px 8px"
textShadow-Text-highlight="0 2px 4px rgba(0,0,0,0.5)"
>
<Text variant="highlight">Important Notice</Text>
<Text variant="highlight">This is Important Too</Text>
</Theme>
</App>
```
### Theme Variables [#theme-variables]
| Variable | Default Value (Light) | Default Value (Dark) |
| --- | --- | --- |
| [backgroundColor](../styles-and-themes/common-units/#color)-Text | *none* | *none* |
| [backgroundColor](../styles-and-themes/common-units/#color)-Text-code | rgb(from $color-surface-100 r g b / 0.4) | rgb(from $color-surface-100 r g b / 0.4) |
| [backgroundColor](../styles-and-themes/common-units/#color)-Text-keyboard | rgb(from $color-surface-100 r g b / 0.4) | rgb(from $color-surface-100 r g b / 0.4) |
| [backgroundColor](../styles-and-themes/common-units/#color)-Text-marked | rgb(from $color-primary-200 r g b / 0.4) | rgb(from $color-primary-400 r g b / 0.4) |
| [borderColor](../styles-and-themes/common-units/#color)-Text-code | $color-surface-100 | $color-surface-100 |
| [borderColor](../styles-and-themes/common-units/#color)-Text-keyboard | $color-surface-300 | $color-surface-300 |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Text | $borderRadius | $borderRadius |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Text-code | 4px | 4px |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Text-keyboard | *none* | *none* |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Text | solid | solid |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Text-code | solid | solid |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Text-keyboard | *none* | *none* |
| [borderWidth](../styles-and-themes/common-units/#size)-Text | $space-0 | $space-0 |
| [borderWidth](../styles-and-themes/common-units/#size)-Text-code | 1px | 1px |
| [borderWidth](../styles-and-themes/common-units/#size)-Text-keyboard | 1px | 1px |
| [direction](../styles-and-themes/layout-props#direction)-Text | *none* | *none* |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-Text | $fontFamily | $fontFamily |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-Text-code | $fontFamily-monospace | $fontFamily-monospace |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-Text-codefence | $fontFamily-monospace | $fontFamily-monospace |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-Text-keyboard | $fontFamily-monospace | $fontFamily-monospace |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-Text-mono | $fontFamily-monospace | $fontFamily-monospace |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-Text-sample | $fontFamily-monospace | $fontFamily-monospace |
| [fontSize](../styles-and-themes/common-units/#size)-Text | $fontSize-sm | $fontSize-sm |
| [fontSize](../styles-and-themes/common-units/#size)-Text-code | $fontSize-sm | $fontSize-sm |
| [fontSize](../styles-and-themes/common-units/#size)-Text-codefence | $fontSize-code | $fontSize-code |
| [fontSize](../styles-and-themes/common-units/#size)-Text-keyboard | $fontSize-sm | $fontSize-sm |
| [fontSize](../styles-and-themes/common-units/#size)-Text-paragraph | *none* | *none* |
| [fontSize](../styles-and-themes/common-units/#size)-Text-placeholder | $fontSize-xs | $fontSize-xs |
| [fontSize](../styles-and-themes/common-units/#size)-Text-sample | $fontSize-sm | $fontSize-sm |
| [fontSize](../styles-and-themes/common-units/#size)-Text-secondary | $fontSize-sm | $fontSize-sm |
| [fontSize](../styles-and-themes/common-units/#size)-Text-small | $fontSize-sm | $fontSize-sm |
| [fontSize](../styles-and-themes/common-units/#size)-Text-sub | $fontSize-xs | $fontSize-xs |
| [fontSize](../styles-and-themes/common-units/#size)-Text-subheading | $fontSize-H6 | $fontSize-H6 |
| [fontSize](../styles-and-themes/common-units/#size)-Text-subtitle | $fontSize-xl | $fontSize-xl |
| [fontSize](../styles-and-themes/common-units/#size)-Text-sup | $fontSize-xs | $fontSize-xs |
| [fontSize](../styles-and-themes/common-units/#size)-Text-tableheading | $fontSize-H6 | $fontSize-H6 |
| [fontSize](../styles-and-themes/common-units/#size)-Text-title | $fontSize-2xl | $fontSize-2xl |
| [fontStretch](../styles-and-themes/common-units/#fontStretch)-Text | *none* | *none* |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text | *none* | *none* |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text-cite | italic | italic |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text-em | italic | italic |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text-marked | *none* | *none* |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text-placeholder | *none* | *none* |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text-subheading | *none* | *none* |
| [fontStyle](../styles-and-themes/common-units/#fontStyle)-Text-var | italic | italic |
| [fontVariant](../styles-and-themes/common-units/#font-variant)-Text | *none* | *none* |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text | $fontWeight-normal | $fontWeight-normal |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-abbr | $fontWeight-bold | $fontWeight-bold |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-keyboard | $fontWeight-bold | $fontWeight-bold |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-marked | *none* | *none* |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-placeholder | *none* | *none* |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-strong | $fontWeight-bold | $fontWeight-bold |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-subheading | $fontWeight-bold | $fontWeight-bold |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-Text-tableheading | $fontWeight-bold | $fontWeight-bold |
| [letterSpacing](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [letterSpacing](../styles-and-themes/common-units/#size)-Text-caption | 0.05rem | 0.05rem |
| [letterSpacing](../styles-and-themes/common-units/#size)-Text-subheading | 0.04em | 0.04em |
| [lineBreak](../styles-and-themes/common-units/#line-break)-Text | *none* | *none* |
| [lineHeight](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [lineHeight](../styles-and-themes/common-units/#size)-Text-codefence | 1.5 | 1.5 |
| [lineHeight](../styles-and-themes/common-units/#size)-Text-marked | *none* | *none* |
| [marginBottom](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [marginBottom](../styles-and-themes/common-units/#size)-Text-code | *none* | *none* |
| [marginBottom](../styles-and-themes/common-units/#size)-Text-small | *none* | *none* |
| [marginBottom](../styles-and-themes/common-units/#size)-Text-tableheading | $space-4 | $space-4 |
| [marginLeft](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [marginLeft](../styles-and-themes/common-units/#size)-Text-code | *none* | *none* |
| [marginLeft](../styles-and-themes/common-units/#size)-Text-small | *none* | *none* |
| [marginRight](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [marginRight](../styles-and-themes/common-units/#size)-Text-code | *none* | *none* |
| [marginRight](../styles-and-themes/common-units/#size)-Text-small | *none* | *none* |
| [marginTop](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [marginTop](../styles-and-themes/common-units/#size)-Text-code | *none* | *none* |
| [marginTop](../styles-and-themes/common-units/#size)-Text-small | *none* | *none* |
| [marginTop](../styles-and-themes/common-units/#size)-Text-tableheading | $space-1 | $space-1 |
| [paddingBottom](../styles-and-themes/common-units/#size)-Text-code | 2px | 2px |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-Text-code | $space-0_5 | $space-0_5 |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-Text-codefence | $space-4 | $space-4 |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-Text-keyboard | $space-1 | $space-1 |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-Text-tableheading | $space-1 | $space-1 |
| [paddingVertical](../styles-and-themes/common-units/#size)-Text-codefence | $space-3 | $space-3 |
| [paddingVertical](../styles-and-themes/common-units/#size)-Text-paragraph | $space-1 | $space-1 |
| [textAlign](../styles-and-themes/common-units/#text-align)-Text | *none* | *none* |
| [textAlignLast](../styles-and-themes/common-units/#text-align)-Text | *none* | *none* |
| [textColor](../styles-and-themes/common-units/#color)-Text | $textColor-primary | $textColor-primary |
| [textColor](../styles-and-themes/common-units/#color)-Text--hover | *none* | *none* |
| [textColor](../styles-and-themes/common-units/#color)-Text-code--hover | *none* | *none* |
| [textColor](../styles-and-themes/common-units/#color)-Text-codefence | $color-surface-900 | $color-surface-900 |
| [textColor](../styles-and-themes/common-units/#color)-Text-marked | *none* | *none* |
| [textColor](../styles-and-themes/common-units/#color)-Text-placeholder | $color-surface-500 | $color-surface-500 |
| [textColor](../styles-and-themes/common-units/#color)-Text-secondary | $textColor-secondary | $textColor-secondary |
| [textColor](../styles-and-themes/common-units/#color)-Text-small--hover | *none* | *none* |
| [textColor](../styles-and-themes/common-units/#color)-Text-subheading | $textColor-secondary | $textColor-secondary |
| [textDecorationColor](../styles-and-themes/common-units/#color)-Text | *none* | *none* |
| [textDecorationColor](../styles-and-themes/common-units/#color)-Text-deleted | *none* | *none* |
| [textDecorationColor](../styles-and-themes/common-units/#color)-Text-inserted | *none* | *none* |
| [textDecorationLine](../styles-and-themes/common-units/#textDecoration)-Text | *none* | *none* |
| [textDecorationLine](../styles-and-themes/common-units/#textDecoration)-Text-deleted | line-through | line-through |
| [textDecorationLine](../styles-and-themes/common-units/#textDecoration)-Text-inserted | underline | underline |
| [textDecorationStyle](../styles-and-themes/common-units/#textDecoration)-Text | *none* | *none* |
| [textDecorationStyle](../styles-and-themes/common-units/#textDecoration)-Text-deleted | *none* | *none* |
| [textDecorationStyle](../styles-and-themes/common-units/#textDecoration)-Text-inserted | *none* | *none* |
| [textDecorationThickness](../styles-and-themes/common-units/#textDecoration)-Text | *none* | *none* |
| [textDecorationThickness](../styles-and-themes/common-units/#textDecoration)-Text-deleted | *none* | *none* |
| [textDecorationThickness](../styles-and-themes/common-units/#textDecoration)-Text-inserted | *none* | *none* |
| [textIndent](../styles-and-themes/common-units/#text-indent)-Text | *none* | *none* |
| [textShadow](../styles-and-themes/common-units/#text-shadow)-Text | *none* | *none* |
| [textTransform](../styles-and-themes/common-units/#textTransform)-Text | *none* | *none* |
| [textTransform](../styles-and-themes/common-units/#textTransform)-Text-abbr | uppercase | uppercase |
| [textTransform](../styles-and-themes/common-units/#textTransform)-Text-subheading | uppercase | uppercase |
| [textUnderlineOffset](../styles-and-themes/common-units/#size)-Text | *none* | *none* |
| [verticalAlignment](../styles-and-themes/common-units/#alignment)-Text | *none* | *none* |
| [verticalAlignment](../styles-and-themes/common-units/#alignment)-Text-code | *none* | *none* |
| [verticalAlignment](../styles-and-themes/common-units/#alignment)-Text-small | *none* | *none* |
| [verticalAlignment](../styles-and-themes/common-units/#alignment)-Text-sub | sub | sub |
| [verticalAlignment](../styles-and-themes/common-units/#alignment)-Text-sup | super | super |
| [wordBreak](../styles-and-themes/common-units/#word-break)-Text | *none* | *none* |
| [wordSpacing](../styles-and-themes/common-units/#word-spacing)-Text | *none* | *none* |
| [wordWrap](../styles-and-themes/common-units/#word-wrap)-Text | *none* | *none* |
| [writingMode](../styles-and-themes/common-units/#writing-mode)-Text | *none* | *none* |
```
--------------------------------------------------------------------------------
/xmlui/tests/components-core/theming/component-layout.resolver.test.ts:
--------------------------------------------------------------------------------
```typescript
import { describe, expect, it } from "vitest";
import {
BASE_COMPONENT_PART,
resolveComponentLayoutProps,
} from "../../../src/components-core/theming/component-layout-resolver";
describe("Component property layout", () => {
it("should parse empty", () => {
const result = resolveComponentLayoutProps({});
expect(result).toStrictEqual({});
});
describe("single component properties", () => {
it("single component property", () => {
const result = resolveComponentLayoutProps({
color: "red",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { color: "red" },
},
});
});
it("single component property: paddingHorizontal", () => {
const result = resolveComponentLayoutProps({
paddingHorizontal: "12px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { paddingLeft: "12px", paddingRight: "12px" },
},
});
});
it("single component property: paddingVertical", () => {
const result = resolveComponentLayoutProps({
paddingVertical: "12px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { paddingTop: "12px", paddingBottom: "12px" },
},
});
});
it("single component property: marginHorizontal", () => {
const result = resolveComponentLayoutProps({
marginHorizontal: "12px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { marginLeft: "12px", marginRight: "12px" },
},
});
});
it("single component property: marginVertical", () => {
const result = resolveComponentLayoutProps({
marginVertical: "12px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { marginTop: "12px", marginBottom: "12px" },
},
});
});
it("single component property: borderHorizontal", () => {
const result = resolveComponentLayoutProps({
borderHorizontal: "12px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { borderLeft: "12px", borderRight: "12px" },
},
});
});
it("single component property: borderVertical", () => {
const result = resolveComponentLayoutProps({
borderVertical: "12px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { borderTop: "12px", borderBottom: "12px" },
},
});
});
it("single component property: wrapContent/true", () => {
const result = resolveComponentLayoutProps({
wrapContent: true,
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexWrap: "wrap" },
},
});
});
it("single component property: wrapContent/false", () => {
const result = resolveComponentLayoutProps({
wrapContent: false,
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexWrap: "nowrap" },
},
});
});
it("single component property: canShrink/true", () => {
const result = resolveComponentLayoutProps({
canShrink: true,
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexShrink: 1 },
},
});
});
it("single component property: canShrink/false", () => {
const result = resolveComponentLayoutProps({
canShrink: false,
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexShrink: 0 },
},
});
});
it("single component property: width (no star size)", () => {
const result = resolveComponentLayoutProps({
width: "100px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { width: "100px" },
},
});
});
it("single component property: width (star size)", () => {
const result = resolveComponentLayoutProps(
{
width: "23*",
},
{
type: "Stack",
orientation: "horizontal",
},
);
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flex: 23, flexShrink: 1 },
},
});
});
it("single component property: height (no star size)", () => {
const result = resolveComponentLayoutProps({
height: "100px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { height: "100px" },
},
});
});
it("single component property: height (star size)", () => {
const result = resolveComponentLayoutProps(
{
height: "23*",
},
{
type: "Stack",
orientation: "vertical",
},
);
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flex: 23, flexShrink: 1 },
},
});
});
});
describe("multiple component properties", () => {
it("multiple component properties", () => {
const result = resolveComponentLayoutProps({
color: "red",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { color: "red", backgroundColor: "green" },
},
});
});
it("multiple component properties: paddingHorizontal", () => {
const result = resolveComponentLayoutProps({
paddingHorizontal: "12px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { paddingLeft: "12px", paddingRight: "12px", backgroundColor: "green" },
},
});
});
it("multiple component properties: paddingVertical", () => {
const result = resolveComponentLayoutProps({
paddingVertical: "12px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { paddingTop: "12px", paddingBottom: "12px", backgroundColor: "green" },
},
});
});
it("multiple component properties: marginHorizontal", () => {
const result = resolveComponentLayoutProps({
marginHorizontal: "12px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { marginLeft: "12px", marginRight: "12px", backgroundColor: "green" },
},
});
});
it("multiple component properties: marginVertical", () => {
const result = resolveComponentLayoutProps({
marginVertical: "12px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { marginTop: "12px", marginBottom: "12px", backgroundColor: "green" },
},
});
});
it("multiple component properties: borderHorizontal", () => {
const result = resolveComponentLayoutProps({
borderHorizontal: "12px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { borderLeft: "12px", borderRight: "12px", backgroundColor: "green" },
},
});
});
it("multiple component properties: borderVertical", () => {
const result = resolveComponentLayoutProps({
borderVertical: "12px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { borderTop: "12px", borderBottom: "12px", backgroundColor: "green" },
},
});
});
it("multiple component properties: wrapContent/true", () => {
const result = resolveComponentLayoutProps({
wrapContent: true,
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexWrap: "wrap", backgroundColor: "green" },
},
});
});
it("multiple component properties: wrapContent/false", () => {
const result = resolveComponentLayoutProps({
wrapContent: false,
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexWrap: "nowrap", backgroundColor: "green" },
},
});
});
it("multiple component properties: canShrink/true", () => {
const result = resolveComponentLayoutProps({
canShrink: true,
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexShrink: 1, backgroundColor: "green" },
},
});
});
it("multiple component properties: canShrink/false", () => {
const result = resolveComponentLayoutProps({
canShrink: false,
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flexShrink: 0, backgroundColor: "green" },
},
});
});
it("multiple component properties: width (no star size)", () => {
const result = resolveComponentLayoutProps({
width: "100px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { width: "100px", backgroundColor: "green" },
},
});
});
it("multiple component properties: width (star size)", () => {
const result = resolveComponentLayoutProps(
{
width: "23*",
backgroundColor: "green",
},
{
type: "Stack",
orientation: "horizontal",
},
);
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flex: 23, flexShrink: 1, backgroundColor: "green" },
},
});
});
it("multiple component properties: height (no star size)", () => {
const result = resolveComponentLayoutProps({
height: "100px",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { height: "100px", backgroundColor: "green" },
},
});
});
it("multiple component properties: height (star size)", () => {
const result = resolveComponentLayoutProps(
{
height: "23*",
backgroundColor: "green",
},
{
type: "Stack",
orientation: "vertical",
},
);
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { flex: 23, flexShrink: 1, backgroundColor: "green" },
},
});
});
});
describe("single component properties with state", () => {
it("single component property with state", () => {
const result = resolveComponentLayoutProps({
"color--hover": "red",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { hover: { color: "red" } } },
},
});
});
it("single component property with multiple states #1", () => {
const result = resolveComponentLayoutProps({
"color--hover--active": "red",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { "hover&active": { color: "red" } } },
},
});
});
it("single component property with multiple states #2", () => {
const result = resolveComponentLayoutProps({
"color--hover--active--focus": "red",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { "hover&active&focus": { color: "red" } } },
},
});
});
it("multiple component properties with state", () => {
const result = resolveComponentLayoutProps({
"color--hover": "red",
"backgroundColor--hover": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { hover: { color: "red", backgroundColor: "blue" } } },
},
});
});
it("multiple component properties with multiple states #1", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red",
"backgroundColor--hover": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { focus: { color: "red" }, hover: { backgroundColor: "blue" } } },
},
});
});
it("multiple component properties with multiple states #2", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red",
"backgroundColor--hover--focus": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
states: { focus: { color: "red" }, "hover&focus": { backgroundColor: "blue" } },
},
},
});
});
it("multiple component properties with multiple states #3", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red",
"backgroundColor--focus": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { focus: { color: "red", backgroundColor: "blue" } } },
},
});
});
it("multiple component properties with multiple states #4", () => {
const result = resolveComponentLayoutProps({
"color--focus--active": "red",
"backgroundColor--focus": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
states: { focus: { backgroundColor: "blue" }, "focus&active": { color: "red" } },
},
},
});
});
});
describe("multiple component properties with and without state", () => {
it("multiple component properties with state", () => {
const result = resolveComponentLayoutProps({
"color--hover": "red",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { backgroundColor: "green", states: { hover: { color: "red" } } },
},
});
});
it("multiple component properties with multiple states #1", () => {
const result = resolveComponentLayoutProps({
"color--hover--active": "red",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { backgroundColor: "green", states: { "hover&active": { color: "red" } } },
},
});
});
it("multiple component properties with multiple states #2", () => {
const result = resolveComponentLayoutProps({
"color--hover--active--focus": "red",
backgroundColor: "green",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
backgroundColor: "green",
states: { "hover&active&focus": { color: "red" } },
},
},
});
});
it("multiple component properties with state (both with state)", () => {
const result = resolveComponentLayoutProps({
"color--hover": "red",
"backgroundColor--hover": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { hover: { color: "red", backgroundColor: "blue" } } },
},
});
});
it("multiple component properties with multiple states #1 (mixed)", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red",
"backgroundColor--hover": "blue",
padding: "10px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
padding: "10px",
states: { focus: { color: "red" }, hover: { backgroundColor: "blue" } },
},
},
});
});
it("multiple component properties with multiple states #2 (mixed)", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red",
"backgroundColor--hover--focus": "blue",
margin: "5px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
margin: "5px",
states: { focus: { color: "red" }, "hover&focus": { backgroundColor: "blue" } },
},
},
});
});
it("multiple component properties with multiple states #3 (mixed)", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red",
"backgroundColor--focus": "blue",
border: "1px solid black",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
border: "1px solid black",
states: { focus: { color: "red", backgroundColor: "blue" } },
},
},
});
});
it("multiple component properties with multiple states #4 (mixed)", () => {
const result = resolveComponentLayoutProps({
"color--focus--active": "red",
"backgroundColor--focus": "blue",
fontSize: "14px",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
fontSize: "14px",
states: { focus: { backgroundColor: "blue" }, "focus&active": { color: "red" } },
},
},
});
});
});
describe("component properties with parts", () => {
it("single component property with part", () => {
const result = resolveComponentLayoutProps({
"color-indicator": "red",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { color: "red" },
},
});
});
it("multiple component property with part", () => {
const result = resolveComponentLayoutProps({
"color-indicator": "red",
"backgroundColor-indicator": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { color: "red", backgroundColor: "green" },
},
});
});
it("multiple component property with multiple parts #1", () => {
const result = resolveComponentLayoutProps({
"color-indicator": "red",
"backgroundColor-handle": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { color: "red" },
},
handle: {
baseStyles: { backgroundColor: "green" },
},
});
});
it("multiple component property with multiple parts #2", () => {
const result = resolveComponentLayoutProps({
"color-indicator": "red",
"backgroundColor-handle": "green",
"fontSize-indicator": "12px",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { color: "red", fontSize: "12px" },
},
handle: {
baseStyles: { backgroundColor: "green" },
},
});
});
});
describe("component properties with parts and state", () => {
it("single component property with part and state", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover": "red",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { hover: { color: "red" } } },
},
});
});
it("single component property with part and multiple states #1", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover--active": "red",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { "hover&active": { color: "red" } } },
},
});
});
it("single component property with part and multiple states #2", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover--active--focus": "red",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { "hover&active&focus": { color: "red" } } },
},
});
});
it("multiple component properties with part and state", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover": "red",
"backgroundColor-indicator--hover": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { hover: { color: "red", backgroundColor: "green" } } },
},
});
});
it("multiple component properties with part and multiple states #1", () => {
const result = resolveComponentLayoutProps({
"color-indicator--focus": "red",
"backgroundColor-indicator--hover": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { focus: { color: "red" }, hover: { backgroundColor: "green" } } },
},
});
});
it("multiple component properties with part and multiple states #2", () => {
const result = resolveComponentLayoutProps({
"color-indicator--focus": "red",
"backgroundColor-indicator--hover--focus": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: {
states: { focus: { color: "red" }, "hover&focus": { backgroundColor: "green" } },
},
},
});
});
it("multiple component properties with part and multiple states #3", () => {
const result = resolveComponentLayoutProps({
"color-indicator--focus": "red",
"backgroundColor-indicator--focus": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { focus: { color: "red", backgroundColor: "green" } } },
},
});
});
it("multiple component properties with part and multiple states #4", () => {
const result = resolveComponentLayoutProps({
"color-indicator--focus--active": "red",
"backgroundColor-indicator--focus": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: {
states: { focus: { backgroundColor: "green" }, "focus&active": { color: "red" } },
},
},
});
});
it("multiple component properties with multiple parts and states #1", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover": "red",
"backgroundColor-handle--focus": "green",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { hover: { color: "red" } } },
},
handle: {
baseStyles: { states: { focus: { backgroundColor: "green" } } },
},
});
});
it("multiple component properties with multiple parts and states #2", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover": "red",
"backgroundColor-handle--focus": "green",
"fontSize-indicator--active": "12px",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { hover: { color: "red" }, active: { fontSize: "12px" } } },
},
handle: {
baseStyles: { states: { focus: { backgroundColor: "green" } } },
},
});
});
it("multiple component properties with multiple parts and mixed states", () => {
const result = resolveComponentLayoutProps({
"color-indicator--hover--active": "red",
"backgroundColor-handle--focus": "green",
"fontSize-indicator--hover": "12px",
"border-handle--focus--active": "1px solid black",
});
expect(result).toStrictEqual({
indicator: {
baseStyles: { states: { "hover&active": { color: "red" }, hover: { fontSize: "12px" } } },
},
handle: {
baseStyles: {
states: {
focus: { backgroundColor: "green" },
"focus&active": { border: "1px solid black" },
},
},
},
});
});
});
describe("mixed component properties, parts, and states", () => {
it("mix of base properties, parts without states, and parts with states", () => {
const result = resolveComponentLayoutProps({
color: "blue", // base property
"backgroundColor-header": "yellow", // part property without state
"fontSize-indicator--hover": "14px", // part property with state
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { color: "blue" },
},
header: {
baseStyles: { backgroundColor: "yellow" },
},
indicator: {
baseStyles: { states: { hover: { fontSize: "14px" } } },
},
});
});
it("mix of base properties with states and parts with states", () => {
const result = resolveComponentLayoutProps({
"color--hover": "red", // base property with state
"backgroundColor-header--focus": "green", // part property with state
"border-footer": "1px solid black", // part property without state
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: { states: { hover: { color: "red" } } },
},
header: {
baseStyles: { states: { focus: { backgroundColor: "green" } } },
},
footer: {
baseStyles: { border: "1px solid black" },
},
});
});
it("complex mix: base properties, parts, multiple states", () => {
const result = resolveComponentLayoutProps({
width: "100px", // base property
height: "50px", // base property
"color--hover": "red", // base property with state
"backgroundColor--active": "blue", // base property with different state
"fontSize-header": "16px", // part property
"padding-header--hover": "10px", // part property with state
"margin-footer--focus--active": "5px", // part property with multiple states
"border-sidebar": "2px solid gray", // different part property
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
width: "100px",
height: "50px",
states: {
hover: { color: "red" },
active: { backgroundColor: "blue" },
},
},
},
header: {
baseStyles: {
fontSize: "16px",
states: { hover: { padding: "10px" } },
},
},
footer: {
baseStyles: { states: { "focus&active": { margin: "5px" } } },
},
sidebar: {
baseStyles: { border: "2px solid gray" },
},
});
});
it("same property applied to different contexts", () => {
const result = resolveComponentLayoutProps({
color: "black", // base color
"color-header": "blue", // header color
"color-footer--hover": "red", // footer color on hover
"color--active": "green", // base color on active
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
color: "black",
states: { active: { color: "green" } },
},
},
header: {
baseStyles: { color: "blue" },
},
footer: {
baseStyles: { states: { hover: { color: "red" } } },
},
});
});
it("overlapping states across base and parts", () => {
const result = resolveComponentLayoutProps({
"backgroundColor--hover": "lightblue", // base background on hover
"color--hover": "darkblue", // base color on hover
"fontSize-title--hover": "18px", // title font size on hover
"padding-title--focus": "8px", // title padding on focus
"margin-content--hover--focus": "12px", // content margin on hover and focus
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
states: {
hover: { backgroundColor: "lightblue", color: "darkblue" },
},
},
},
title: {
baseStyles: {
states: {
hover: { fontSize: "18px" },
focus: { padding: "8px" },
},
},
},
content: {
baseStyles: { states: { "hover&focus": { margin: "12px" } } },
},
});
});
it("multiple properties per part with mixed states", () => {
const result = resolveComponentLayoutProps({
"width-card": "200px", // card width (no state)
"height-card": "150px", // card height (no state)
"backgroundColor-card--hover": "gray", // card background on hover
"border-card--focus": "2px solid blue", // card border on focus
"color-card--hover--active": "white", // card color on hover and active
"padding-card": "16px", // card padding (no state)
});
expect(result).toStrictEqual({
card: {
baseStyles: {
width: "200px",
height: "150px",
padding: "16px",
states: {
hover: { backgroundColor: "gray" },
focus: { border: "2px solid blue" },
"hover&active": { color: "white" },
},
},
},
});
});
it("state conflicts and merging across different parts", () => {
const result = resolveComponentLayoutProps({
"color--focus": "red", // base color on focus
"backgroundColor--focus": "yellow", // base background on focus
"fontSize-header--focus": "20px", // header font size on focus
"color-footer--focus": "blue", // footer color on focus (different from base)
"margin-header": "10px", // header margin (no state)
"padding-footer--active": "5px", // footer padding on active
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
states: {
focus: { color: "red", backgroundColor: "yellow" },
},
},
},
header: {
baseStyles: {
margin: "10px",
states: { focus: { fontSize: "20px" } },
},
},
footer: {
baseStyles: {
states: {
focus: { color: "blue" },
active: { padding: "5px" },
},
},
},
});
});
});
describe("regression", () => {
it("has responsive with state", () => {
const result = resolveComponentLayoutProps({
backgroundColor: "red",
"backgroundColor-xxl": "green",
"backgroundColor-xxl--hover": "blue",
});
expect(result).toStrictEqual({
[BASE_COMPONENT_PART]: {
baseStyles: {
backgroundColor: "red",
},
responsiveStyles: {
xxl: {
backgroundColor: "green",
states: {
hover: { backgroundColor: "blue" },
},
},
},
},
});
});
});
});
```