This is page 47 of 146. Use http://codebase.md/xmlui-org/xmlui/%7Bnode.props.src?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ └── config.json
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog-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
│ │ │ ├── icons
│ │ │ │ ├── github.svg
│ │ │ │ └── rss.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
│ │ │ ├── LinkButton.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ └── Separator.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
│ │ │ ├── icons
│ │ │ │ ├── github.svg
│ │ │ │ └── rss.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
│ │ ├── staticwebapp.config.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
│ │ │ ├── LinkButton.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── Separator.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
│ │ │ └── test-padding.xmlui
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.json
├── tsdown.config.ts
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/components-core/script-runner/ScriptingSourceTree.ts:
--------------------------------------------------------------------------------
```typescript
import type { GenericToken } from "../../parsers/common/GenericToken";
import type { TokenType } from "../../parsers/scripting/TokenType";
import type { ScriptParserErrorMessage } from "../../abstractions/scripting/ScriptParserError";
// --- All binding expression tree node types
type ScriptNode = Statement | Expression;
type ScriptingToken = GenericToken<TokenType>;
// The root type of all source tree nodes
export interface ScripNodeBase {
// Node type discriminator
type: ScriptNode["type"];
// The unique id of the node
nodeId: number;
// The start token of the node
startToken?: ScriptingToken;
// The end token of the node
endToken?: ScriptingToken;
}
// Import the actual implementation constants from outside the abstractions folder
import * as NodeTypes from "../../parsers/scripting/ScriptingNodeTypes";
// Re-export the constants so they can be used both as types and values
export const {
// Statement node type values
T_BLOCK_STATEMENT,
T_EMPTY_STATEMENT,
T_EXPRESSION_STATEMENT,
T_ARROW_EXPRESSION_STATEMENT,
T_LET_STATEMENT,
T_CONST_STATEMENT,
T_VAR_STATEMENT,
T_IF_STATEMENT,
T_RETURN_STATEMENT,
T_BREAK_STATEMENT,
T_CONTINUE_STATEMENT,
T_WHILE_STATEMENT,
T_DO_WHILE_STATEMENT,
T_FOR_STATEMENT,
T_FOR_IN_STATEMENT,
T_FOR_OF_STATEMENT,
T_THROW_STATEMENT,
T_TRY_STATEMENT,
T_SWITCH_STATEMENT,
T_FUNCTION_DECLARATION,
// Expression node type values
T_UNARY_EXPRESSION,
T_BINARY_EXPRESSION,
T_SEQUENCE_EXPRESSION,
T_CONDITIONAL_EXPRESSION,
T_FUNCTION_INVOCATION_EXPRESSION,
T_MEMBER_ACCESS_EXPRESSION,
T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
T_IDENTIFIER,
T_TEMPLATE_LITERAL_EXPRESSION,
T_LITERAL,
T_ARRAY_LITERAL,
T_OBJECT_LITERAL,
T_SPREAD_EXPRESSION,
T_ASSIGNMENT_EXPRESSION,
T_NO_ARG_EXPRESSION,
T_ARROW_EXPRESSION,
T_PREFIX_OP_EXPRESSION,
T_POSTFIX_OP_EXPRESSION,
T_REACTIVE_VAR_DECLARATION,
// Other node type values
T_VAR_DECLARATION,
T_DESTRUCTURE,
T_ARRAY_DESTRUCTURE,
T_OBJECT_DESTRUCTURE,
T_SWITCH_CASE
} = NodeTypes;
// --- Statement node types
type BLOCK_STATEMENT = typeof T_BLOCK_STATEMENT;
type EMPTY_STATEMENT = typeof T_EMPTY_STATEMENT;
type EXPRESSION_STATEMENT = typeof T_EXPRESSION_STATEMENT;
type ARROW_EXPRESSION_STATEMENT = typeof T_ARROW_EXPRESSION_STATEMENT;
type LET_STATEMENT = typeof T_LET_STATEMENT;
type CONST_STATEMENT = typeof T_CONST_STATEMENT;
type VAR_STATEMENT = typeof T_VAR_STATEMENT;
type IF_STATEMENT = typeof T_IF_STATEMENT;
type RETURN_STATEMENT = typeof T_RETURN_STATEMENT;
type BREAK_STATEMENT = typeof T_BREAK_STATEMENT;
type CONTINUE_STATEMENT = typeof T_CONTINUE_STATEMENT;
type WHILE_STATEMENT = typeof T_WHILE_STATEMENT;
type DO_WHILE_STATEMENT = typeof T_DO_WHILE_STATEMENT;
type FOR_STATEMENT = typeof T_FOR_STATEMENT;
type FOR_IN_STATEMENT = typeof T_FOR_IN_STATEMENT;
type FOR_OF_STATEMENT = typeof T_FOR_OF_STATEMENT;
type THROW_STATEMENT = typeof T_THROW_STATEMENT;
type TRY_STATEMENT = typeof T_TRY_STATEMENT;
type SWITCH_STATEMENT = typeof T_SWITCH_STATEMENT;
type FUNCTION_DECLARATION = typeof T_FUNCTION_DECLARATION;
// --- Expression node types
type UNARY_EXPRESSION = typeof T_UNARY_EXPRESSION;
type BINARY_EXPRESSION = typeof T_BINARY_EXPRESSION;
type SEQUENCE_EXPRESSION = typeof T_SEQUENCE_EXPRESSION;
type CONDITIONAL_EXPRESSION = typeof T_CONDITIONAL_EXPRESSION;
type FUNCTION_INVOCATION_EXPRESSION = typeof T_FUNCTION_INVOCATION_EXPRESSION;
type MEMBER_ACCESS_EXPRESSION = typeof T_MEMBER_ACCESS_EXPRESSION;
type CALCULATED_MEMBER_ACCESS_EXPRESSION = typeof T_CALCULATED_MEMBER_ACCESS_EXPRESSION;
type IDENTIFIER = typeof T_IDENTIFIER;
type TEMPLATE_LITERAL_EXPRESSION = typeof T_TEMPLATE_LITERAL_EXPRESSION;
type LITERAL = typeof T_LITERAL;
type ARRAY_LITERAL = typeof T_ARRAY_LITERAL;
type OBJECT_LITERAL = typeof T_OBJECT_LITERAL;
type SPREAD_EXPRESSION = typeof T_SPREAD_EXPRESSION;
type ASSIGNMENT_EXPRESSION = typeof T_ASSIGNMENT_EXPRESSION;
type NO_ARG_EXPRESSION = typeof T_NO_ARG_EXPRESSION;
type ARROW_EXPRESSION = typeof T_ARROW_EXPRESSION;
type PREFIX_OP_EXPRESSION = typeof T_PREFIX_OP_EXPRESSION;
type POSTFIX_OP_EXPRESSION = typeof T_POSTFIX_OP_EXPRESSION;
type REACTIVE_VAR_DECLARATION = typeof T_REACTIVE_VAR_DECLARATION;
// --- Other node types
type VAR_DECLARATION = typeof T_VAR_DECLARATION;
type DESTRUCTURE = typeof T_DESTRUCTURE;
type ARRAY_DESTRUCTURE = typeof T_ARRAY_DESTRUCTURE;
type OBJECT_DESTRUCTURE = typeof T_OBJECT_DESTRUCTURE;
type SWITCH_CASE = typeof T_SWITCH_CASE;
// =====================================================================================================================
// Statements
export type Statement =
| BlockStatement
| EmptyStatement
| ExpressionStatement
| ArrowExpressionStatement
| LetStatement
| ConstStatement
| VarStatement
| IfStatement
| ReturnStatement
| BreakStatement
| ContinueStatement
| WhileStatement
| DoWhileStatement
| ForStatement
| ForInStatement
| ForOfStatement
| ThrowStatement
| TryStatement
| SwitchStatement
| FunctionDeclaration;
export type LoopStatement = WhileStatement | DoWhileStatement;
export interface EmptyStatement extends ScripNodeBase {
type: EMPTY_STATEMENT;
}
export interface ExpressionStatement extends ScripNodeBase {
type: EXPRESSION_STATEMENT;
expr: Expression;
}
export interface ArrowExpressionStatement extends ScripNodeBase {
type: ARROW_EXPRESSION_STATEMENT;
expr: ArrowExpression;
}
export interface VarDeclaration extends ExpressionBase {
type: VAR_DECLARATION;
id?: string;
aDestr?: ArrayDestructure[];
oDestr?: ObjectDestructure[];
expr?: Expression;
}
export interface DestructureBase extends ExpressionBase {
id?: string;
aDestr?: ArrayDestructure[];
oDestr?: ObjectDestructure[];
}
export interface Destructure extends DestructureBase {
type: DESTRUCTURE;
aDestr?: ArrayDestructure[];
oDestr?: ObjectDestructure[];
}
export interface ArrayDestructure extends DestructureBase {
type: ARRAY_DESTRUCTURE;
}
export interface ObjectDestructure extends DestructureBase {
type: OBJECT_DESTRUCTURE;
id: string;
alias?: string;
}
export interface LetStatement extends ScripNodeBase {
type: LET_STATEMENT;
decls: VarDeclaration[];
}
export interface ConstStatement extends ScripNodeBase {
type: CONST_STATEMENT;
decls: VarDeclaration[];
}
export interface VarStatement extends ScripNodeBase {
type: VAR_STATEMENT;
decls: ReactiveVarDeclaration[];
}
export interface ReactiveVarDeclaration extends ExpressionBase {
type: REACTIVE_VAR_DECLARATION;
id: Identifier;
expr: Expression;
}
export interface BlockStatement extends ScripNodeBase {
type: BLOCK_STATEMENT;
stmts: Statement[];
}
export interface IfStatement extends ScripNodeBase {
type: IF_STATEMENT;
cond: Expression;
thenB: Statement;
elseB?: Statement;
}
export interface ReturnStatement extends ScripNodeBase {
type: RETURN_STATEMENT;
expr?: Expression;
}
export interface WhileStatement extends ScripNodeBase {
type: WHILE_STATEMENT;
cond: Expression;
body: Statement;
}
export interface DoWhileStatement extends ScripNodeBase {
type: DO_WHILE_STATEMENT;
cond: Expression;
body: Statement;
}
export interface BreakStatement extends ScripNodeBase {
type: BREAK_STATEMENT;
}
export interface ContinueStatement extends ScripNodeBase {
type: CONTINUE_STATEMENT;
}
export interface ThrowStatement extends ScripNodeBase {
type: THROW_STATEMENT;
expr: Expression;
}
export interface TryStatement extends ScripNodeBase {
type: TRY_STATEMENT;
tryB: BlockStatement;
catchB?: BlockStatement;
catchV?: Identifier;
finallyB?: BlockStatement;
}
export interface ForStatement extends ScripNodeBase {
type: FOR_STATEMENT;
init?: ExpressionStatement | LetStatement;
cond?: Expression;
upd?: Expression;
body: Statement;
}
export type ForVarBinding = "let" | "const" | "none";
export interface ForInStatement extends ScripNodeBase {
type: FOR_IN_STATEMENT;
varB: ForVarBinding;
id: Identifier;
expr: Expression;
body: Statement;
}
export interface ForOfStatement extends ScripNodeBase {
type: FOR_OF_STATEMENT;
varB: ForVarBinding;
id: Identifier;
expr: Expression;
body: Statement;
}
export interface SwitchStatement extends ScripNodeBase {
type: SWITCH_STATEMENT;
expr: Expression;
cases: SwitchCase[];
}
export interface SwitchCase extends ExpressionBase {
type: SWITCH_CASE;
caseE?: Expression;
stmts?: Statement[];
}
export interface FunctionDeclaration extends ScripNodeBase {
type: FUNCTION_DECLARATION;
id: Identifier;
args: Expression[];
stmt: BlockStatement;
}
// =====================================================================================================================
// Expressions
// All syntax nodes that represent an expression
export type Expression =
| UnaryExpression
| BinaryExpression
| SequenceExpression
| ConditionalExpression
| FunctionInvocationExpression
| MemberAccessExpression
| CalculatedMemberAccessExpression
| Identifier
| TemplateLiteralExpression
| Literal
| ArrayLiteral
| ObjectLiteral
| SpreadExpression
| AssignmentExpression
| NoArgExpression
| ArrowExpression
| PrefixOpExpression
| PostfixOpExpression
| ReactiveVarDeclaration
| VarDeclaration
| Destructure
| ObjectDestructure
| ArrayDestructure
| SwitchCase;
// Common base node for all expression syntax nodes
export interface ExpressionBase extends ScripNodeBase {
// Is this expression parenthesized?
parenthesized?: number;
}
export type UnaryOpSymbols = "+" | "-" | "~" | "!" | "typeof" | "delete";
export type BinaryOpSymbols =
| "**"
| "*"
| "/"
| "%"
| "+"
| "-"
| "<<"
| ">>"
| ">>>"
| "<"
| "<="
| ">"
| ">="
| "=="
| "==="
| "!="
| "!=="
| "&"
| "|"
| "^"
| "&&"
| "||"
| "??"
| "in";
export type AssignmentSymbols =
| "="
| "+="
| "-="
| "**="
| "*="
| "/="
| "%="
| "<<="
| ">>="
| ">>>="
| "&="
| "^="
| "|="
| "&&="
| "||="
| "??=";
export type PrefixOpSymbol = "++" | "--";
export interface UnaryExpression extends ExpressionBase {
type: UNARY_EXPRESSION;
op: UnaryOpSymbols;
expr: Expression;
}
export interface BinaryExpression extends ExpressionBase {
type: BINARY_EXPRESSION;
op: BinaryOpSymbols;
left: Expression;
right: Expression;
}
export interface SequenceExpression extends ExpressionBase {
type: SEQUENCE_EXPRESSION;
exprs: Expression[];
loose?: boolean;
}
export interface ConditionalExpression extends ExpressionBase {
type: CONDITIONAL_EXPRESSION;
cond: Expression;
thenE: Expression;
elseE: Expression;
}
export interface FunctionInvocationExpression extends ExpressionBase {
type: FUNCTION_INVOCATION_EXPRESSION;
obj: Expression;
arguments: Expression[];
}
export interface MemberAccessExpression extends ExpressionBase {
type: MEMBER_ACCESS_EXPRESSION;
obj: Expression;
member: string;
opt?: boolean;
}
export interface CalculatedMemberAccessExpression extends ExpressionBase {
type: CALCULATED_MEMBER_ACCESS_EXPRESSION;
obj: Expression;
member: Expression;
}
export interface Identifier extends ExpressionBase {
type: IDENTIFIER;
name: string;
isGlobal?: boolean;
}
export interface Literal extends ExpressionBase {
type: LITERAL;
value: any;
}
export interface TemplateLiteralExpression extends ExpressionBase {
type: TEMPLATE_LITERAL_EXPRESSION;
segments: (Literal | Expression)[];
}
export interface ArrayLiteral extends ExpressionBase {
type: ARRAY_LITERAL;
items: Expression[];
loose?: boolean;
}
export interface ObjectLiteral extends ExpressionBase {
type: OBJECT_LITERAL;
props: (SpreadExpression | [Expression, Expression])[];
}
export interface SpreadExpression extends ExpressionBase {
type: SPREAD_EXPRESSION;
expr: Expression;
}
export interface AssignmentExpression extends ExpressionBase {
type: ASSIGNMENT_EXPRESSION;
leftValue: Expression;
op: AssignmentSymbols;
expr: Expression;
}
export interface NoArgExpression extends ExpressionBase {
type: NO_ARG_EXPRESSION;
}
export interface ArrowExpression extends ExpressionBase {
type: ARROW_EXPRESSION;
name?: string;
args: Expression[];
statement: Statement;
}
export interface PrefixOpExpression extends ExpressionBase {
type: PREFIX_OP_EXPRESSION;
op: PrefixOpSymbol;
expr: Expression;
}
export interface PostfixOpExpression extends ExpressionBase {
type: POSTFIX_OP_EXPRESSION;
op: PrefixOpSymbol;
expr: Expression;
}
/**
* Represents a parsed and resolved module
*/
export type ScriptModule = {
type: "ScriptModule";
name: string;
parent?: ScriptModule | null;
functions: Record<string, FunctionDeclaration>;
statements: Statement[];
sources: Map<Statement, string>;
};
/**
* Represents a module error
*/
export type ModuleErrors = Record<string, ScriptParserErrorMessage[]>;
export type CollectedDeclarations = {
vars: Record<string, CodeDeclaration>;
functions: Record<string, CodeDeclaration>;
moduleErrors?: ModuleErrors;
};
export type CodeDeclaration = {
source?: string;
tree: Expression;
[x: string]: unknown;
};
```
--------------------------------------------------------------------------------
/xmlui/src/components/NestedApp/NestedAppNative.tsx:
--------------------------------------------------------------------------------
```typescript
import type { CSSProperties, ReactNode } from "react";
import { startTransition, useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { Root } from "react-dom/client";
import ReactDOM from "react-dom/client";
import { AppRoot } from "../../components-core/rendering/AppRoot";
import type { ThemeTone } from "../../abstractions/ThemingDefs";
import { errReportComponent, xmlUiMarkupToComponent } from "../../components-core/xmlui-parser";
import { ApiInterceptorProvider } from "../../components-core/interception/ApiInterceptorProvider";
import { ErrorBoundary } from "../../components-core/rendering/ErrorBoundary";
import type { CompoundComponentDef } from "../../abstractions/ComponentDefs";
import { useTheme } from "../../components-core/theming/ThemeContext";
import { useComponentRegistry } from "../ComponentRegistryContext";
import { useIndexerContext } from "../App/IndexerContext";
import { useApiInterceptorContext } from "../../components-core/interception/useApiInterceptorContext";
import { EMPTY_ARRAY } from "../../components-core/constants";
import { useIsomorphicLayoutEffect } from "../../components-core/utils/hooks";
import styles from "./NestedApp.module.scss";
import classnames from "classnames";
import {
StyleInjectionTargetContext,
StyleProvider,
useStyles,
} from "../../components-core/theming/StyleContext";
type NestedAppProps = {
api?: any;
app: string;
components?: any[];
config?: any;
activeTone?: ThemeTone;
activeTheme?: string;
height?: string | number;
style?: CSSProperties;
refreshVersion?: number;
withSplashScreen?: boolean;
className?: string;
};
function AnimatedLogo() {
return (
<div className={styles.loadingContainer}>
<div className={styles.loadingText}>Loading XMLUI App...</div>
<div className={styles.logoWrapper}>
<svg viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg">
{/* Unchanged inner paths */}
<path
d="M9.04674 19.3954C8.2739 19.3954 7.60226 19.2265 7.03199 18.8887C6.47443 18.5384 6.0435 18.0505 5.73938 17.425C5.43526 16.7869 5.2832 16.0362 5.2832 15.173V9.89961C5.2832 9.7745 5.32771 9.66815 5.41637 9.58059C5.50502 9.493 5.61275 9.44922 5.73938 9.44922H7.41222C7.55157 9.44922 7.6593 9.493 7.73524 9.58059C7.8239 9.66815 7.86841 9.7745 7.86841 9.89961V15.0604C7.86841 16.6117 8.55895 17.3874 9.94021 17.3874C10.5991 17.3874 11.1187 17.181 11.4988 16.7681C11.8917 16.3553 12.0881 15.786 12.0881 15.0604V9.89961C12.0881 9.7745 12.1325 9.66815 12.2211 9.58059C12.3098 9.493 12.4175 9.44922 12.5443 9.44922H14.217C14.3436 9.44922 14.4513 9.493 14.54 9.58059C14.6288 9.66815 14.6732 9.7745 14.6732 9.89961V18.7574C14.6732 18.8825 14.6288 18.9888 14.54 19.0764C14.4513 19.164 14.3436 19.2078 14.217 19.2078H12.6773C12.538 19.2078 12.4239 19.164 12.3352 19.0764C12.2591 18.9888 12.2211 18.8825 12.2211 18.7574V17.988C11.879 18.4258 11.4545 18.7699 10.9476 19.0201C10.4407 19.2703 9.80704 19.3954 9.04674 19.3954Z"
fill="#3367CC"
/>
<path
d="M17.6397 19.2104C17.5129 19.2104 17.4052 19.1666 17.3165 19.079C17.2279 18.9914 17.1835 18.8851 17.1835 18.76V9.90221C17.1835 9.7771 17.2279 9.67075 17.3165 9.58319C17.4052 9.4956 17.5129 9.45182 17.6397 9.45182H19.2174C19.3567 9.45182 19.4644 9.4956 19.5404 9.58319C19.6292 9.67075 19.6736 9.7771 19.6736 9.90221V18.76C19.6736 18.8851 19.6292 18.9914 19.5404 19.079C19.4644 19.1666 19.3567 19.2104 19.2174 19.2104H17.6397ZM17.5636 7.8379C17.4243 7.8379 17.3102 7.80038 17.2215 7.72531C17.1454 7.63773 17.1074 7.52514 17.1074 7.38751V6.03633C17.1074 5.91122 17.1454 5.80487 17.2215 5.71731C17.3102 5.62972 17.4243 5.58594 17.5636 5.58594H19.2933C19.4327 5.58594 19.5467 5.62972 19.6354 5.71731C19.7242 5.80487 19.7686 5.91122 19.7686 6.03633V7.38751C19.7686 7.52514 19.7242 7.63773 19.6354 7.72531C19.5467 7.80038 19.4327 7.8379 19.2933 7.8379H17.5636Z"
fill="#3367CC"
/>
{/* ✨ MODIFIED outer path for animation */}
<path
className={styles.animatedLogoPath}
d="M23.0215 2.81748H2.53486V23.044H23.0215V2.81748Z"
fill="none"
stroke="#3367CC"
strokeWidth="0.75"
/>
</svg>
</div>
</div>
);
}
export function LazyNestedApp({
immediate,
...restProps
}: NestedAppProps & { immediate?: boolean }) {
const [shouldRender, setShouldRender] = useState(immediate || false);
useEffect(() => {
if (!immediate) {
startTransition(() => {
setShouldRender(true);
});
}
}, [immediate]);
if (!shouldRender) {
return null;
}
return <NestedApp {...restProps} />;
}
export function IndexAwareNestedApp(props: NestedAppProps & { immediate?: boolean }) {
const { indexing } = useIndexerContext();
if (indexing) {
return null;
}
return <LazyNestedApp {...props} />;
}
export function NestedApp({
api,
app,
components = EMPTY_ARRAY,
config,
activeTheme,
activeTone,
height,
style,
refreshVersion,
withSplashScreen = false,
className,
}: NestedAppProps) {
const rootRef = useRef<HTMLDivElement>(null);
const shadowRef = useRef(null);
const contentRootRef = useRef<Root | null>(null);
const theme = useTheme();
const toneToApply = activeTone || config?.defaultTone || theme?.activeThemeTone;
const componentRegistry = useComponentRegistry();
const parentInterceptorContext = useApiInterceptorContext();
const [initialized, setInitialized] = useState(!withSplashScreen);
const [revealAnimationFinished, setRevealAnimationFinished] = useState(false);
//TODO illesg: we should come up with something to make sure that nestedApps don't overwrite each other's mocked api endpoints
// disabled for now, as it messes up the paths of not mocked APIs (e.g. resources/{staticJsonfiles})
//const safeId = playgroundId || nestedAppId;
//const apiUrl = api ? `/${safeId.replaceAll(":", "")}` : '';
const apiUrl = "";
const mock = useMemo(() => {
if (!api) {
return undefined;
}
let apiObject = api;
if (typeof api === "string") {
try {
apiObject = JSON.parse(api.replaceAll("\n", " "));
} catch (e) {
console.error("Failed to parse API definition", e);
return undefined;
}
}
return {
...apiObject,
type: "in-memory",
// apiUrl: apiUrl + (apiObject.apiUrl || ""),
};
}, [api]);
useIsomorphicLayoutEffect(() => {
if (!shadowRef.current && rootRef.current) {
// Clone existing style and link tags
shadowRef.current = rootRef.current.attachShadow({ mode: "open" });
let style = document.createElement("style");
// since we are using shadow DOM, we need to define the layers here
// to ensure that the styles are applied correctly (the adopted styleheets setting is asynchronous, so the layer order might not be respected if we don't do this)
// MUST BE IN SYNC WITH THE STYLESHEET ORDER IN index.scss
style.innerHTML = "@layer reset, base, components, dynamic;";
shadowRef.current.appendChild(style);
// This should run once to prepare the stylesheets
const sheetPromises = Array.from(document.styleSheets).map(async (sheet) => {
// Check if the owner element has the attribute you want to skip
if (
sheet.ownerNode &&
sheet.ownerNode instanceof Element &&
sheet.ownerNode.hasAttribute("data-style-hash")
) {
return null; // Skip this stylesheet
}
// Can't access cross-origin sheets, so skip them
if (!sheet.href || sheet.href.startsWith(window.location.origin)) {
try {
// Create a new CSSStyleSheet object
const newSheet = new CSSStyleSheet();
// Get the CSS rules as text
const cssText = Array.from(sheet.cssRules)
.map((rule) => rule.cssText)
.join(" \n");
// Apply the text to the new sheet object
await newSheet.replace(cssText);
return newSheet;
} catch (e) {
// console.error('Could not process stylesheet:', sheet.href, e);
return null;
}
}
return null;
});
// When your component mounts and the shadow root is available...
void Promise.all(sheetPromises).then((sheets) => {
// Filter out any sheets that failed to load
const validSheets = sheets.filter(Boolean);
// Apply the array of constructed stylesheets to the shadow root
// This is synchronous and does not trigger new network requests
shadowRef.current.adoptedStyleSheets = validSheets;
});
}
if (!contentRootRef.current && shadowRef.current) {
contentRootRef.current = ReactDOM.createRoot(shadowRef.current);
}
}, []);
const onInit = useCallback(() => {
setInitialized(true);
}, []);
useEffect(() => {
let { errors, component, erroneousCompoundComponentName } = xmlUiMarkupToComponent(app);
if (errors.length > 0) {
component = errReportComponent(errors, "Main.xmlui", erroneousCompoundComponentName);
}
const compoundComponents: CompoundComponentDef[] = (components ?? []).map((src) => {
const isErrorReportComponent = typeof src !== "string";
if (isErrorReportComponent) {
return src;
}
let { errors, component, erroneousCompoundComponentName } = xmlUiMarkupToComponent(
src as string,
);
if (errors.length > 0) {
return errReportComponent(errors, `nested xmlui`, erroneousCompoundComponentName);
}
return component;
});
let globalProps = {
name: config?.name,
...(config?.appGlobals || {}),
apiUrl,
};
contentRootRef.current?.render(
<ErrorBoundary node={component}>
<StyleInjectionTargetContext.Provider value={shadowRef.current}>
<StyleProvider forceNew={true}>
<ApiInterceptorProvider
parentInterceptorContext={parentInterceptorContext}
key={`app-${refreshVersion}`}
interceptor={mock}
waitForApiInterceptor={true}
>
<NestedAppRoot themeStylesToReset={theme.themeStyles}>
<AppRoot
onInit={onInit}
isNested={true}
previewMode={true}
standalone={true}
trackContainerHeight={height ? "fixed" : "auto"}
node={component}
globalProps={globalProps}
defaultTheme={activeTheme || config?.defaultTheme}
defaultTone={toneToApply as ThemeTone}
contributes={{
compoundComponents,
themes: config?.themes,
}}
resources={config?.resources}
extensionManager={componentRegistry.getExtensionManager()}
/>
</NestedAppRoot>
</ApiInterceptorProvider>
</StyleProvider>
</StyleInjectionTargetContext.Provider>
</ErrorBoundary>,
);
}, [
onInit,
activeTheme,
app,
componentRegistry,
components,
config?.appGlobals,
config?.defaultTheme,
config?.name,
config?.resources,
config?.themes,
height,
mock,
parentInterceptorContext,
style,
theme.themeStyles,
toneToApply,
refreshVersion,
]);
const mountedRef = useRef(false);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
setTimeout(() => {
// Unmount the content root after a delay (and only if the component is not mounted anymore, could happen in dev mode - double useEffect call)
if (!mountedRef.current) {
contentRootRef.current?.unmount();
contentRootRef.current = null;
}
}, 0);
};
}, []);
const animationFinished = useCallback(() => {
setRevealAnimationFinished(true);
}, []);
const shouldAnimate = withSplashScreen && !revealAnimationFinished;
return (
<div className={classnames(styles.nestedAppPlaceholder, className)}>
{shouldAnimate && <AnimatedLogo />}
<div
ref={rootRef}
onTransitionEnd={animationFinished}
className={classnames(styles.nestedAppRoot, {
[styles.shouldAnimate]: shouldAnimate,
[styles.initialized]: initialized,
})}
/>
</div>
);
}
function NestedAppRoot({
children,
themeStylesToReset,
}: {
children: ReactNode;
themeStylesToReset?: Record<string, string>;
}) {
// css variables are leaking into to shadow dom, so we reset them here
const themeVarReset = useMemo(() => {
const vars = {};
Object.keys(themeStylesToReset).forEach((key) => {
vars[key] = "initial";
});
return vars;
}, [themeStylesToReset]);
const resetClassName = useStyles(themeVarReset, { prepend: true });
return (
<div className={classnames(resetClassName, styles.shadowRoot)} id={"nested-app-root"}>
<div className={styles.content}>{children}</div>
</div>
);
}
```
--------------------------------------------------------------------------------
/xmlui/src/components/FileInput/FileInput.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { test, expect } from "../../testing/fixtures";
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test("component renders with basic props", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput/>`);
const driver = await createFileInputDriver();
await expect(driver.component).toBeVisible();
});
test("component displays browse button", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput buttonLabel="Choose File"/>`);
const driver = await createFileInputDriver();
await expect(driver.getBrowseButton()).toBeVisible();
await expect(driver.getBrowseButton()).toContainText("Choose File");
});
test("component displays placeholder text", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput placeholder="Select a file..."/>`);
const driver = await createFileInputDriver();
const placeholder = await driver.getPlaceholder();
expect(placeholder).toBe("Select a file...");
});
test("component handles disabled state", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput enabled="false"/>`);
const driver = await createFileInputDriver();
await expect(driver.getBrowseButton()).toBeDisabled();
expect(await driver.isEnabled()).toBe(false);
});
test("component supports multiple file selection", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput multiple="true"/>`);
const driver = await createFileInputDriver();
expect(await driver.isMultiple()).toBe(true);
});
test("component supports directory selection", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput directory="true"/>`);
const driver = await createFileInputDriver();
expect(await driver.isDirectory()).toBe(true);
});
test("component accepts specific file types", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput acceptsFileType="image/*,application/pdf"/>`);
const driver = await createFileInputDriver();
const acceptedTypes = await driver.getAcceptedFileTypes();
expect(acceptedTypes).toBe("image/*,application/pdf");
});
test("component accepts file type array", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput acceptsFileType="['.jpg', '.png', '.pdf']"/>`);
const driver = await createFileInputDriver();
const acceptedTypes = await driver.getAcceptedFileTypes();
expect(acceptedTypes).toContain(".jpg");
});
// =============================================================================
// ACCESSIBILITY TESTS (REQUIRED)
// =============================================================================
test("component has correct accessibility attributes", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput label="Upload Document"/>`);
const driver = await createFileInputDriver();
await expect(driver.getBrowseButton()).toHaveRole("button");
await expect(driver.getHiddenInput()).toHaveAttribute("type", "file");
});
test("component is keyboard accessible", async ({ page, initTestBed, createFileInputDriver }) => {
await initTestBed(`
<VStack>
<FileInput testId="fileInput" label="Input" />
</VStack>
`);
const driver = await createFileInputDriver("fileInput");
await driver.getTextBox().focus();
await expect(driver.getTextBox()).toBeFocused();
});
test("component supports tab navigation", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`
<VStack>
<FileInput testId="fileInput" label="Input" />
</VStack>
`);
const driver = await createFileInputDriver("fileInput");
await driver.getTextBox().focus();
await expect(driver.getTextBox()).toBeFocused();
await driver.getTextBox().press("Tab");
await expect(driver.getBrowseButton()).toBeVisible();
await expect(driver.getBrowseButton()).not.toBeDisabled();
await expect(driver.getBrowseButton()).toBeFocused();
});
test("component has hidden file input for screen readers", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput/>`);
const driver = await createFileInputDriver();
const hiddenInput = driver.getHiddenInput();
await expect(hiddenInput).toBeAttached();
await expect(hiddenInput).toHaveAttribute("type", "file");
});
test("component textbox is readonly for accessibility", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput/>`);
const driver = await createFileInputDriver();
expect(await driver.hasReadOnlyAttribute()).toBe(true);
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test("component applies theme variables correctly", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput/>`, {
testThemeVars: {
"backgroundColor-Button": "rgb(255, 0, 0)",
},
});
const driver = await createFileInputDriver();
// FileInput uses Button themes, so check the button
await expect(driver.getBrowseButton()).toHaveCSS("background-color", "rgb(255, 0, 0)");
});
test("component shows validation states", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput validationStatus="error"/>`);
const driver = await createFileInputDriver();
// Validation should be visible in the component
await expect(driver.component).toBeVisible();
await expect(driver.getTextBox()).toBeVisible();
});
test("component supports different button variants", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput variant="outline"/>`);
const driver = await createFileInputDriver();
// Check that variant prop is passed through
await expect(driver.getBrowseButton()).toBeVisible();
await expect(driver.getBrowseButton()).toContainText("Browse");
});
test("component supports different button sizes", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput buttonSize="lg"/>`);
const driver = await createFileInputDriver();
await expect(driver.getBrowseButton()).toHaveClass(/lg/);
});
test("component supports button positioning", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput buttonPosition="start"/>`);
const driver = await createFileInputDriver();
await expect(driver.getContainer()).toHaveClass(/buttonStart/);
});
// =============================================================================
// EDGE CASE TESTS (CRITICAL)
// =============================================================================
test("component handles null and undefined props gracefully", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput/>`);
const driver = await createFileInputDriver();
await expect(driver.component).toBeVisible();
expect(await driver.getSelectedFiles()).toBe("");
});
test("component handles empty acceptsFileType", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`<FileInput acceptsFileType=""/>`);
const driver = await createFileInputDriver();
expect(await driver.getAcceptedFileTypes()).toBe("");
});
test("component handles special characters in placeholder", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput placeholder="Select file with émojis 🚀 & quotes"/>`);
const driver = await createFileInputDriver();
const placeholder = await driver.getPlaceholder();
expect(placeholder).toBe("Select file with émojis 🚀 & quotes");
});
test("component handles conflicting multiple and directory props", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput multiple="false" directory="true"/>`);
const driver = await createFileInputDriver();
// Directory mode should enable multiple files
expect(await driver.isDirectory()).toBe(true);
expect(await driver.isMultiple()).toBe(true);
});
test("component handles empty file selection gracefully", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`<FileInput/>`);
const driver = await createFileInputDriver();
expect(await driver.getSelectedFiles()).toBe("");
});
// =============================================================================
// PERFORMANCE TESTS
// =============================================================================
test("component handles rapid button clicks efficiently", async ({
initTestBed,
createFileInputDriver,
}) => {
const { testStateDriver } = await initTestBed(`
<FileInput onFocus="testState = ++testState || 1"/>
`);
const driver = await createFileInputDriver();
// Multiple rapid clicks should not cause issues
await driver.getBrowseButton().click();
await driver.getBrowseButton().click();
await driver.getBrowseButton().click();
await expect(driver.component).toBeVisible();
});
// =============================================================================
// INTEGRATION TESTS
// =============================================================================
test("component works correctly in different layout contexts", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`
<VStack>
<FileInput label="Layout Test"/>
</VStack>
`);
const driver = await createFileInputDriver();
await expect(driver.component).toBeVisible();
});
test("component works in form context", async ({ initTestBed, createFileInputDriver }) => {
await initTestBed(`
<Form>
<FileInput label="Upload File" required="true"/>
</Form>
`);
const driver = await createFileInputDriver();
await expect(driver.component).toBeVisible();
});
test("gotFocus event fires on focus", async ({ initTestBed, page, createFileInputDriver }) => {
const { testStateDriver } = await initTestBed(`
<FileInput testId="fileInput" onGotFocus="testState = 'focused'" />
`);
const driver = await createFileInputDriver("fileInput");
await driver.getTextBox().focus();
await expect.poll(testStateDriver.testState).toEqual("focused");
});
test("gotFocus event fires on label focus", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<FileInput testId="fileInput" onGotFocus="testState = 'focused'" label="test" />
`);
await page.getByText("test").click();
await expect.poll(testStateDriver.testState).toEqual("focused");
});
test("lostFocus event fires on blue", async ({ initTestBed, page, createFileInputDriver }) => {
const { testStateDriver } = await initTestBed(`
<FileInput testId="fileInput" onLostFocus="testState = 'blurred'" />
`);
const driver = await createFileInputDriver("fileInput");
await driver.getTextBox().focus();
await driver.getTextBox().blur();
await expect.poll(testStateDriver.testState).toEqual("blurred");
});
test("component supports custom button templates", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`
<FileInput>
<property name="buttonIcon">
<Icon name="upload"/>
</property>
</FileInput>
`);
const driver = await createFileInputDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getBrowseButton()).toContainText("Browse");
});
test("component handles label positioning correctly", async ({
initTestBed,
createFileInputDriver,
}) => {
await initTestBed(`
<FileInput
label="Upload Document"
labelPosition="top"
labelWidth="100px"
/>
`);
const driver = await createFileInputDriver();
await expect(driver.component).toBeVisible();
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test("input has correct width in px", async ({ page, initTestBed }) => {
await initTestBed(`<FileInput 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(`<FileInput 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(`<FileInput 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(`<FileInput width="50%" label="test" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
```
--------------------------------------------------------------------------------
/docs/content/components/Checkbox.md:
--------------------------------------------------------------------------------
```markdown
# Checkbox [#checkbox]
`Checkbox` allows users to make binary choices with a clickable box that shows checked/unchecked states. It's essential for settings, preferences, multi-select lists, and accepting terms or conditions.
**Key features:**
- **Flexible labeling**: Position labels on any side and support custom label templates
- **Validation support**: Built-in validation states for form error handling
- **Indeterminate state**: Special visual state for mixed selections (useful for "select all" scenarios)
To bind data to a `Checkbox`, use the XMLUI [Forms infrastructure](/forms).
## Checkbox Values [#checkbox-values]
The `initialValue` and `value` properties of the checkbox are transformed to a Boolean value to display the checked (`true`) or unchecked (`false`) state with this logic:
- `null` and `undefined` go to `false`.
- If the property is Boolean, the property value is used as is.
- If it is a number, `NaN` and `0` result in `false`; other values represent `true`.
- If the property is a string, the empty string and the literal "false" string result in `false`; others result in `true`.
- The empty array value goes to `false`; other array values result in `true`.
- Object values with no properties result in `false`; other values represent `true`.
## Use children as Content Template [#use-children-as-content-template]
The [inputTemplate](#inputtemplate) property can be replaced by setting the item template component directly as the Checkbox's child.
In the following example, the two Checkbox are functionally the same:
```xmlui copy
<App>
<!-- This is the same -->
<Checkbox>
<property name="inputTemplate">
<Text>Template</Text>
</property>
</Checkbox>
<!-- As this -->
<Checkbox>
<Text>Template</Text>
</Checkbox>
</App>
```
## Properties [#properties]
### `autoFocus` (default: false) [#autofocus-default-false]
If this property is set to `true`, the component gets the focus automatically when displayed.
### `enabled` (default: true) [#enabled-default-true]
This boolean property value indicates whether the component responds to user events (`true`) or not (`false`).
```xmlui-pg copy display {4-5, 9-10} name="Example: enabled"
<App>
Enabled checkboxes:
<HStack>
<Checkbox initialValue="true" enabled="true" />
<Checkbox initialValue="false" enabled="true" />
</HStack>
Disabled checkboxes:
<HStack>
<Checkbox initialValue="true" enabled="false" />
<Checkbox initilaValue="false" enabled="false" />
</HStack>
</App>
```
### `indeterminate` (default: false) [#indeterminate-default-false]
The `true` value of this property signals that the component is in an _intedeterminate state_.
This prop is commonly used if there are several other checkboxes linked to one checkbox and some items in that group of checkboxes are in a mixed state: at least one item has a different value compared to the rest.
The following sample binds the state of two checkboxes to one and updates the state of the top checkbox accordingly. When the states of the bound checkboxes are different, the top checkbox is set to indeterminate:
```xmlui-pg copy display name="Example: indeterminate"
---app copy display {4}
<App var.indeterminate="{false}">
<Checkbox
label="Indeterminate Checkbox"
indeterminate="{indeterminate}"
initialValue="{cb1.value}"
readOnly="true" />
<ChangeListener
listenTo="{ { v1: cb1.value, v2: cb2.value } }"
onDidChange="indeterminate = cb1.value !== cb2.value" />
Group of checkboxes:
<HStack>
<Checkbox label="Checkbox #1" id="cb1" initialValue="true" />
<Checkbox label="Checkbox #2" id="cb2" initialValue="false" />
</HStack>
</App>
---desc
Try this sample by clicking the bottom group of checkboxes.
```
### `initialValue` (default: false) [#initialvalue-default-false]
This property sets the component's initial value.
### `inputTemplate` [#inputtemplate]
This property is used to define a custom checkbox input template
### `readOnly` (default: false) [#readonly-default-false]
Set this property to `true` to disallow changing the component value.
```xmlui-pg copy {3} display name="Example: readOnly"
<App>
<Checkbox readOnly="true" label="Checked" initialValue="true" />
<Checkbox readOnly="true" label="Unchecked" intialValue="false" />
</App>
```
### `required` (default: false) [#required-default-false]
Set this property to `true` to indicate it must have a value before submitting the containing form.
### `validationStatus` (default: "none") [#validationstatus-default-none]
This property allows you to set the validation status of the input component.
Available values:
| Value | Description |
| --- | --- |
| `valid` | Visual indicator for an input that is accepted |
| `warning` | Visual indicator for an input that produced a warning |
| `error` | Visual indicator for an input that produced an error |
## Events [#events]
### `click` [#click]
This event is triggered when the Checkbox is clicked.
### `didChange` [#didchange]
This event is triggered when value of Checkbox has changed.
```xmlui-pg copy display name="Example: didChange"
<App verticalAlignment="center" var.changes="">
<Checkbox label="Changeable" onDidChange="changes += '+'" />
<Checkbox
label="Readonly"
readOnly="true"
onDidChange="changes += '-'" />
<Text value="Changes: {changes}" />
</App>
```
### `gotFocus` [#gotfocus]
This event is triggered when the Checkbox has received the focus.
Click the `Checkbox` in the example demo to change the label text. Note how clicking elsewhere resets the text to the original.
```xmlui-pg copy display name="Example: gotFocus/lostFocus"
<App var.focused="{false}" verticalAlignment="center">
<Checkbox
value="true"
onGotFocus="focused = true"
onLostFocus="focused = false"
/>
<Text value="{focused === true ? 'I am focused!' : 'I have lost the focus!'}" />
</App>
```
### `lostFocus` [#lostfocus]
This event is triggered when the Checkbox has lost the focus.
(See the example above)
## Exposed Methods [#exposed-methods]
### `setValue` [#setvalue]
This method sets the current value of the Checkbox.
**Signature**: `set value(value: boolean): void`
- `value`: The new value to set for the checkbox.
You can use this method to set the checkbox's current value programmatically (`true`: checked, `false`: unchecked).
```xmlui-pg copy {10,13,15} display name="Example: value and setValue"
<App var.changes="">
<Checkbox
id="checkbox"
readOnly="true"
label="This checkbox can be set only programmatically"
onDidChange="changes += '+'" />
<HStack>
<Button
label="Check"
onClick="checkbox.setValue(true)" />
<Button
label="Uncheck"
onClick="checkbox.setValue(false)" />
</HStack>
<Text>The checkbox is {checkbox.value ? "checked" : "unchecked"}</Text>
<Text value="Changes: {changes}" />
</App>
```
### `value` [#value]
This method returns the current value of the Checkbox.
**Signature**: `get value(): boolean`
You can query this read-only API property to query the checkbox's current value (`true`: checked, `false`: unchecked).
See an example in the `setValue` API method.
## Parts [#parts]
The component has some parts that can be styled through layout properties and theme variables separately:
- **`input`**: The checkbox input area.
- **`label`**: The label displayed for the checkbox.
## Styling [#styling]
### Theme Variables [#theme-variables]
| Variable | Default Value (Light) | Default Value (Dark) |
| --- | --- | --- |
| [backgroundColor](../styles-and-themes/common-units/#color)-Checkbox--default | *none* | *none* |
| [backgroundColor](../styles-and-themes/common-units/#color)-Checkbox--disabled | $color-surface-200 | $color-surface-200 |
| [backgroundColor](../styles-and-themes/common-units/#color)-Checkbox--disabled | $color-surface-200 | $color-surface-200 |
| [backgroundColor](../styles-and-themes/common-units/#color)-Checkbox--error | *none* | *none* |
| [backgroundColor](../styles-and-themes/common-units/#color)-Checkbox--success | *none* | *none* |
| [backgroundColor](../styles-and-themes/common-units/#color)-Checkbox--warning | *none* | *none* |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox | $color-primary-500 | $color-primary-500 |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox | $color-primary-500 | $color-primary-500 |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox--error | $borderColor-Checkbox--error | $borderColor-Checkbox--error |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox--error | $borderColor-Checkbox--error | $borderColor-Checkbox--error |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox--success | $borderColor-Checkbox--success | $borderColor-Checkbox--success |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox--success | $borderColor-Checkbox--success | $borderColor-Checkbox--success |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox--warning | $borderColor-Checkbox--warning | $borderColor-Checkbox--warning |
| [backgroundColor](../styles-and-themes/common-units/#color)-checked-Checkbox--warning | $borderColor-Checkbox--warning | $borderColor-Checkbox--warning |
| [backgroundColor](../styles-and-themes/common-units/#color)-indicator-Checkbox | $backgroundColor-primary | $backgroundColor-primary |
| [borderColor](../styles-and-themes/common-units/#color)-Checkbox--default | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Checkbox--default--hover | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Checkbox--disabled | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Checkbox--error | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Checkbox--success | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Checkbox--warning | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox | $color-primary-500 | $color-primary-500 |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox | $color-primary-500 | $color-primary-500 |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox--error | $borderColor-Checkbox--error | $borderColor-Checkbox--error |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox--error | $borderColor-Checkbox--error | $borderColor-Checkbox--error |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox--success | $borderColor-Checkbox--success | $borderColor-Checkbox--success |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox--success | $borderColor-Checkbox--success | $borderColor-Checkbox--success |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox--warning | $borderColor-Checkbox--warning | $borderColor-Checkbox--warning |
| [borderColor](../styles-and-themes/common-units/#color)-checked-Checkbox--warning | $borderColor-Checkbox--warning | $borderColor-Checkbox--warning |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Checkbox--default | *none* | *none* |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Checkbox--error | *none* | *none* |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Checkbox--success | *none* | *none* |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Checkbox--warning | *none* | *none* |
| [outlineColor](../styles-and-themes/common-units/#color)-Checkbox--default--focus | *none* | *none* |
| [outlineColor](../styles-and-themes/common-units/#color)-Checkbox--error--focus | *none* | *none* |
| [outlineColor](../styles-and-themes/common-units/#color)-Checkbox--success--focus | *none* | *none* |
| [outlineColor](../styles-and-themes/common-units/#color)-Checkbox--warning--focus | *none* | *none* |
| [outlineOffset](../styles-and-themes/common-units/#size)-Checkbox--default--focus | *none* | *none* |
| [outlineOffset](../styles-and-themes/common-units/#size)-Checkbox--error--focus | *none* | *none* |
| [outlineOffset](../styles-and-themes/common-units/#size)-Checkbox--success--focus | *none* | *none* |
| [outlineOffset](../styles-and-themes/common-units/#size)-Checkbox--warning--focus | *none* | *none* |
| [outlineStyle](../styles-and-themes/common-units/#border)-Checkbox--default--focus | *none* | *none* |
| [outlineStyle](../styles-and-themes/common-units/#border)-Checkbox--error--focus | *none* | *none* |
| [outlineStyle](../styles-and-themes/common-units/#border)-Checkbox--success--focus | *none* | *none* |
| [outlineStyle](../styles-and-themes/common-units/#border)-Checkbox--warning--focus | *none* | *none* |
| [outlineWidth](../styles-and-themes/common-units/#size)-Checkbox--default--focus | *none* | *none* |
| [outlineWidth](../styles-and-themes/common-units/#size)-Checkbox--error--focus | *none* | *none* |
| [outlineWidth](../styles-and-themes/common-units/#size)-Checkbox--success--focus | *none* | *none* |
| [outlineWidth](../styles-and-themes/common-units/#size)-Checkbox--warning--focus | *none* | *none* |
```
--------------------------------------------------------------------------------
/xmlui/src/components/TextArea/TextArea.module.scss:
--------------------------------------------------------------------------------
```scss
@use "../../components-core/theming/themes" as t;
// --- This code snippet is required to collect the theme variables used in this module
$themeVars: (
);
@function createThemeVar($componentVariable) {
$themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
@return t.getThemeVar($themeVars, $componentVariable);
}
$componentName: "TextArea";
$themeVars: t.composePaddingVars($themeVars, $componentName);
// Variables for default variant
$borderRadius-TextArea--default: createThemeVar("Input:borderRadius-#{$componentName}--default");
$borderColor-TextArea--default: createThemeVar("Input:borderColor-#{$componentName}--default");
$borderWidth-TextArea--default: createThemeVar("Input:borderWidth-#{$componentName}--default");
$borderStyle-TextArea--default: createThemeVar("Input:borderStyle-#{$componentName}--default");
$fontSize-TextArea--default: createThemeVar("Input:fontSize-#{$componentName}--default");
$backgroundColor-TextArea--default: createThemeVar("Input:backgroundColor-#{$componentName}--default");
$boxShadow-TextArea--default: createThemeVar("Input:boxShadow-#{$componentName}--default");
$textColor-TextArea--default: createThemeVar("Input:textColor-#{$componentName}--default");
$borderColor-TextArea--default--hover: createThemeVar("Input:borderColor-#{$componentName}--default--hover");
$backgroundColor-TextArea--default--hover: createThemeVar("Input:backgroundColor-#{$componentName}--default--hover");
$boxShadow-TextArea--default--hover: createThemeVar("Input:boxShadow-#{$componentName}--default--hover");
$textColor-TextArea--default--hover: createThemeVar("Input:textColor-#{$componentName}--default--hover");
$borderColor-TextArea--default--focus: createThemeVar("Input:borderColor-#{$componentName}--default--focus");
$backgroundColor-TextArea--default--focus: createThemeVar("Input:backgroundColor-#{$componentName}--default--focus");
$boxShadow-TextArea--default--focus: createThemeVar("Input:boxShadow-#{$componentName}--default--focus");
$textColor-TextArea--default--focus: createThemeVar("Input:textColor-#{$componentName}--default--focus");
$outlineWidth-TextArea--default--focus: createThemeVar("Input:outlineWidth-#{$componentName}--default--focus");
$outlineColor-TextArea--default--focus: createThemeVar("Input:outlineColor-#{$componentName}--default--focus");
$outlineStyle-TextArea--default--focus: createThemeVar("Input:outlineStyle-#{$componentName}--default--focus");
$outlineOffset-TextArea--default--focus: createThemeVar("Input:outlineOffset-#{$componentName}--default--focus");
$textColor-placeholder-TextArea--default: createThemeVar("Input:textColor-placeholder-#{$componentName}--default");
$fontSize-placeholder-TextArea--default: createThemeVar("Input:fontSize-placeholder-#{$componentName}--default");
// Variables for error variant
$borderRadius-TextArea--error: createThemeVar("Input:borderRadius-#{$componentName}--error");
$borderColor-TextArea--error: createThemeVar("Input:borderColor-#{$componentName}--error");
$borderWidth-TextArea--error: createThemeVar("Input:borderWidth-#{$componentName}--error");
$borderStyle-TextArea--error: createThemeVar("Input:borderStyle-#{$componentName}--error");
$fontSize-TextArea--error: createThemeVar("Input:fontSize-#{$componentName}--error");
$backgroundColor-TextArea--error: createThemeVar("Input:backgroundColor-#{$componentName}--error");
$boxShadow-TextArea--error: createThemeVar("Input:boxShadow-#{$componentName}--error");
$textColor-TextArea--error: createThemeVar("Input:textColor-#{$componentName}--error");
$borderColor-TextArea--error--hover: createThemeVar("Input:borderColor-#{$componentName}--error--hover");
$backgroundColor-TextArea--error--hover: createThemeVar("Input:backgroundColor-#{$componentName}--error--hover");
$boxShadow-TextArea--error--hover: createThemeVar("Input:boxShadow-#{$componentName}--error--hover");
$textColor-TextArea--error--hover: createThemeVar("Input:textColor-#{$componentName}--error--hover");
$borderColor-TextArea--error--focus: createThemeVar("Input:borderColor-#{$componentName}--error--focus");
$backgroundColor-TextArea--error--focus: createThemeVar("Input:backgroundColor-#{$componentName}--error--focus");
$boxShadow-TextArea--error--focus: createThemeVar("Input:boxShadow-#{$componentName}--error--focus");
$textColor-TextArea--error--focus: createThemeVar("Input:textColor-#{$componentName}--error--focus");
$outlineWidth-TextArea--error--focus: createThemeVar("Input:outlineWidth-#{$componentName}--error--focus");
$outlineColor-TextArea--error--focus: createThemeVar("Input:outlineColor-#{$componentName}--error--focus");
$outlineStyle-TextArea--error--focus: createThemeVar("Input:outlineStyle-#{$componentName}--error--focus");
$outlineOffset-TextArea--error--focus: createThemeVar("Input:outlineOffset-#{$componentName}--error--focus");
$textColor-placeholder-TextArea--error: createThemeVar("Input:textColor-placeholder-#{$componentName}--error");
$fontSize-placeholder-TextArea--error: createThemeVar("Input:fontSize-placeholder-#{$componentName}--error");
// Variables for warning variant
$borderRadius-TextArea--warning: createThemeVar("Input:borderRadius-#{$componentName}--warning");
$borderColor-TextArea--warning: createThemeVar("Input:borderColor-#{$componentName}--warning");
$borderWidth-TextArea--warning: createThemeVar("Input:borderWidth-#{$componentName}--warning");
$borderStyle-TextArea--warning: createThemeVar("Input:borderStyle-#{$componentName}--warning");
$fontSize-TextArea--warning: createThemeVar("Input:fontSize-#{$componentName}--warning");
$backgroundColor-TextArea--warning: createThemeVar("Input:backgroundColor-#{$componentName}--warning");
$boxShadow-TextArea--warning: createThemeVar("Input:boxShadow-#{$componentName}--warning");
$textColor-TextArea--warning: createThemeVar("Input:textColor-#{$componentName}--warning");
$borderColor-TextArea--warning--hover: createThemeVar("Input:borderColor-#{$componentName}--warning--hover");
$backgroundColor-TextArea--warning--hover: createThemeVar("Input:backgroundColor-#{$componentName}--warning--hover");
$boxShadow-TextArea--warning--hover: createThemeVar("Input:boxShadow-#{$componentName}--warning--hover");
$textColor-TextArea--warning--hover: createThemeVar("Input:textColor-#{$componentName}--warning--hover");
$borderColor-TextArea--warning--focus: createThemeVar("Input:borderColor-#{$componentName}--warning--focus");
$backgroundColor-TextArea--warning--focus: createThemeVar("Input:backgroundColor-#{$componentName}--warning--focus");
$boxShadow-TextArea--warning--focus: createThemeVar("Input:boxShadow-#{$componentName}--warning--focus");
$textColor-TextArea--warning--focus: createThemeVar("Input:textColor-#{$componentName}--warning--focus");
$outlineWidth-TextArea--warning--focus: createThemeVar("Input:outlineWidth-#{$componentName}--warning--focus");
$outlineColor-TextArea--warning--focus: createThemeVar("Input:outlineColor-#{$componentName}--warning--focus");
$outlineStyle-TextArea--warning--focus: createThemeVar("Input:outlineStyle-#{$componentName}--warning--focus");
$outlineOffset-TextArea--warning--focus: createThemeVar("Input:outlineOffset-#{$componentName}--warning--focus");
$textColor-placeholder-TextArea--warning: createThemeVar("Input:textColor-placeholder-#{$componentName}--warning");
$fontSize-placeholder-TextArea--warning: createThemeVar("Input:fontSize-placeholder-#{$componentName}--warning");
// Variables for success variant
$borderRadius-TextArea--success: createThemeVar("Input:borderRadius-#{$componentName}--success");
$borderColor-TextArea--success: createThemeVar("Input:borderColor-#{$componentName}--success");
$borderWidth-TextArea--success: createThemeVar("Input:borderWidth-#{$componentName}--success");
$borderStyle-TextArea--success: createThemeVar("Input:borderStyle-#{$componentName}--success");
$fontSize-TextArea--success: createThemeVar("Input:fontSize-#{$componentName}--success");
$backgroundColor-TextArea--success: createThemeVar("Input:backgroundColor-#{$componentName}--success");
$boxShadow-TextArea--success: createThemeVar("Input:boxShadow-#{$componentName}--success");
$textColor-TextArea--success: createThemeVar("Input:textColor-#{$componentName}--success");
$borderColor-TextArea--success--hover: createThemeVar("Input:borderColor-#{$componentName}--success--hover");
$backgroundColor-TextArea--success--hover: createThemeVar("Input:backgroundColor-#{$componentName}--success--hover");
$boxShadow-TextArea--success--hover: createThemeVar("Input:boxShadow-#{$componentName}--success--hover");
$textColor-TextArea--success--hover: createThemeVar("Input:textColor-#{$componentName}--success--hover");
$borderColor-TextArea--success--focus: createThemeVar("Input:borderColor-#{$componentName}--success--focus");
$backgroundColor-TextArea--success--focus: createThemeVar("Input:backgroundColor-#{$componentName}--success--focus");
$boxShadow-TextArea--success--focus: createThemeVar("Input:boxShadow-#{$componentName}--success--focus");
$textColor-TextArea--success--focus: createThemeVar("Input:textColor-#{$componentName}--success--focus");
$outlineWidth-TextArea--success--focus: createThemeVar("Input:outlineWidth-#{$componentName}--success--focus");
$outlineColor-TextArea--success--focus: createThemeVar("Input:outlineColor-#{$componentName}--success--focus");
$outlineStyle-TextArea--success--focus: createThemeVar("Input:outlineStyle-#{$componentName}--success--focus");
$outlineOffset-TextArea--success--focus: createThemeVar("Input:outlineOffset-#{$componentName}--success--focus");
$textColor-placeholder-TextArea--success: createThemeVar("Input:textColor-placeholder-#{$componentName}--success");
$fontSize-placeholder-TextArea--success: createThemeVar("Input:fontSize-placeholder-#{$componentName}--success");
// Variables for @layer section
$backgroundColor-TextArea--disabled: createThemeVar("Input:backgroundColor-#{$componentName}--disabled");
$textColor-TextArea--disabled: createThemeVar("Input:textColor-#{$componentName}--disabled");
$borderColor-TextArea--disabled: createThemeVar("Input:borderColor-#{$componentName}--disabled");
// --- CSS properties of a particular Textarea variant
@mixin variant($variantName) {
border-radius: createThemeVar("Input:borderRadius-#{$componentName}--#{$variantName}");
border-color: createThemeVar("Input:borderColor-#{$componentName}--#{$variantName}");
border-width: createThemeVar("Input:borderWidth-#{$componentName}--#{$variantName}");
border-style: createThemeVar("Input:borderStyle-#{$componentName}--#{$variantName}");
font-size: createThemeVar("Input:fontSize-#{$componentName}--#{$variantName}");
background-color: createThemeVar("Input:backgroundColor-#{$componentName}--#{$variantName}");
box-shadow: createThemeVar("Input:boxShadow-#{$componentName}--#{$variantName}");
color: createThemeVar("Input:textColor-#{$componentName}--#{$variantName}");
&:hover {
border-color: createThemeVar("Input:borderColor-#{$componentName}--#{$variantName}--hover");
background-color: createThemeVar("Input:backgroundColor-#{$componentName}--#{$variantName}--hover"
);
box-shadow: createThemeVar("Input:boxShadow-#{$componentName}--#{$variantName}--hover");
color: createThemeVar("Input:textColor-#{$componentName}--#{$variantName}--hover");
}
&:focus-within {
border-color: createThemeVar("Input:borderColor-#{$componentName}--#{$variantName}--focus");
background-color: createThemeVar("Input:backgroundColor-#{$componentName}--#{$variantName}--focus"
);
box-shadow: createThemeVar("Input:boxShadow-#{$componentName}--#{$variantName}--focus");
color: createThemeVar("Input:textColor-#{$componentName}--#{$variantName}--focus");
}
&:focus-visible {
outline-width: createThemeVar("Input:outlineWidth-#{$componentName}--#{$variantName}--focus");
outline-color: createThemeVar("Input:outlineColor-#{$componentName}--#{$variantName}--focus");
outline-style: createThemeVar("Input:outlineStyle-#{$componentName}--#{$variantName}--focus");
outline-offset: createThemeVar("Input:outlineOffset-#{$componentName}--#{$variantName}--focus");
}
&::placeholder {
color: createThemeVar("Input:textColor-placeholder-#{$componentName}--#{$variantName}");
font-size: createThemeVar("Input:fontSize-placeholder-#{$componentName}--#{$variantName}");
}
}
@layer components {
.textarea {
display: block;
width: 100%;
line-height: 1.5em;
outline: none;
resize: none;
border-style: solid;
border-width: 1px;
transition:
border 0.1s ease-in-out,
box-shadow 0.1s ease-in-out,
background 0.1s ease-in-out;
@include t.paddingVars($themeVars, $componentName);
@include variant("default");
&.error {
@include variant("error");
}
&.warning {
@include variant("warning");
}
&.valid {
@include variant("success");
}
&:is(:disabled) {
cursor: not-allowed;
background-color: $backgroundColor-TextArea--disabled;
color: $textColor-TextArea--disabled;
border-color: $borderColor-TextArea--disabled;
}
}
.resizeHorizontal {
resize: horizontal;
}
.resizeVertical {
resize: vertical;
}
.resizeBoth {
resize: both;
}
}
// --- We export the theme variables to add them to the component renderer
:export {
themeVars: t.json-stringify($themeVars);
}
```
--------------------------------------------------------------------------------
/xmlui/src/components/FormItem/FormItem.tsx:
--------------------------------------------------------------------------------
```typescript
import styles from "./FormItem.module.scss";
import { createComponentRenderer } from "../../components-core/renderers";
import { parseScssVar } from "../../components-core/theming/themeVars";
import { useMemo } from "react";
import {
defaultValidationMode,
formControlTypesMd,
validationModeMd,
validationSeverityValues,
type FormItemValidations,
} from "../Form/FormContext";
import {
createMetadata,
d,
dAutoFocus,
dComponent,
dEnabled,
dInitialValue,
dLabel,
dLabelPosition,
dLabelWidth,
dRequired,
} from "../metadata-helpers";
import { parseSeverity } from "./Validations";
import { CustomFormItem, FormItem, defaultProps } from "./FormItemNative";
import { MemoizedItem } from "../container-helpers";
const COMP = "FormItem";
// NOTE: We need to filter the "none" value out so that it doesn't show up in the docs.
const filteredValidationSeverityValues = validationSeverityValues.filter(
(value) => value !== "none",
);
export const FormItemMd = createMetadata({
status: "stable",
allowArbitraryProps: true,
description:
"`FormItem` wraps individual input controls within a `Form`, providing data " +
"binding, validation, labeling, and layout functionality. It connects form " +
"controls to the parent form's data model and handles validation feedback " +
"automatically. " +
"**Note:** `FormItem` must be used inside a `Form` component.",
props: {
bindTo: {
description:
"This property binds a particular input field to one of the attributes of the \`Form\` data. " +
"It names the property of the form's \`data\` data to get the input's initial value." +
"When the field is saved, its value will be stored in the \`data\` property with this name. " +
"If the property is not set, the input will be bound to an internal data field but not submitted.",
},
autoFocus: dAutoFocus(),
label: dLabel(),
labelPosition: dLabelPosition(),
labelWidth: dLabelWidth(COMP),
labelBreak: {
description:
`This boolean value indicates if the label can be split into multiple lines if it would ` +
`overflow the available label width.`,
type: "boolean",
defaultValue: defaultProps.labelBreak,
},
enabled: dEnabled(),
type: {
description:
`This property is used to determine the specific input control the FormItem will wrap ` +
`around. Note that the control names start with a lowercase letter and map to input ` +
`components found in XMLUI.`,
availableValues: formControlTypesMd,
defaultValue: defaultProps.type,
valueType: "string",
},
customValidationsDebounce: {
description: `This optional number prop determines the time interval between two runs of a custom validation.`,
type: "number",
defaultValue: defaultProps.customValidationsDebounce,
},
validationMode: {
description:
`This property sets what kind of validation mode or strategy to employ for a particular ` +
`input field.`,
availableValues: validationModeMd,
defaultValue: defaultValidationMode,
},
initialValue: dInitialValue(),
required: dRequired(),
requiredInvalidMessage: {
description:
"This optional string property is used to customize the message that is displayed if the " +
"field is not filled in. If not defined, the default message is used.",
valueType: "string",
},
minLength: {
description:
"This property sets the minimum length of the input value. If the value is not set, " +
"no minimum length check is done.",
valueType: "number",
},
maxLength: {
description:
"This property sets the maximum length of the input value. If the value is not set, " +
"no maximum length check is done.",
valueType: "number",
},
maxTextLength: {
description:
"The maximum length of the text in the input field. If this value is not set, " +
"no maximum length constraint is set for the input field.",
valueType: "number",
},
lengthInvalidMessage: {
description:
"This optional string property is used to customize the message that is displayed on a failed " +
"length check: [minLength](#minlength) or [maxLength](#maxlength).",
valueType: "string",
},
lengthInvalidSeverity: {
description: `This property sets the severity level of the length validation.`,
valueType: "string",
availableValues: filteredValidationSeverityValues,
defaultValue: "error",
},
minValue: {
description:
"The minimum value of the input. If this value is not specified, no minimum " +
"value check is done.",
valueType: "number",
},
maxValue: {
description:
"The maximum value of the input. If this value is not specified, no maximum " +
"value check is done.",
valueType: "number",
},
rangeInvalidMessage: {
description:
`This optional string property is used to customize the message that is displayed when ` +
`a value is out of range.`,
valueType: "string",
},
rangeInvalidSeverity: {
description: `This property sets the severity level of the value range validation.`,
valueType: "string",
availableValues: filteredValidationSeverityValues,
defaultValue: "error",
},
pattern: {
description:
"This value specifies a predefined regular expression to test the input value. " +
"If this value is not set, no pattern check is done.",
valueType: "string",
},
patternInvalidMessage: {
description:
`This optional string property is used to customize the message that is displayed on a ` +
`failed pattern test.`,
valueType: "string",
},
patternInvalidSeverity: {
description: `This property sets the severity level of the pattern validation.`,
valueType: "string",
availableValues: filteredValidationSeverityValues,
defaultValue: "error",
},
regex: {
description:
"This value specifies a custom regular expression to test the input value. If this value " +
"is not set, no regular expression pattern check is done.",
valueType: "string",
},
regexInvalidMessage: {
description:
`This optional string property is used to customize the message that is displayed on a ` +
`failed regular expression test.`,
valueType: "string",
},
regexInvalidSeverity: {
description: `This property sets the severity level of regular expression validation.`,
valueType: "string",
availableValues: filteredValidationSeverityValues,
defaultValue: "error",
},
inputTemplate: dComponent("This property is used to define a custom input template."),
gap: {
description: "This property defines the gap between the adornments and the input area.",
valueType: "string",
defaultValue: defaultProps.gap,
},
noSubmit: {
description:
"When set to `true`, the field will not be included in the form's submitted data. " +
"This is useful for fields that should be present in the form but not submitted, " +
"similar to hidden fields. If multiple FormItems reference the same `bindTo` value " +
"and any of them has `noSubmit` set to `true`, the field will NOT be submitted.",
type: "boolean",
defaultValue: defaultProps.noSubmit,
},
},
events: {
validate: d(`This event is used to define a custom validation function.`),
},
apis: {
addItem: {
description:
"This method adds a new item to the list held by the FormItem. The function has a single " +
"parameter, the data to add to the FormItem. The new item is appended to the end of the list.",
signature: "addItem(data: any): void",
parameters: {
data: "The data to add to the FormItem's list.",
},
},
removeItem: {
description:
"Removes the item specified by its index from the list held by the FormItem. " +
"The function has a single argument, the index to remove.",
signature: "removeItem(index: number): void",
parameters: {
index: "The index of the item to remove from the FormItem's list.",
},
},
},
contextVars: {
$value: d("Current value of the FormItem, accessible in expressions and code snippets"),
$setValue: d("Function to set the FormItem's value programmatically"),
$validationResult: d("Current validation state and error messages for this field"),
},
themeVars: parseScssVar(styles.themeVars),
defaultThemeVars: {
"textColor-FormItemLabel": "$textColor-primary",
"fontSize-FormItemLabel": "$fontSize-sm",
"fontWeight-FormItemLabel": "$fontWeight-medium",
"fontStyle-FormItemLabel": "normal",
"textTransform-FormItemLabel": "none",
"textColor-FormItemLabel-requiredMark": "$color-danger-400",
},
});
export const formItemComponentRenderer = createComponentRenderer(
COMP,
FormItemMd,
({
node,
renderChild,
extractValue,
className,
layoutContext,
lookupEventHandler,
lookupAction,
registerComponentApi,
}) => {
const {
bindTo,
autoFocus,
label,
labelPosition,
labelWidth,
labelBreak,
enabled,
required,
type,
requiredInvalidMessage,
minLength,
maxLength,
lengthInvalidMessage,
lengthInvalidSeverity,
minValue,
maxValue,
rangeInvalidMessage,
rangeInvalidSeverity,
pattern,
patternInvalidMessage,
patternInvalidSeverity,
regex,
regexInvalidMessage,
regexInvalidSeverity,
customValidationsDebounce,
validationMode,
maxTextLength,
gap,
noSubmit,
...rest
} = node.props;
const resolvedRestProps = extractValue(rest);
const formItemType = extractValue.asOptionalString(type);
const isCustomFormItem =
(formItemType === undefined || formItemType === "custom") && !!node.children;
const inputTemplate = node.children || node.props?.inputTemplate;
return (
<FormItem
// --- validation props
required={extractValue.asOptionalBoolean(required)}
requiredInvalidMessage={extractValue.asOptionalString(requiredInvalidMessage)}
minLength={extractValue.asOptionalNumber(minLength)}
maxLength={extractValue.asOptionalNumber(maxLength)}
lengthInvalidMessage={extractValue.asOptionalString(lengthInvalidMessage)}
lengthInvalidSeverity={parseSeverity(extractValue.asOptionalString(lengthInvalidSeverity))}
minValue={extractValue.asOptionalNumber(minValue)}
maxValue={extractValue.asOptionalNumber(maxValue)}
rangeInvalidMessage={extractValue.asOptionalString(rangeInvalidMessage)}
rangeInvalidSeverity={parseSeverity(extractValue.asOptionalString(rangeInvalidSeverity))}
pattern={extractValue.asOptionalString(pattern)}
patternInvalidMessage={extractValue.asOptionalString(patternInvalidMessage)}
patternInvalidSeverity={parseSeverity(
extractValue.asOptionalString(patternInvalidSeverity),
)}
regex={extractValue.asOptionalString(regex)}
regexInvalidMessage={extractValue.asOptionalString(regexInvalidMessage)}
regexInvalidSeverity={parseSeverity(extractValue.asOptionalString(regexInvalidSeverity))}
// ----
className={className}
layoutContext={layoutContext}
labelBreak={extractValue.asOptionalBoolean(labelBreak)}
labelWidth={extractValue.asOptionalString(labelWidth)}
bindTo={extractValue.asString(bindTo)}
autoFocus={extractValue.asOptionalBoolean(autoFocus)}
enabled={extractValue.asOptionalBoolean(enabled)}
label={extractValue.asOptionalString(label)}
labelPosition={extractValue.asOptionalString(labelPosition)}
type={isCustomFormItem ? "custom" : formItemType}
onValidate={lookupEventHandler("validate")}
customValidationsDebounce={extractValue.asOptionalNumber(customValidationsDebounce)}
validationMode={extractValue.asOptionalString(validationMode)}
registerComponentApi={registerComponentApi}
maxTextLength={extractValue(maxTextLength)}
itemIndex={extractValue("{$itemIndex}")}
initialValue={extractValue(node.props.initialValue)}
noSubmit={extractValue.asOptionalBoolean(noSubmit)}
inputRenderer={
inputTemplate
? (contextVars) => (
<MemoizedItem
contextVars={contextVars}
node={inputTemplate}
renderChild={renderChild}
layoutContext={layoutContext}
/>
)
: undefined
}
gap={extractValue.asOptionalString(gap)}
{...resolvedRestProps}
>
{isCustomFormItem ? (
<CustomFormItem
renderChild={renderChild}
node={node as any}
bindTo={extractValue.asString(bindTo)}
/>
) : (
renderChild(node.children, {
type: formItemType,
})
)}
</FormItem>
);
},
);
```