This is page 39 of 181. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ └── config.json
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── layout-changes.md
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ └── lorem-ipsum.png
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ └── welcome-to-the-xmlui-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── HelloMd.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── Animation.tsx
│ │ │ ├── AnimationNative.tsx
│ │ │ ├── FadeAnimation.tsx
│ │ │ ├── FadeInAnimation.tsx
│ │ │ ├── FadeOutAnimation.tsx
│ │ │ ├── index.tsx
│ │ │ ├── ScaleAnimation.tsx
│ │ │ └── SlideInAnimation.tsx
│ │ └── tsconfig.json
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ ├── tsconfig.json
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── HelloWorld.module.scss
│ │ │ ├── HelloWorld.tsx
│ │ │ ├── HelloWorldNative.tsx
│ │ │ └── index.tsx
│ │ └── tsconfig.json
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── IPhoneFrame.module.scss
│ │ │ ├── IPhoneFrame.tsx
│ │ │ ├── MacOSAppFrame.module.scss
│ │ │ ├── MacOSAppFrame.tsx
│ │ │ ├── WindowsAppFrame.module.scss
│ │ │ └── WindowsAppFrame.tsx
│ │ └── tsconfig.json
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── LazyPdfNative.tsx
│ │ │ ├── Pdf.module.scss
│ │ │ └── Pdf.tsx
│ │ └── tsconfig.json
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── hooks
│ │ │ │ ├── usePlayground.ts
│ │ │ │ └── useToast.ts
│ │ │ ├── index.tsx
│ │ │ ├── playground
│ │ │ │ ├── Box.module.scss
│ │ │ │ ├── Box.tsx
│ │ │ │ ├── CodeSelector.tsx
│ │ │ │ ├── ConfirmationDialog.module.scss
│ │ │ │ ├── ConfirmationDialog.tsx
│ │ │ │ ├── Editor.tsx
│ │ │ │ ├── Header.module.scss
│ │ │ │ ├── Header.tsx
│ │ │ │ ├── Playground.tsx
│ │ │ │ ├── PlaygroundContent.module.scss
│ │ │ │ ├── PlaygroundContent.tsx
│ │ │ │ ├── PlaygroundNative.module.scss
│ │ │ │ ├── PlaygroundNative.tsx
│ │ │ │ ├── Preview.module.scss
│ │ │ │ ├── Preview.tsx
│ │ │ │ ├── Select.module.scss
│ │ │ │ ├── StandalonePlayground.tsx
│ │ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ │ ├── ThemeSwitcher.module.scss
│ │ │ │ ├── ThemeSwitcher.tsx
│ │ │ │ ├── ToneSwitcher.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── providers
│ │ │ │ ├── Toast.module.scss
│ │ │ │ └── ToastProvider.tsx
│ │ │ ├── state
│ │ │ │ └── store.ts
│ │ │ ├── themes
│ │ │ │ └── theme.ts
│ │ │ └── utils
│ │ │ └── helpers.ts
│ │ └── tsconfig.json
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Search.module.scss
│ │ │ └── Search.tsx
│ │ └── tsconfig.json
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Spreadsheet.tsx
│ │ │ └── SpreadsheetNative.tsx
│ │ └── tsconfig.json
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ ├── src
│ │ ├── Carousel
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── FancyButton
│ │ │ ├── FancyButton.module.scss
│ │ │ ├── FancyButton.tsx
│ │ │ └── FancyButton.xmlui
│ │ ├── Hello
│ │ │ ├── Hello.tsx
│ │ │ ├── Hello.xmlui
│ │ │ └── Hello.xmlui.xs
│ │ ├── HeroSection
│ │ │ ├── HeroSection.module.scss
│ │ │ ├── HeroSection.tsx
│ │ │ └── HeroSectionNative.tsx
│ │ ├── index.tsx
│ │ ├── ScrollToTop
│ │ │ ├── ScrollToTop.module.scss
│ │ │ ├── ScrollToTop.tsx
│ │ │ └── ScrollToTopNative.tsx
│ │ └── vite-env.d.ts
│ └── tsconfig.json
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── playwright.config.ts
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── get-langserver-metadata.mjs
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ ├── LabelListNative.module.scss
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.mjs
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.bin.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/packages/xmlui-website-blocks/src/FancyButton/FancyButton.module.scss:
--------------------------------------------------------------------------------
```scss
1 | // FancyButton.module.scss
2 | @use "../../../../xmlui/src/components-core/theming/themes" as t;
3 |
4 | // --- This code snippet is required to collect the theme variables used in this module
5 | $themeVars: ();
6 | @function createThemeVar($componentVariable) {
7 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
8 | @return t.getThemeVar($themeVars, $componentVariable);
9 | }
10 |
11 | // Define theme variables
12 | $component: "FancyButton";
13 | $fontSize-FancyButton: createThemeVar("fontSize-#{$component}");
14 | $fontWeight-FancyButton: createThemeVar("fontWeight-#{$component}");
15 | $textColor-FancyButton: createThemeVar("textColor-#{$component}");
16 | $gap-FancyButton: createThemeVar("gap-#{$component}");
17 | $borderStyle-FancyButton: createThemeVar("borderStyle-#{$component}");
18 | $borderWidth-FancyButton: createThemeVar("borderWidth-#{$component}");
19 | $outlineColor-FancyButton--focus: createThemeVar("outlineColor-#{$component}--focus");
20 | $outlineWidth-FancyButton--focus: createThemeVar("outlineWidth-#{$component}--focus");
21 | $outlineStyle-FancyButton--focus: createThemeVar("outlineStyle-#{$component}--focus");
22 | $outlineOffset-FancyButton--focus: createThemeVar("outlineOffset-#{$component}--focus");
23 |
24 | // Disabled state variables
25 | $backgroundColor-FancyButton--disabled: createThemeVar("backgroundColor-#{$component}--disabled");
26 | $borderColor-FancyButton--disabled: createThemeVar("borderColor-#{$component}--disabled");
27 | $textColor-FancyButton--disabled: createThemeVar("textColor-#{$component}--disabled");
28 |
29 | @mixin sizeVariant($size) {
30 | padding: createThemeVar("paddingVertical-#{$component}-#{$size}")
31 | createThemeVar("paddingHorizontal-#{$component}-#{$size}");
32 | font-size: createThemeVar("fontSize-#{$component}-#{$size}");
33 | gap: createThemeVar("gap-#{$component}-#{$size}");
34 | }
35 |
36 | @mixin themeVariant($variant) {
37 | border-radius: createThemeVar("borderRadius-#{$component}-#{$variant}");
38 | background-color: createThemeVar("backgroundColor-#{$component}-#{$variant}");
39 | color: createThemeVar("textColor-#{$component}-#{$variant}");
40 | border: createThemeVar("border-#{$component}-#{$variant}");
41 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}");
42 | border-style: createThemeVar("borderStyle-#{$component}-#{$variant}");
43 | border-width: createThemeVar("borderWidth-#{$component}-#{$variant}");
44 |
45 | &.xs {
46 | border-radius: createThemeVar("borderRadius-#{$component}-#{$variant}-xs");
47 | border: createThemeVar("border-#{$component}-#{$variant}-xs");
48 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}-xs");
49 | border-style: createThemeVar("borderStyle-#{$component}-#{$variant}-xs");
50 | border-width: createThemeVar("borderWidth-#{$component}-#{$variant}-xs");
51 | }
52 |
53 | &.sm {
54 | border-radius: createThemeVar("borderRadius-#{$component}-#{$variant}-sm");
55 | border: createThemeVar("border-#{$component}-#{$variant}-sm");
56 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}-sm");
57 | border-style: createThemeVar("borderStyle-#{$component}-#{$variant}-sm");
58 | border-width: createThemeVar("borderWidth-#{$component}-#{$variant}-sm");
59 | }
60 |
61 | &.md {
62 | border-radius: createThemeVar("borderRadius-#{$component}-#{$variant}-md");
63 | border: createThemeVar("border-#{$component}-#{$variant}-md");
64 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}-md");
65 | border-style: createThemeVar("borderStyle-#{$component}-#{$variant}-md");
66 | border-width: createThemeVar("borderWidth-#{$component}-#{$variant}-md");
67 | }
68 |
69 | &.lg {
70 | border-radius: createThemeVar("borderRadius-#{$component}-#{$variant}-lg");
71 | border: createThemeVar("border-#{$component}-#{$variant}-lg");
72 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}-lg");
73 | border-style: createThemeVar("borderStyle-#{$component}-#{$variant}-lg");
74 | border-width: createThemeVar("borderWidth-#{$component}-#{$variant}-lg");
75 | }
76 |
77 | &.xl {
78 | border-radius: createThemeVar("borderRadius-#{$component}-#{$variant}-xl");
79 | border: createThemeVar("border-#{$component}-#{$variant}-xl");
80 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}-xl");
81 | border-style: createThemeVar("borderStyle-#{$component}-#{$variant}-xl");
82 | border-width: createThemeVar("borderWidth-#{$component}-#{$variant}-xl");
83 | }
84 |
85 | &:hover:not(.fancyButton--disabled) {
86 | background-color: createThemeVar("backgroundColor-#{$component}-#{$variant}--hover");
87 | border-color: createThemeVar("borderColor-#{$component}-#{$variant}--hover");
88 | }
89 |
90 | &:active:not(.fancyButton--disabled) {
91 | background-color: createThemeVar("backgroundColor-#{$component}-#{$variant}--active");
92 | }
93 | }
94 |
95 | // --- This part defines the CSS styles
96 |
97 | @layer components {
98 | .fancyButton {
99 | display: inline-flex;
100 | flex-direction: row;
101 | align-items: center;
102 | justify-content: center;
103 | gap: $gap-FancyButton;
104 | cursor: pointer;
105 | transition: all 0.2s ease-in-out;
106 | border: none;
107 | outline: none;
108 | font-family: inherit;
109 | text-decoration: none;
110 | user-select: none;
111 | position: relative;
112 |
113 | width: fit-content;
114 | height: fit-content;
115 | font-size: $fontSize-FancyButton;
116 | font-weight: $fontWeight-FancyButton;
117 | color: $textColor-FancyButton;
118 | border-style: $borderStyle-FancyButton;
119 | border-width: $borderWidth-FancyButton;
120 |
121 | &:focus-visible {
122 | outline: $outlineStyle-FancyButton--focus $outlineWidth-FancyButton--focus
123 | $outlineColor-FancyButton--focus;
124 | outline-offset: $outlineOffset-FancyButton--focus;
125 | }
126 |
127 | // Size variations
128 | &.xs {
129 | @include sizeVariant("xs");
130 | }
131 |
132 | &.sm {
133 | @include sizeVariant("sm");
134 | }
135 |
136 | &.md {
137 | @include sizeVariant("md");
138 | }
139 |
140 | &.lg {
141 | @include sizeVariant("lg");
142 | }
143 |
144 | &.xl {
145 | @include sizeVariant("xl");
146 | }
147 |
148 | // Rounded variant
149 | &.rounded {
150 | @include themeVariant("rounded");
151 | }
152 |
153 | // Square variant
154 | &.square {
155 | @include themeVariant("square");
156 | }
157 |
158 | // Pill variant
159 | &.pill {
160 | @include themeVariant("pill");
161 | }
162 |
163 | // Outlines pill variant
164 | &.outlinedPill {
165 | @include themeVariant("outlinedPill");
166 | }
167 |
168 | // Disabled state
169 | &.disabled {
170 | cursor: not-allowed;
171 | background-color: $backgroundColor-FancyButton--disabled !important;
172 | border-color: $borderColor-FancyButton--disabled !important;
173 | color: $textColor-FancyButton--disabled !important;
174 | }
175 |
176 | // Icon only styling
177 | &.iconOnly {
178 | aspect-ratio: 1;
179 | padding: 0.5rem;
180 | }
181 |
182 | // Content position
183 | &.contentPositionStart {
184 | justify-content: flex-start;
185 | }
186 |
187 | &.contentPositionCenter {
188 | justify-content: center;
189 | }
190 |
191 | &.contentPositionEnd {
192 | justify-content: flex-end;
193 | }
194 |
195 | // Icon position (when both icon and content exist)
196 | &.iconPositionEnd {
197 | flex-direction: row-reverse;
198 | }
199 | }
200 | }
201 |
202 | // --- We export the theme variables to add them to the component renderer
203 | :export {
204 | themeVars: t.json-stringify($themeVars);
205 | }
206 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Markdown/Markdown.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./Markdown.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import { Markdown, defaultProps } from "./MarkdownNative";
6 | import type React from "react";
7 | import { forwardRef, useMemo } from "react";
8 | import type { ValueExtractor } from "../../abstractions/RendererDefs";
9 | import { parseBindingExpression } from "./parse-binding-expr";
10 | import type { CodeHighlighter } from "../CodeBlock/highlight-code";
11 | import {
12 | convertPlaygroundPatternToMarkdown,
13 | convertTreeDisplayToMarkdown,
14 | observePlaygroundPattern,
15 | observeTreeDisplay,
16 | } from "./utils";
17 | import { createMetadata, d } from "../metadata-helpers";
18 |
19 | const COMP = "Markdown";
20 |
21 | export const MarkdownMd = createMetadata({
22 | status: "stable",
23 | description:
24 | "`Markdown` renders formatted text using markdown syntax. Use " +
25 | "[Text](/working-with-text) for simple, styled text content, and `Markdown` " +
26 | "when you need [rich formatting](/working-with-markdown).",
27 | themeVars: parseScssVar(styles.themeVars),
28 | props: {
29 | content: d(
30 | "This property sets the markdown content to display. Alternatively, you can nest " +
31 | "the markdown content as a child in a CDATA section. In neither this property " +
32 | "value nor any child is defined, empty content is displayed.",
33 | ),
34 | codeHighlighter: {
35 | description: "This property sets the code highlighter to use.",
36 | isInternal: true,
37 | },
38 | removeIndents: {
39 | description:
40 | "This boolean property specifies whether leading indents should be " +
41 | "removed from the markdown content. If set to `true`, the shortest " +
42 | "indent found at the start of the content lines is removed from the " +
43 | "beginning of every line.",
44 | valueType: "boolean",
45 | defaultValue: defaultProps.removeIndents,
46 | },
47 | showHeadingAnchors: {
48 | description:
49 | "This boolean property specifies whether heading anchors should be " +
50 | "displayed. If set to `true`, heading anchors will be displayed on hover " +
51 | "next to headings.",
52 | valueType: "boolean",
53 | },
54 | },
55 |
56 | defaultThemeVars: {
57 | "backgroundColor-Admonition": "$color-primary-100",
58 | "border-Admonition": "1px solid $color-primary-300",
59 | "backgroundColor-Admonition-warning": "$color-warn-100",
60 | "borderColor-Admonition-warning": "$color-warn-300",
61 | "backgroundColor-Admonition-danger": "$color-danger-100",
62 | "borderColor-Admonition-danger": "$color-danger-300",
63 | "borderRadius-Admonition": "$space-2",
64 | "size-icon-Admonition": "$space-5",
65 | "paddingLeft-Admonition": "$space-2",
66 | "paddingRight-Admonition": "$space-6",
67 | "paddingTop-Admonition": "$space-3",
68 | "paddingBottom-Admonition": "$space-2",
69 | "marginLeft-Admonition-content": "$space-1_5",
70 | "marginTop-Admonition": "$space-7",
71 | "marginBottom-Admonition": "$space-7",
72 |
73 | "marginTop-Blockquote": "$space-7",
74 | "marginBottom-Blockquote": "$space-7",
75 | "paddingHorizontal-Blockquote": "$space-6",
76 | "paddingTop-Blockquote": "$space-3",
77 | "paddingBottom-Blockquote": "$space-2_5",
78 | "backgroundColor-Blockquote": "$color-surface-100",
79 | "width-accent-Blockquote": "3px",
80 | "color-accent-Blockquote": "$color-surface-500",
81 |
82 | "marginTop-HtmlLi": "$space-2_5",
83 | "marginBottom-HtmlLi": "$space-2_5",
84 |
85 | "marginTop-Image-markdown": "$space-4",
86 | "marginBottom-Image-markdown": "$space-4",
87 | "marginLeft-Image-markdown": "$space-0",
88 | "marginRight-Image-markdown": "$space-0",
89 |
90 | "marginTop-Text-markdown": "$space-3",
91 | "marginBottom-Text-markdown": "$space-6",
92 | "fontSize-Text-markdown": "fontSize-${COMP}",
93 | "fontWeight-Text-markdown": "fontWeight-Text",
94 |
95 | light: {
96 | // --- No light-specific theme vars
97 | },
98 | dark: {
99 | "backgroundColor-Blockquote": "$color-surface-50",
100 | "backgroundColor-Admonition": "$color-primary-200",
101 | },
102 | },
103 | });
104 |
105 | export const markdownComponentRenderer = createComponentRenderer(
106 | COMP,
107 | MarkdownMd,
108 | ({ node, extractValue, renderChild, className }) => {
109 | let renderedChildren = "";
110 |
111 | // 1. Static content prop fallback
112 | if (!renderedChildren) {
113 | renderedChildren = extractValue.asString(node.props.content);
114 | }
115 |
116 | // 2. "data" property fallback
117 | if (!renderedChildren && typeof (node.props as any).data === "string") {
118 | renderedChildren = extractValue.asString((node.props as any).data);
119 | }
120 |
121 | // 3. Children fallback
122 | if (!renderedChildren) {
123 | (node.children ?? []).forEach((child) => {
124 | const renderedChild = renderChild(child);
125 | if (typeof renderedChild === "string") {
126 | renderedChildren += renderedChild;
127 | }
128 | });
129 | }
130 |
131 | return (
132 | <TransformedMarkdown
133 | className={className}
134 | removeIndents={extractValue.asOptionalBoolean(node.props.removeIndents, true)}
135 | codeHighlighter={extractValue(node.props.codeHighlighter)}
136 | extractValue={extractValue}
137 | showHeadingAnchors={extractValue.asOptionalBoolean(node.props.showHeadingAnchors)}
138 | >
139 | {renderedChildren}
140 | </TransformedMarkdown>
141 | );
142 | },
143 | );
144 |
145 | type TransformedMarkdownProps = {
146 | children: React.ReactNode;
147 | removeIndents?: boolean;
148 | className?: string;
149 | extractValue: ValueExtractor;
150 | codeHighlighter?: CodeHighlighter;
151 | showHeadingAnchors?: boolean;
152 | };
153 |
154 | const TransformedMarkdown = forwardRef<HTMLDivElement, TransformedMarkdownProps>(
155 | (
156 | {
157 | children,
158 | removeIndents,
159 | className,
160 | extractValue,
161 | codeHighlighter,
162 | showHeadingAnchors,
163 | }: TransformedMarkdownProps,
164 | ref,
165 | ) => {
166 | const markdownContent = useMemo(() => {
167 | if (typeof children !== "string") {
168 | return null;
169 | }
170 |
171 | // --- Resolve binding expression values
172 | // --- Resolve xmlui playground definitions
173 |
174 | let resolvedMd = children;
175 | while (true) {
176 | const nextPlayground = observePlaygroundPattern(resolvedMd);
177 | if (!nextPlayground) break;
178 |
179 | resolvedMd =
180 | resolvedMd.slice(0, nextPlayground[0]) +
181 | convertPlaygroundPatternToMarkdown(nextPlayground[2]) +
182 | resolvedMd.slice(nextPlayground[1]);
183 | }
184 |
185 | while (true) {
186 | const nextTreeDisplay = observeTreeDisplay(resolvedMd);
187 | if (!nextTreeDisplay) break;
188 | resolvedMd =
189 | resolvedMd.slice(0, nextTreeDisplay[0]) +
190 | convertTreeDisplayToMarkdown(nextTreeDisplay[2]) +
191 | resolvedMd.slice(nextTreeDisplay[1]);
192 | }
193 |
194 | resolvedMd = parseBindingExpression(resolvedMd, extractValue);
195 | return resolvedMd;
196 | }, [children, extractValue]);
197 |
198 | return (
199 | <Markdown
200 | ref={ref}
201 | removeIndents={removeIndents}
202 | codeHighlighter={codeHighlighter}
203 | className={className}
204 | showHeadingAnchors={showHeadingAnchors}
205 | >
206 | {markdownContent}
207 | </Markdown>
208 | );
209 | },
210 | );
211 |
212 | export { Markdown } from "./MarkdownNative";
213 |
```
--------------------------------------------------------------------------------
/xmlui/tests/parsers/xmlui/transform.errors.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, expect, it, assert } from "vitest";
2 | import { transformSource } from "./xmlui";
3 |
4 | describe("Xmlui transform - errors", () => {
5 | it("Missing name in compound component", () => {
6 | try {
7 | transformSource("<Component />");
8 | assert.fail("Exception expected");
9 | } catch (err) {
10 | expect(err.toString()).includes("T003");
11 | }
12 | });
13 |
14 | it("Invalid name in compound component #1", () => {
15 | try {
16 | transformSource("<Component name=''/>");
17 | assert.fail("Exception expected");
18 | } catch (err) {
19 | expect(err.toString()).includes("T004");
20 | }
21 | });
22 |
23 | it("Invalid name in compound component #2", () => {
24 | try {
25 | transformSource("<Component name='alma'/>");
26 | assert.fail("Exception expected");
27 | } catch (err) {
28 | expect(err.toString()).includes("T004");
29 | }
30 | });
31 |
32 | it("Compound child in compound component", () => {
33 | try {
34 | transformSource("<Component name='MyComp'><Component /></Component>");
35 | assert.fail("Exception expected");
36 | } catch (err) {
37 | expect(err.toString()).includes("T006");
38 | }
39 | });
40 |
41 | it("Invalid attribute in compound component", () => {
42 | try {
43 | transformSource("<Component name='MyComp' blabla='123'><Stack/></Component>");
44 | assert.fail("Exception expected");
45 | } catch (err) {
46 | expect(err.toString()).includes("T021");
47 | }
48 | });
49 |
50 | it("Event attribute starts with 'on'", () => {
51 | try {
52 | transformSource("<Stack><event name='onClick' /></Stack>");
53 | assert.fail("Exception expected");
54 | } catch (err) {
55 | expect(err.toString()).includes("T008");
56 | }
57 | });
58 |
59 | it("'uses' is invalid in a compound component", () => {
60 | try {
61 | transformSource("<Component name='MyComp'><Stack/><uses /></Component>");
62 | assert.fail("Exception expected");
63 | } catch (err) {
64 | expect(err.toString()).includes("T009");
65 | }
66 | });
67 |
68 | it("'loaders' is invalid in a compound component", () => {
69 | try {
70 | transformSource("<Component name='MyComp'><Stack/><loaders /></Component>");
71 | assert.fail("Exception expected");
72 | } catch (err) {
73 | expect(err.toString()).includes("T009");
74 | }
75 | });
76 |
77 | it("Invalid attribute in prop", () => {
78 | try {
79 | transformSource("<Stack><property name='my' blabal='123'/></Stack>");
80 | assert.fail("Exception expected");
81 | } catch (err) {
82 | expect(err.toString()).includes("T011");
83 | }
84 | });
85 |
86 | it("Invalid attribute in event", () => {
87 | try {
88 | transformSource("<Stack><event name='my' blabal='123'/></Stack>");
89 | assert.fail("Exception expected");
90 | } catch (err) {
91 | expect(err.toString()).includes("T011");
92 | }
93 | });
94 |
95 | it("Invalid attribute in var", () => {
96 | try {
97 | transformSource("<Stack><variable name='my' blabal='123'/></Stack>");
98 | assert.fail("Exception expected");
99 | } catch (err) {
100 | expect(err.toString()).includes("T011");
101 | }
102 | });
103 |
104 | it("Invalid attribute in api", () => {
105 | try {
106 | transformSource("<Stack><property name='my' blabal='123'/></Stack>");
107 | assert.fail("Exception expected");
108 | } catch (err) {
109 | expect(err.toString()).includes("T011");
110 | }
111 | });
112 |
113 | it("Name required in prop #1", () => {
114 | try {
115 | transformSource("<Stack><property /></Stack>");
116 | assert.fail("Exception expected");
117 | } catch (err) {
118 | expect(err.toString()).includes("T012");
119 | }
120 | });
121 |
122 | it("Name required in prop #2", () => {
123 | try {
124 | transformSource("<Stack><property name='' /></Stack>");
125 | assert.fail("Exception expected");
126 | } catch (err) {
127 | expect(err.toString()).includes("T012");
128 | }
129 | });
130 |
131 | it("Name required in event #1", () => {
132 | try {
133 | transformSource("<Stack><event /></Stack>");
134 | assert.fail("Exception expected");
135 | } catch (err) {
136 | expect(err.toString()).includes("T012");
137 | }
138 | });
139 |
140 | it("Name required in event #2", () => {
141 | try {
142 | transformSource("<Stack><event name='' /></Stack>");
143 | assert.fail("Exception expected");
144 | } catch (err) {
145 | expect(err.toString()).includes("T012");
146 | }
147 | });
148 |
149 | it("Name required in var #1", () => {
150 | try {
151 | transformSource("<Stack><variable/></Stack>");
152 | assert.fail("Exception expected");
153 | } catch (err) {
154 | expect(err.toString()).includes("T012");
155 | }
156 | });
157 |
158 | it("Name required in var #2", () => {
159 | try {
160 | transformSource("<Stack><variable name='' /></Stack>");
161 | assert.fail("Exception expected");
162 | } catch (err) {
163 | expect(err.toString()).includes("T012");
164 | }
165 | });
166 |
167 | it("Name required in method #1", () => {
168 | try {
169 | transformSource("<Stack><method /></Stack>");
170 | assert.fail("Exception expected");
171 | } catch (err) {
172 | expect(err.toString()).includes("T012");
173 | }
174 | });
175 |
176 | it("Name required in api #2", () => {
177 | try {
178 | transformSource("<Stack><method name='' /></Stack>");
179 | assert.fail("Exception expected");
180 | } catch (err) {
181 | expect(err.toString()).includes("T012");
182 | }
183 | });
184 |
185 | it("A 'uses' must have value #1", () => {
186 | try {
187 | transformSource("<Stack><uses value=''/></Stack>");
188 | assert.fail("Exception expected");
189 | } catch (err) {
190 | expect(err.toString()).includes("T015");
191 | }
192 | });
193 |
194 | it("A 'uses' must have value #2", () => {
195 | try {
196 | transformSource("<Stack><uses/></Stack>");
197 | assert.fail("Exception expected");
198 | } catch (err) {
199 | expect(err.toString()).includes("T015");
200 | }
201 | });
202 |
203 | it("A 'value' can have 'field' and 'item' child", () => {
204 | try {
205 | transformSource("<Stack><property name='my'><dummy /></property></Stack>");
206 | assert.fail("Exception expected");
207 | } catch (err) {
208 | expect(err.toString()).includes("T016");
209 | }
210 | });
211 |
212 | it("Cannot mix field and item children #1", () => {
213 | try {
214 | transformSource(
215 | "<Stack><property name='my'><field name='my' /><item value='3'/></property></Stack>",
216 | );
217 | assert.fail("Exception expected");
218 | } catch (err) {
219 | expect(err.toString()).includes("T017");
220 | }
221 | });
222 |
223 | it("Cannot mix field and item children #2", () => {
224 | try {
225 | transformSource(
226 | `<Stack>
227 | <property name='my'>
228 | <item value='3'/>
229 | <field name='my' />
230 | </property>
231 | </Stack>`,
232 | );
233 | assert.fail("Exception expected");
234 | } catch (err) {
235 | expect(err.toString()).includes("T017");
236 | }
237 | });
238 |
239 | it("Item cannot have a 'name' attribute", () => {
240 | try {
241 | transformSource("<Stack><property name='my'><item name='my' value='3'/></property></Stack>");
242 | assert.fail("Exception expected");
243 | } catch (err) {
244 | expect(err.toString()).includes("T018");
245 | }
246 | });
247 |
248 | it("throws script errors in script tag", () => {
249 | try {
250 | transformSource(`
251 | <App>
252 | <script>
253 | var a =;
254 | </script>
255 | <Text>Hello World!</Text>
256 | </App>
257 | `);
258 | assert.fail("Exception expected");
259 | } catch (err) {
260 | expect(err.toString()).include("W001");
261 | }
262 | });
263 | });
264 |
```
--------------------------------------------------------------------------------
/xmlui/src/abstractions/RendererDefs.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { CSSProperties, ForwardedRef, ReactNode, RefObject } from "react";
2 |
3 | import type { AppContextObject } from "./AppContextDefs";
4 | import type {
5 | ComponentDef,
6 | ComponentMetadata,
7 | CompoundComponentDef,
8 | DynamicChildComponentDef, ParentRenderContext,
9 | } from "./ComponentDefs";
10 | import type { ContainerState } from "./ContainerDefs";
11 | import type { LookupActionOptions, LookupAsyncFn, LookupSyncFn } from "./ActionDefs";
12 | import type { AsyncFunction } from "./FunctionDefs";
13 | import type {ComponentApi} from "../components-core/rendering/ContainerWrapper";
14 |
15 | // This interface defines the renderer context for the exposed components of the
16 | // XMLUI framework.
17 | export interface RendererContext<TMd extends ComponentMetadata = ComponentMetadata>
18 | extends ComponentRendererContextBase<TMd> {
19 | uid: symbol; // The unique identifier of the component instance
20 |
21 | updateState: UpdateStateFn; // A component invokes this function to change its internal state
22 |
23 | // Context variables (all keys starting with "$") available in the current container
24 | contextVars: Record<string, any>;
25 |
26 | // When a component wants to access a property value (which may contain a binding
27 | // expression to evaluate), it must use this property to get the current value.
28 | extractValue: ValueExtractor;
29 |
30 | // This function gets a physical resource URL according to the provided logical URL.
31 | extractResourceUrl: (url?: string) => string | undefined;
32 |
33 | // This function gets an async executable function that handles an event.
34 | lookupEventHandler: LookupEventHandlerFn<TMd>;
35 |
36 | registerComponentApi: RegisterComponentApiFn; // A component can register its APIs with this function
37 |
38 | lookupAction: LookupAsyncFn; // This function obtains an action by its name with the specified options
39 |
40 | // This function retrieves a sync function the component can use as a callback
41 | lookupSyncCallback: LookupSyncFn;
42 |
43 | className?: string;
44 | }
45 |
46 | export type UpdateStateFn = (componentState: any, options?: any) => void; // This function updates the state of a component.
47 |
48 | // This type represent the function that extracts the value from a component property
49 | export type ValueExtractor = {
50 | (expression?: any, strict?: boolean): any; // Get a value (any) from a component property
51 |
52 | asString(expression?: any): string; // Get a string value from an expression
53 |
54 | // Get an optional string value from an expression
55 | asOptionalString<T extends string>(expression?: any, defValue?: string): T | undefined;
56 |
57 | // Get an optional string value from an expression
58 | asOptionalStringArray(expression?: any): (string | undefined)[];
59 |
60 | asDisplayText(expression?: any): string; // Get a display string value from an expression
61 |
62 | asNumber(expression?: any): number; // Get a number value from an expression
63 |
64 | // Get an optional number value from an expression
65 | asOptionalNumber(expression?: any, defValue?: number): number | undefined;
66 |
67 | // Get a boolean value (JavaScript semantics) from an expression
68 | asBoolean(expression?: any): boolean;
69 |
70 | // Get an optional Boolean value from an expression
71 | asOptionalBoolean(expression?: any, defValue?: boolean): boolean | undefined;
72 |
73 | // Get a CSS size value from an expression
74 | asSize(expression?: any): string;
75 | };
76 |
77 | // This function retrieves an async function for a particular component's specified
78 | // event to be invoked as an event handler (`undefined` if the particular event
79 | // handler is not defined).
80 | export type LookupEventHandlerFn<TMd extends ComponentMetadata = ComponentMetadata> = (
81 | eventName: keyof NonNullable<TMd["events"]>,
82 | actionOptions?: LookupActionOptions,
83 | ) => AsyncFunction | undefined;
84 |
85 | // This type represents a function that registers all API endpoints of a particular component.
86 | export type RegisterComponentApiFn = (componentApi: ComponentApi) => void;
87 |
88 | // Function signature to render a particular child component (or set of child components)
89 | export type RenderChildFn<L extends ComponentDef = ComponentDef> = (
90 | children?:
91 | | ComponentDef
92 | | ComponentDef[]
93 | | DynamicChildComponentDef
94 | | DynamicChildComponentDef[]
95 | | string,
96 | layoutContext?: LayoutContext<L>,
97 | parentRenderContext?: ParentRenderContext,
98 | uidInfoRef?: RefObject<Record<string, any>>,
99 | ref?: ForwardedRef<any>,
100 | rest?: Record<string, any>
101 | ) => ReactNode | ReactNode[];
102 |
103 | // Each component is rendered in a particular layout context (for example, within a
104 | // stack). This type provides information about that context and the operations that
105 | // render children in it.
106 | export type LayoutContext<T extends ComponentDef = ComponentDef> = {
107 | type?: string; // The type of the layout context
108 |
109 | // This function allows the React representation of a particular child node to be
110 | // wrapped in whatever React components to accommodate the current layout context.
111 | // When the engine is about to render children in a particular layout context, it
112 | // checks the existence of this function. If declared, the engine invokes it.
113 | wrapChild?: (
114 | context: RendererContext<T>,
115 | renderedChild: ReactNode,
116 | metadata?: ComponentMetadata,
117 | ) => ReactNode;
118 |
119 | // Arbitrary props extending the layout context
120 | [key: string]: any;
121 | };
122 |
123 | export type NonCssLayoutProps = {
124 | horizontalAlignment?: string;
125 | verticalAlignment?: string;
126 | orientation?: string;
127 | };
128 |
129 | // This function renders a component definition into a React component
130 | export type ComponentRendererFn<T extends ComponentDef> = (
131 | context: RendererContext<T>,
132 | ) => ReactNode;
133 |
134 | // This function renders a component definition into a React component
135 | export type CompoundComponentRendererInfo = {
136 | compoundComponentDef: CompoundComponentDef;
137 | metadata?: ComponentMetadata;
138 | };
139 |
140 | // Components must be registered with a component registry so the engine can use them.
141 | // This type collects the information held by the registry.
142 | export type ComponentRendererDef<T extends ComponentDef = any> = {
143 | // The component's type identifier. In the markup, the component must use this name
144 | // to be recognized.
145 | type: string;
146 |
147 | // This function renders the component from its definition to its React representation.
148 | renderer: ComponentRendererFn<T>;
149 |
150 | // The metadata to use when rendering the component
151 | metadata?: ComponentMetadata;
152 | };
153 |
154 | // Rendering components (turning component definitions into their React node
155 | // representation) is a complicated process that requires information describing the
156 | // actual context. This interface defines the common properties of that context.
157 | export interface ComponentRendererContextBase<TMd extends ComponentMetadata = ComponentMetadata> {
158 | // The definition of the component to render
159 | node: ComponentDef<TMd>;
160 |
161 | // The state of the container in which the component is rendered
162 | state: ContainerState;
163 |
164 | // The application context the component (and its binding expressions) can use
165 | appContext?: AppContextObject;
166 |
167 | // The component can use this function to render its child components
168 | renderChild: RenderChildFn;
169 |
170 | // Information about the layout context in which the component is rendered
171 | layoutContext?: LayoutContext;
172 | }
173 |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/TableOfContentsContext.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import {
2 | createContext,
3 | useCallback,
4 | useContext,
5 | useEffect,
6 | useMemo,
7 | useRef,
8 | useState,
9 | } from "react";
10 | import { useIsomorphicLayoutEffect, useScrollEventHandler, useScrollParent } from "./utils/hooks";
11 | import { useNavigate } from "@remix-run/react";
12 | import { EMPTY_ARRAY, EMPTY_OBJECT } from "./constants";
13 | import { useAppContext } from "./AppContext";
14 |
15 |
16 | // --- Stores the information about a particular heading to be displayed in the TOC.
17 | type HeadingItem = {
18 | // --- The id of the heading.
19 | id: string;
20 |
21 | // --- Heading level
22 | level: number;
23 |
24 | // --- Heading thext to display in the TOC.
25 | text: string;
26 |
27 | // --- Reference to the anchor element.
28 | anchor: HTMLAnchorElement | null;
29 | };
30 |
31 | type ActiveAnchorChangedCallback = (id: string) => void;
32 |
33 | // --- The context object that is used to store the hierarchy of headings.
34 | interface ITableOfContentsContext {
35 | // --- The list of headings in the TOC
36 | headings: HeadingItem[];
37 |
38 | // --- This method allows adding a new heading to the TOC.
39 | registerHeading: (headingItem: HeadingItem) => void;
40 |
41 | // --- This flag indicates whether the intersection observer is enabled.
42 | hasTableOfContents: boolean;
43 |
44 | // --- This method allows setting the id of the active anchor.
45 | scrollToAnchor: (id: string, smoothScrolling: boolean) => void;
46 |
47 | subscribeToActiveAnchorChange: (callback: ActiveAnchorChangedCallback) => () => void;
48 | activeAnchorId: string;
49 | }
50 |
51 | /**
52 | * Several components work together to represent the hierarchy of a particular
53 | * app page as a TOC. This React component provides a context for storing this
54 | * hierarchy information.
55 | */
56 | export const TableOfContentsContext = createContext<ITableOfContentsContext | null>(null);
57 |
58 | /**
59 | * This provider component injects the specified children into the TOC context.
60 | */
61 | export function TableOfContentsProvider({ children }: { children: React.ReactNode }) {
62 | const [headings, setHeadings] = useState<Record<string, HeadingItem>>(EMPTY_OBJECT);
63 | const [callbacks, setCallbacks] = useState<Array<ActiveAnchorChangedCallback>>(EMPTY_ARRAY);
64 | const observer = useRef<IntersectionObserver | null>(null);
65 | const {forceRefreshAnchorScroll} = useAppContext();
66 | const thisRef = useRef({
67 | suspendPositionBasedSetActiveId: false,
68 | });
69 | const scrollParent = useScrollParent(Object.values(headings)?.[0]?.anchor);
70 | useScrollEventHandler(scrollParent, {
71 | onScrollEnd: useCallback(() => {
72 | thisRef.current.suspendPositionBasedSetActiveId = false;
73 | }, []),
74 | });
75 | const [activeAnchorId, setActiveAnchorId] = useState(null);
76 |
77 | const notify = useCallback(
78 | (id) => {
79 | callbacks.forEach((cb) => cb(id));
80 | setActiveAnchorId(id);
81 | },
82 | [callbacks],
83 | );
84 |
85 | useEffect(() => {
86 | if (callbacks.length) {
87 | const handleObserver = (entries: any) => {
88 | entries.forEach((entry: any) => {
89 | if (entry?.isIntersecting) {
90 | if (!thisRef.current.suspendPositionBasedSetActiveId) {
91 | notify(entry.target.id);
92 | }
93 | }
94 | });
95 | };
96 |
97 | // stolen from nextra: https://github.com/shuding/nextra/blob/3729f67059f1fbdd3f98125bebabbe568c918694/packages/nextra-theme-docs/src/mdx-components/heading-anchor.client.tsx
98 | // let headerHeight = getComputedStyle(scrollParent || document.body).getPropertyValue(
99 | // '--header-abs-height'
100 | // );
101 | observer.current = new IntersectionObserver(handleObserver, {
102 | rootMargin: `0% 0% -80%`,
103 | // root: scrollParent,
104 | // threshold: [0, 1],
105 | });
106 |
107 | Object.values(headings).forEach((elem) => observer?.current?.observe?.(elem.anchor!));
108 | return () => observer.current?.disconnect();
109 | }
110 | }, [callbacks.length, headings, notify, scrollParent]);
111 |
112 | const registerHeading = useCallback((headingItem: HeadingItem) => {
113 | setHeadings((prevHeadings) => {
114 | return {
115 | ...prevHeadings,
116 | [headingItem.id]: headingItem,
117 | };
118 | });
119 |
120 | return () => {
121 | setHeadings((prevHeadings) => {
122 | const newHeadings = { ...prevHeadings };
123 | delete newHeadings[headingItem.id];
124 | return newHeadings;
125 | });
126 | };
127 | }, []);
128 |
129 | const navigate = useNavigate();
130 |
131 | const scrollToAnchor = useCallback(
132 | (id: string, smoothScrolling: boolean) => {
133 | const value = headings[id];
134 | if (value) {
135 | thisRef.current.suspendPositionBasedSetActiveId = true;
136 | value.anchor.scrollIntoView({
137 | block: "start",
138 | inline: "start",
139 | behavior: smoothScrolling ? "smooth" : "auto",
140 | });
141 | notify(id);
142 | requestAnimationFrame(() => {
143 | navigate(
144 | {
145 | hash: `#${value.id}`,
146 | },
147 | {
148 | state: {
149 | preventHashScroll: true,
150 | },
151 | },
152 | );
153 | //we clear the preventHashScroll route state: https://stackoverflow.com/questions/72121228/how-to-update-location-state-in-react-router-v6
154 | requestAnimationFrame(()=>{
155 | navigate({
156 | hash: `#${value.id}`,
157 | }, { replace: true });
158 | })
159 | });
160 | }
161 | },
162 | [headings, navigate, notify],
163 | );
164 |
165 | const sortedHeadings = useMemo(() => {
166 | return Object.values(headings).sort(function (a, b) {
167 | if (a.anchor === b.anchor) return 0;
168 | if (a.anchor.compareDocumentPosition(b.anchor) & Node.DOCUMENT_POSITION_PRECEDING) {
169 | // b comes before a
170 | return 1;
171 | }
172 | return -1;
173 | });
174 | }, [headings]);
175 |
176 |
177 | //the content could take time to load, this way we try to force the scroll to anchor mechanism to kick in
178 | const hasHeadings = sortedHeadings.length > 0;
179 | useIsomorphicLayoutEffect(()=>{
180 | if(hasHeadings){
181 | forceRefreshAnchorScroll();
182 | }
183 | }, [forceRefreshAnchorScroll, hasHeadings]);
184 |
185 | const subscribeToActiveAnchorChange = useCallback((cb: ActiveAnchorChangedCallback) => {
186 | setCallbacks((prev) => {
187 | return [...prev, cb];
188 | });
189 | return () => {
190 | setCallbacks((prev) => {
191 | return prev.filter((item) => item !== cb);
192 | });
193 | };
194 | }, []);
195 |
196 | const contextValue: ITableOfContentsContext = useMemo(() => {
197 | return {
198 | registerHeading,
199 | headings: sortedHeadings,
200 | scrollToAnchor,
201 | subscribeToActiveAnchorChange,
202 | hasTableOfContents: callbacks.length > 0,
203 | activeAnchorId
204 | };
205 | }, [
206 | registerHeading,
207 | sortedHeadings,
208 | scrollToAnchor,
209 | subscribeToActiveAnchorChange,
210 | callbacks.length,
211 | activeAnchorId,
212 | ]);
213 |
214 | return (
215 | <TableOfContentsContext.Provider value={contextValue}>
216 | {children}
217 | </TableOfContentsContext.Provider>
218 | );
219 | }
220 |
221 | export function useTableOfContents() {
222 | const context = useContext(TableOfContentsContext);
223 |
224 | if (!context) {
225 | throw new Error(`The TableOfContents component can only be used inside a Page component.
226 | <App>
227 | <Pages>
228 | <Page url="/">
229 | <Heading>Harry Potter and the Sorcerer's Stone</Heading>
230 | <TableOfContents />
231 | </Page>
232 | </Pages>
233 | </App>
234 |
235 | `);
236 | }
237 |
238 | return context;
239 | }
240 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/NumberBox/NumberBox.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./NumberBox.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import {
6 | createMetadata,
7 | d,
8 | dAutoFocus,
9 | dDidChange,
10 | dEnabled,
11 | dEndIcon,
12 | dEndText,
13 | dGotFocus,
14 | dInitialValue,
15 | dLostFocus,
16 | dMaxLength,
17 | dPlaceholder,
18 | dReadonly,
19 | dRequired,
20 | dStartIcon,
21 | dStartText,
22 | dValidationStatus,
23 | } from "../metadata-helpers";
24 | import { defaultProps, NumberBox } from "./NumberBoxNative";
25 |
26 | const COMP = "NumberBox";
27 |
28 | export const NumberBoxMd = createMetadata({
29 | status: "stable",
30 | description:
31 | "`NumberBox` provides a specialized input field for numeric values with built-in " +
32 | "validation, spinner buttons, and flexible formatting options. It supports both " +
33 | "integer and floating-point numbers, handles empty states as null values, and " +
34 | "integrates seamlessly with form validation.",
35 | parts: {
36 | label: {
37 | description: "The label displayed for the text box.",
38 | },
39 | startAdornment: {
40 | description: "The adornment displayed at the start of the text box.",
41 | },
42 | endAdornment: {
43 | description: "The adornment displayed at the end of the text box.",
44 | },
45 | input: {
46 | description: "The text box input area.",
47 | },
48 | },
49 | props: {
50 | placeholder: dPlaceholder(),
51 | initialValue: dInitialValue(),
52 | maxLength: dMaxLength(),
53 | autoFocus: dAutoFocus(),
54 | required: dRequired(),
55 | readOnly: dReadonly(),
56 | enabled: dEnabled(),
57 | validationStatus: dValidationStatus(),
58 | startText: dStartText(),
59 | startIcon: dStartIcon(),
60 | endText: dEndText(),
61 | endIcon: dEndIcon(),
62 | gap: {
63 | description: "This property defines the gap between the adornments and the input area.",
64 | },
65 | hasSpinBox: {
66 | description: `This boolean prop shows (\`true\`) or hides (\`false\`) the spinner buttons for the input field.`,
67 | valueType: "boolean",
68 | defaultValue: defaultProps.hasSpinBox,
69 | },
70 | spinnerUpIcon: d(
71 | `Allows setting an alternate icon displayed in the ${COMP} spinner for incrementing values. You can change ` +
72 | `the default icon for all ${COMP} instances with the "icon.spinnerUp:NumberBox" declaration in the ` +
73 | `app configuration file.`,
74 | ),
75 | spinnerDownIcon: d(
76 | `Allows setting an alternate icon displayed in the ${COMP} spinner for decrementing values. You can change ` +
77 | `the default icon for all ${COMP} instances with the "icon.spinnerDown:NumberBox" declaration in the ` +
78 | `app configuration file.`,
79 | ),
80 | step: {
81 | description: `This prop governs how big the step when clicking on the spinner of the field.`,
82 | valueType: "number",
83 | defaultValue: defaultProps.step,
84 | },
85 | integersOnly: {
86 | description:
87 | `This boolean property signs whether the input field accepts integers only (\`true\`) ` +
88 | `or not (\`false\`).`,
89 | valueType: "boolean",
90 | defaultValue: defaultProps.integersOnly,
91 | },
92 | zeroOrPositive: {
93 | description:
94 | `This boolean property determines whether the input value can only be 0 or positive numbers ` +
95 | `(\`true\`) or also negative (\`false\`).`,
96 | valueType: "boolean",
97 | defaultValue: defaultProps.zeroOrPositive,
98 | },
99 | minValue: {
100 | description:
101 | "The minimum value the input field allows. Can be a float or an integer if " +
102 | "[\`integersOnly\`](#integersonly) is set to \`false\`, otherwise it can only be an integer." +
103 | "If not set, no minimum value check is done.",
104 | defaultValue: defaultProps.min,
105 | },
106 | maxValue: {
107 | description:
108 | "The maximum value the input field allows. Can be a float or an integer if " +
109 | "[\`integersOnly\`](#integersonly) is set to \`false\`, otherwise it can only be an integer." +
110 | "If not set, no maximum value check is done.",
111 | defaultValue: defaultProps.max,
112 | },
113 | },
114 | events: {
115 | gotFocus: dGotFocus(COMP),
116 | lostFocus: dLostFocus(COMP),
117 | didChange: dDidChange(COMP),
118 | },
119 | apis: {
120 | focus: {
121 | description: `This API focuses the input field of the \`${COMP}\`. You can use it to programmatically focus the field.`,
122 | signature: "focus(): void",
123 | },
124 | value: {
125 | description: `This API retrieves the current value of the \`${COMP}\`. You can use it to get the value programmatically.`,
126 | signature: "get value(): number | undefined",
127 | },
128 | setValue: {
129 | description: `This API sets the value of the \`${COMP}\`. You can use it to programmatically change the value.`,
130 | signature: "setValue(value: number | undefined): void",
131 | },
132 | },
133 | themeVars: parseScssVar(styles.themeVars),
134 | defaultThemeVars: {
135 | [`paddingVertical-${COMP}`]: "$space-2",
136 | [`paddingHorizontal-${COMP}`]: "$space-2",
137 | },
138 | });
139 |
140 | export const numberBoxComponentRenderer = createComponentRenderer(
141 | COMP,
142 | NumberBoxMd,
143 | ({
144 | node,
145 | state,
146 | updateState,
147 | lookupEventHandler,
148 | extractValue,
149 | className,
150 | registerComponentApi,
151 | }) => {
152 | let extractedInitialValue;
153 | try {
154 | extractedInitialValue = extractValue.asOptionalNumber(node.props.initialValue);
155 | } catch {}
156 | return (
157 | <NumberBox
158 | className={className}
159 | value={state?.value}
160 | initialValue={extractedInitialValue}
161 | step={extractValue(node.props.step)}
162 | enabled={extractValue.asOptionalBoolean(node.props.enabled)}
163 | placeholder={extractValue.asOptionalString(node.props.placeholder)}
164 | validationStatus={extractValue(node.props.validationStatus)}
165 | updateState={updateState}
166 | onDidChange={lookupEventHandler("didChange")}
167 | onFocus={lookupEventHandler("gotFocus")}
168 | onBlur={lookupEventHandler("lostFocus")}
169 | registerComponentApi={registerComponentApi}
170 | hasSpinBox={extractValue.asOptionalBoolean(node.props.hasSpinBox)}
171 | integersOnly={extractValue.asOptionalBoolean(node.props.integersOnly)}
172 | zeroOrPositive={extractValue.asOptionalBoolean(node.props.zeroOrPositive)}
173 | min={extractValue.asOptionalNumber(node.props.minValue)}
174 | max={extractValue.asOptionalNumber(node.props.maxValue)}
175 | startText={extractValue.asOptionalString(node.props.startText)}
176 | startIcon={extractValue.asOptionalString(node.props.startIcon)}
177 | endText={extractValue.asOptionalString(node.props.endText)}
178 | gap={extractValue.asOptionalString(node.props.gap)}
179 | endIcon={extractValue.asOptionalString(node.props.endIcon)}
180 | spinnerUpIcon={extractValue.asOptionalString(node.props.spinnerUpIcon)}
181 | spinnerDownIcon={extractValue.asOptionalString(node.props.spinnerDownIcon)}
182 | autoFocus={extractValue.asOptionalBoolean(node.props.autoFocus)}
183 | readOnly={extractValue.asOptionalBoolean(node.props.readOnly)}
184 | maxLength={extractValue(node.props.maxLength)}
185 | required={extractValue.asOptionalBoolean(node.props.required)}
186 | direction={extractValue(node.props.direction)}
187 | />
188 | );
189 | },
190 | );
191 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/TextBox/TextBoxNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { type CSSProperties, type ForwardedRef, forwardRef, useId, useState } from "react";
2 | import React, { useCallback, useEffect, useRef } from "react";
3 | import classnames from "classnames";
4 |
5 | import styles from "./TextBox.module.scss";
6 |
7 | import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs";
8 | import { noop } from "../../components-core/constants";
9 | import { useEvent } from "../../components-core/utils/misc";
10 | import { Adornment } from "../Input/InputAdornment";
11 | import type { ValidationStatus } from "../abstractions";
12 | import { PART_START_ADORNMENT, PART_INPUT, PART_END_ADORNMENT } from "../../components-core/parts";
13 |
14 | /**
15 | * TextBox component that supports text input with various configurations.
16 | * Features:
17 | * - Standard text, password, and search input types
18 | * - Input validation states
19 | * - Start/end adornments (icons and text)
20 | * - Password visibility toggle option
21 | */
22 |
23 | type Props = {
24 | id?: string;
25 | type?: "text" | "password" | "search";
26 | value?: string;
27 | updateState?: UpdateStateFn;
28 | initialValue?: string;
29 | style?: CSSProperties;
30 | className?: string;
31 | maxLength?: number;
32 | enabled?: boolean;
33 | placeholder?: string;
34 | validationStatus?: ValidationStatus;
35 | onDidChange?: (newValue: string) => void;
36 | onFocus?: () => void;
37 | onBlur?: () => void;
38 | onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
39 | registerComponentApi?: RegisterComponentApiFn;
40 | startText?: string;
41 | startIcon?: string;
42 | endText?: string;
43 | endIcon?: string;
44 | gap?: string;
45 | autoFocus?: boolean;
46 | readOnly?: boolean;
47 | tabIndex?: number;
48 | required?: boolean;
49 | /**
50 | * When true and type is "password", displays a toggle icon to show/hide password text
51 | * Default: false
52 | */
53 | showPasswordToggle?: boolean;
54 | /**
55 | * The icon to show when the password is visible
56 | * Default: "eye"
57 | */
58 | passwordVisibleIcon?: string;
59 | /**
60 | * The icon to show when the password is hidden
61 | * Default: "eye-off"
62 | */
63 | passwordHiddenIcon?: string;
64 | };
65 |
66 | export const defaultProps: Pick<
67 | Props,
68 | | "type"
69 | | "value"
70 | | "initialValue"
71 | | "enabled"
72 | | "validationStatus"
73 | | "onDidChange"
74 | | "onFocus"
75 | | "onBlur"
76 | | "onKeyDown"
77 | | "updateState"
78 | | "passwordVisibleIcon"
79 | | "passwordHiddenIcon"
80 | > = {
81 | type: "text",
82 | value: "",
83 | initialValue: "",
84 | enabled: true,
85 | validationStatus: "none",
86 | onDidChange: noop,
87 | onFocus: noop,
88 | onBlur: noop,
89 | onKeyDown: noop,
90 | updateState: noop,
91 | passwordVisibleIcon: "eye",
92 | passwordHiddenIcon: "eye-off",
93 | };
94 |
95 | export const TextBox = forwardRef(function TextBox(
96 | {
97 | id,
98 | type = defaultProps.type,
99 | value = defaultProps.value,
100 | updateState = defaultProps.updateState,
101 | initialValue = defaultProps.initialValue,
102 | style,
103 | className,
104 | maxLength,
105 | enabled = defaultProps.enabled,
106 | placeholder,
107 | validationStatus = defaultProps.validationStatus,
108 | onDidChange = defaultProps.onDidChange,
109 | onFocus = defaultProps.onFocus,
110 | onBlur = defaultProps.onBlur,
111 | onKeyDown = defaultProps.onKeyDown,
112 | registerComponentApi,
113 | startText,
114 | startIcon,
115 | endText,
116 | endIcon,
117 | gap,
118 | autoFocus,
119 | readOnly,
120 | tabIndex,
121 | required,
122 | showPasswordToggle,
123 | passwordVisibleIcon = defaultProps.passwordVisibleIcon,
124 | passwordHiddenIcon = defaultProps.passwordHiddenIcon,
125 | ...rest
126 | }: Props,
127 | ref: ForwardedRef<HTMLDivElement>,
128 | ) {
129 | const inputRef = useRef<HTMLInputElement>(null);
130 |
131 | // State to control password visibility
132 | const [showPassword, setShowPassword] = useState(false);
133 |
134 | // Determine the actual input type based on the password visibility toggle
135 | const actualType = type === "password" && showPassword ? "text" : type;
136 |
137 | // Toggle password visibility
138 | const togglePasswordVisibility = useCallback(() => {
139 | setShowPassword((prev) => !prev);
140 | }, []);
141 |
142 | useEffect(() => {
143 | if (autoFocus) {
144 | setTimeout(() => {
145 | inputRef.current?.focus();
146 | }, 0);
147 | }
148 | }, [autoFocus, inputRef]);
149 |
150 | // --- NOTE: This is a workaround for the jumping caret issue.
151 | // --- Local state can sync up values that can get set asynchronously outside the component.
152 | const [localValue, setLocalValue] = React.useState(value);
153 | useEffect(() => {
154 | setLocalValue(value);
155 | }, [value]);
156 | // --- End NOTE
157 |
158 | // --- Initialize the related field with the input's initial value
159 | useEffect(() => {
160 | updateState({ value: initialValue }, { initial: true });
161 | }, [initialValue, updateState]);
162 |
163 | const updateValue = useCallback(
164 | (value: string) => {
165 | setLocalValue(value);
166 | updateState({ value });
167 | onDidChange(value);
168 | },
169 | [onDidChange, updateState],
170 | );
171 |
172 | // --- Handle the value change events for this input
173 | const onInputChange = useCallback(
174 | (event: React.ChangeEvent<HTMLInputElement>) => {
175 | updateValue(event.target.value);
176 | },
177 | [updateValue],
178 | );
179 |
180 | // --- Manage obtaining and losing the focus
181 | const handleOnFocus = useCallback(() => {
182 | onFocus?.();
183 | }, [onFocus]);
184 |
185 | const handleOnBlur = useCallback(() => {
186 | onBlur?.();
187 | }, [onBlur]);
188 |
189 | const focus = useCallback(() => {
190 | inputRef.current?.focus();
191 | }, []);
192 |
193 | const setValue = useEvent((newValue) => {
194 | updateValue(newValue);
195 | });
196 |
197 | useEffect(() => {
198 | registerComponentApi?.({
199 | focus,
200 | setValue,
201 | });
202 | }, [focus, registerComponentApi, setValue]);
203 |
204 | return (
205 | <div
206 | {...rest}
207 | ref={ref}
208 | className={classnames(className, styles.inputRoot, {
209 | [styles.disabled]: !enabled,
210 | [styles.readOnly]: readOnly,
211 | [styles.error]: validationStatus === "error",
212 | [styles.warning]: validationStatus === "warning",
213 | [styles.valid]: validationStatus === "valid",
214 | })}
215 | tabIndex={-1}
216 | onFocus={focus}
217 | style={{ ...style, gap }}
218 | >
219 | <Adornment
220 | data-part-id={PART_START_ADORNMENT}
221 | text={startText}
222 | iconName={startIcon}
223 | className={classnames(styles.adornment)}
224 | />
225 | <input
226 | id={id}
227 | ref={inputRef}
228 | data-part-id={PART_INPUT}
229 | type={actualType}
230 | className={classnames(styles.input, {
231 | [styles.readOnly]: readOnly,
232 | })}
233 | disabled={!enabled}
234 | value={localValue}
235 | maxLength={maxLength}
236 | placeholder={placeholder}
237 | onChange={onInputChange}
238 | onFocus={handleOnFocus}
239 | onBlur={handleOnBlur}
240 | onKeyDown={onKeyDown}
241 | readOnly={readOnly}
242 | autoFocus={autoFocus}
243 | tabIndex={enabled ? tabIndex : -1}
244 | required={required}
245 | />
246 | {type === "password" && showPasswordToggle ? (
247 | <Adornment
248 | data-part-id={PART_END_ADORNMENT}
249 | iconName={showPassword ? passwordVisibleIcon : passwordHiddenIcon}
250 | className={classnames(styles.adornment, styles.passwordToggle)}
251 | onClick={togglePasswordVisibility}
252 | />
253 | ) : (
254 | <Adornment
255 | data-part-id={PART_END_ADORNMENT}
256 | text={endText}
257 | iconName={endIcon}
258 | className={styles.adornment}
259 | />
260 | )}
261 | </div>
262 | );
263 | });
264 |
```
--------------------------------------------------------------------------------
/packages/xmlui-website-blocks/src/HeroSection/HeroSectionNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { forwardRef, ReactNode } from "react";
2 | import { Button, Icon, Breakout, useTheme } from "xmlui";
3 |
4 | import styles from "./HeroSection.module.scss";
5 |
6 | import classnames from "classnames";
7 | import { Theme } from "xmlui";
8 |
9 | const PART_HEADER = "header";
10 | const PART_CONTENT = "content";
11 | const PART_HEADING_SECTION = "headingSection";
12 | const PART_PREAMBLE = "preamble";
13 | const PART_HEADLINE = "headline";
14 | const PART_SUBHEADLINE = "subheadline";
15 | const PART_MAIN_TEXT = "mainText";
16 | const PART_CTA_BUTTON = "ctaButton";
17 | const PART_IMAGE = "image";
18 | const PART_BACKGROUND = "background";
19 |
20 | type Props = {
21 | children?: ReactNode;
22 | backgroundTemplate?: ReactNode;
23 | contentPlacement?: "left" | "right" | "bottom";
24 | contentAlignment?: "start" | "center" | "end";
25 | headerWidth?: string | number;
26 | contentWidth?: string | number;
27 | gap?: string | number;
28 | headerAlignment?: string;
29 | preamble?: string;
30 | headline?: string;
31 | subheadline?: string;
32 | mainText?: string;
33 | mainTextTemplate?: ReactNode;
34 | ctaButtonIcon?: string;
35 | ctaButtonText?: string;
36 | ctaButtonTemplate?: ReactNode;
37 | image?: string;
38 | imageWidth?: number | string;
39 | imageHeight?: number | string;
40 | fullWidthBackground?: boolean;
41 | className?: string;
42 | headerTone?: string;
43 | contentTone?: string;
44 | onCtaClick?: () => void;
45 | };
46 | export const defaultProps: Pick<
47 | Props,
48 | "fullWidthBackground" | "contentPlacement" | "contentAlignment" | "headerWidth" | "contentWidth"
49 | > = {
50 | headerWidth: "50%",
51 | fullWidthBackground: true,
52 | contentPlacement: "bottom",
53 | contentAlignment: "center",
54 | contentWidth: "$maxWidth-content",
55 | };
56 |
57 | export const HeroSection = forwardRef(
58 | (
59 | {
60 | children,
61 | backgroundTemplate,
62 | headerAlignment,
63 | contentPlacement = defaultProps.contentPlacement,
64 | contentAlignment = defaultProps.contentAlignment,
65 | headerWidth = defaultProps.headerWidth,
66 | contentWidth,
67 | gap,
68 | preamble,
69 | headline,
70 | subheadline,
71 | mainText,
72 | mainTextTemplate,
73 | ctaButtonIcon,
74 | ctaButtonText,
75 | ctaButtonTemplate,
76 | image,
77 | imageWidth,
78 | imageHeight,
79 | fullWidthBackground = defaultProps.fullWidthBackground,
80 | className,
81 | headerTone,
82 | contentTone,
83 | onCtaClick,
84 | }: Props,
85 | ref: React.Ref<HTMLDivElement>,
86 | ) => {
87 | // Validate contentPlacement and default to "bottom" if invalid
88 | const validContentPlacements = ["left", "right", "bottom"] as const;
89 | const effectiveContentPlacement = validContentPlacements.includes(contentPlacement as any)
90 | ? contentPlacement
91 | : "bottom";
92 |
93 | // Default headerAlignment to "center" if not provided
94 | const effectiveHeaderAlignment = headerAlignment || "center";
95 | const isHorizontal =
96 | effectiveContentPlacement === "left" || effectiveContentPlacement === "right";
97 |
98 | // Optional tone colors
99 | const { activeThemeTone } = useTheme();
100 | const effectiveTone = (prop?: string) => {
101 | switch (prop) {
102 | case "light":
103 | case "dark":
104 | return prop;
105 | case "reverse":
106 | return activeThemeTone === "light" ? "dark" : "light";
107 | default:
108 | return activeThemeTone;
109 | }
110 | };
111 |
112 | let effectiveHeaderTone = effectiveTone(headerTone);
113 | let effectiveContentTone = effectiveTone(contentTone);
114 |
115 | // Only render CTA button if there's content for it
116 | const ctaButton =
117 | (ctaButtonTemplate || ctaButtonText) &&
118 | (ctaButtonTemplate || (
119 | <Button
120 | data-part-id={PART_CTA_BUTTON}
121 | className={classnames(styles.ctaButton)}
122 | icon={ctaButtonIcon && <Icon name={ctaButtonIcon} aria-hidden />}
123 | onClick={onCtaClick}
124 | >
125 | {ctaButtonText}
126 | </Button>
127 | ));
128 |
129 | // Header section (preamble to CTA button)
130 | const headerSection = (
131 | <div
132 | data-part-id={PART_HEADER}
133 | className={classnames(styles.header)}
134 | style={{
135 | width: isHorizontal ? headerWidth : undefined,
136 | flexShrink: isHorizontal ? 0 : undefined,
137 | }}
138 | >
139 | <Theme tone={effectiveHeaderTone}>
140 | <div
141 | data-part-id={PART_HEADING_SECTION}
142 | className={classnames(styles.headingSection, {
143 | [styles.start]: effectiveHeaderAlignment === "start",
144 | [styles.center]: effectiveHeaderAlignment === "center",
145 | [styles.end]: effectiveHeaderAlignment === "end",
146 | })}
147 | >
148 | {preamble && (
149 | <div data-part-id={PART_PREAMBLE} className={styles.preamble}>
150 | {preamble}
151 | </div>
152 | )}
153 | {headline && (
154 | <div data-part-id={PART_HEADLINE} className={styles.headline}>
155 | {headline}
156 | </div>
157 | )}
158 | {subheadline && (
159 | <div data-part-id={PART_SUBHEADLINE} className={styles.subheadline}>
160 | {subheadline}
161 | </div>
162 | )}
163 | {mainTextTemplate && (
164 | <div data-part-id={PART_MAIN_TEXT} className={styles.textWrapper}>
165 | {mainTextTemplate}
166 | </div>
167 | )}
168 | {!mainTextTemplate && mainText && (
169 | <div data-part-id={PART_MAIN_TEXT} className={styles.mainText}>
170 | {mainText}
171 | </div>
172 | )}
173 | {ctaButton && <div className={styles.ctaButtonWrapper}>{ctaButton}</div>}
174 | </div>
175 | </Theme>
176 | </div>
177 | );
178 |
179 | // Content section (image + children)
180 | const contentSection = (
181 | <div
182 | data-part-id={PART_CONTENT}
183 | className={classnames(styles.content, {
184 | [styles.contentStart]: contentAlignment === "start",
185 | [styles.contentCenter]: contentAlignment === "center",
186 | [styles.contentEnd]: contentAlignment === "end",
187 | })}
188 | >
189 | <Theme tone={effectiveContentTone}>
190 | <>
191 | {image && (
192 | <img
193 | data-part-id={PART_IMAGE}
194 | className={styles.image}
195 | src={image}
196 | style={{ width: imageWidth, height: imageHeight }}
197 | aria-hidden
198 | />
199 | )}
200 | {children}
201 | </>
202 | </Theme>
203 | </div>
204 | );
205 |
206 | const heroContent = (
207 | <div
208 | ref={ref}
209 | data-part-id={PART_BACKGROUND}
210 | className={classnames(styles.heroWrapper, className)}
211 | >
212 | {backgroundTemplate && (
213 | <div className={styles.backgroundTemplate}>{backgroundTemplate}</div>
214 | )}
215 | <div
216 | className={classnames(styles.heroContent, {
217 | [styles.horizontal]: isHorizontal,
218 | [styles.vertical]: !isHorizontal,
219 | })}
220 | style={{ gap, width: contentWidth }}
221 | >
222 | {effectiveContentPlacement === "left" && contentSection}
223 | {headerSection}
224 | {(effectiveContentPlacement === "right" || effectiveContentPlacement === "bottom") &&
225 | contentSection}
226 | </div>
227 | </div>
228 | );
229 |
230 | return fullWidthBackground ? <Breakout>{heroContent}</Breakout> : heroContent;
231 | },
232 | );
233 |
```
--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/parser-expressions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, expect, it } from "vitest";
2 |
3 | import { Parser } from "../../../src/parsers/scripting/Parser";
4 | import {
5 | FunctionInvocationExpression,
6 | MemberAccessExpression,
7 | PostfixOpExpression,
8 | PrefixOpExpression,
9 | SequenceExpression,
10 | SpreadExpression,
11 | T_ARRAY_LITERAL,
12 | T_BINARY_EXPRESSION,
13 | T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
14 | T_CONDITIONAL_EXPRESSION,
15 | T_FUNCTION_INVOCATION_EXPRESSION,
16 | T_IDENTIFIER,
17 | T_LITERAL,
18 | T_MEMBER_ACCESS_EXPRESSION,
19 | T_POSTFIX_OP_EXPRESSION,
20 | T_PREFIX_OP_EXPRESSION,
21 | T_SEQUENCE_EXPRESSION,
22 | T_SPREAD_EXPRESSION,
23 | T_UNARY_EXPRESSION,
24 | } from "../../../src/components-core/script-runner/ScriptingSourceTree";
25 |
26 | describe("Parser - miscellaneous expressions", () => {
27 | const sequenceCases = [
28 | { src: "a, b, a+b", len: 3, idx: 0, exp: T_IDENTIFIER },
29 | { src: "a, b, a+b", len: 3, idx: 1, exp: T_IDENTIFIER },
30 | { src: "a, b, a+b", len: 3, idx: 2, exp: T_BINARY_EXPRESSION },
31 | { src: "a(b), b.a, a[b], !a", len: 4, idx: 0, exp: T_FUNCTION_INVOCATION_EXPRESSION },
32 | { src: "a(b), b.a, a[b], !a", len: 4, idx: 1, exp: T_MEMBER_ACCESS_EXPRESSION },
33 | { src: "a(b), b.a, a[b], !a", len: 4, idx: 2, exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
34 | { src: "a(b), b.a, a[b], !a", len: 4, idx: 3, exp: T_UNARY_EXPRESSION },
35 | { src: 'a, 12.3, "Hello"', len: 3, idx: 1, exp: T_LITERAL },
36 | { src: 'a, 12.3, "Hello"', len: 3, idx: 2, exp: T_LITERAL },
37 | ];
38 | sequenceCases.forEach((c) => {
39 | it(`Sequence expression: ${c.src}`, () => {
40 | // --- Arrange
41 | const wParser = new Parser(c.src);
42 |
43 | // --- Act
44 | const expr = wParser.parseExpr();
45 |
46 | // --- Assert
47 | expect(expr).not.equal(null);
48 | if (!expr) return;
49 |
50 | expect(expr.type).equal(T_SEQUENCE_EXPRESSION);
51 | const sequence = expr as SequenceExpression;
52 | expect(sequence.exprs.length).equal(c.len);
53 | expect(sequence.exprs[c.idx].type).equal(c.exp);
54 | });
55 | });
56 |
57 | const invocationCases = [
58 | { src: "func()", len: 0, idx: -1, exp: null },
59 | { src: "func(a, b)", len: 2, idx: 0, exp: T_IDENTIFIER },
60 | { src: "func(a, b)", len: 2, idx: 1, exp: T_IDENTIFIER },
61 | { src: "func(123, a+b, a[b])", len: 3, idx: 0, exp: T_LITERAL },
62 | { src: "func(123, a+b, a[b])", len: 3, idx: 1, exp: T_BINARY_EXPRESSION },
63 | { src: "func(123, a+b, a[b])", len: 3, idx: 2, exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
64 | ];
65 | invocationCases.forEach((c) => {
66 | it(`FunctionInvocation: ${c.src}`, () => {
67 | // --- Arrange
68 | const wParser = new Parser(c.src);
69 |
70 | // --- Act
71 | const expr = wParser.parseExpr();
72 |
73 | // --- Assert
74 | expect(expr).not.equal(null);
75 | if (!expr) return;
76 | expect(expr.type).equal(T_FUNCTION_INVOCATION_EXPRESSION);
77 | const invocation = expr as FunctionInvocationExpression;
78 | expect(invocation.obj.type).equal(T_IDENTIFIER);
79 | expect(invocation.arguments.length).equal(c.len);
80 | if (c.len > 0) {
81 | // eslint-disable-next-line jest/no-conditional-expect
82 | expect(invocation.arguments[c.idx].type).equal(c.exp);
83 | }
84 | });
85 | });
86 |
87 | const objectCases = [
88 | { src: "func()", exp: T_IDENTIFIER },
89 | { src: "(+a)()", exp: T_UNARY_EXPRESSION },
90 | { src: "(a+b)()", exp: T_BINARY_EXPRESSION },
91 | { src: "(a ? b : c)()", exp: T_CONDITIONAL_EXPRESSION },
92 | { src: "(123)()", exp: T_LITERAL },
93 | { src: '("Hello")()', exp: T_LITERAL },
94 | { src: "(func(a, b))()", exp: T_FUNCTION_INVOCATION_EXPRESSION },
95 | { src: "(a.b)()", exp: T_MEMBER_ACCESS_EXPRESSION },
96 | { src: "(a[b])()", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
97 | ];
98 | objectCases.forEach((c) => {
99 | it(`FunctionInvocation object: ${c.src}`, () => {
100 | // --- Arrange
101 | const wParser = new Parser(c.src);
102 |
103 | // --- Act
104 | const expr = wParser.parseExpr();
105 |
106 | // --- Assert
107 | expect(expr).not.equal(null);
108 | if (!expr) return;
109 | expect(expr.type).equal(T_FUNCTION_INVOCATION_EXPRESSION);
110 | const invocation = expr as FunctionInvocationExpression;
111 | expect(invocation.obj.type).equal(c.exp);
112 | });
113 | });
114 |
115 | const memberAccessCases = [
116 | { src: "a.b", exp: T_IDENTIFIER },
117 | { src: "(+a).b", exp: T_UNARY_EXPRESSION },
118 | { src: "(a+b).b", exp: T_BINARY_EXPRESSION },
119 | { src: "(a ? b : c).b", exp: T_CONDITIONAL_EXPRESSION },
120 | { src: "(123).b", exp: T_LITERAL },
121 | { src: '("Hello").b', exp: T_LITERAL },
122 | { src: "(func(a, b)).b", exp: T_FUNCTION_INVOCATION_EXPRESSION },
123 | { src: "(a.b).b", exp: T_MEMBER_ACCESS_EXPRESSION },
124 | { src: "(a[b]).b", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
125 | ];
126 | memberAccessCases.forEach((c) => {
127 | it(`MemberAccess: ${c.src}`, () => {
128 | // --- Arrange
129 | const wParser = new Parser(c.src);
130 |
131 | // --- Act
132 | const expr = wParser.parseExpr();
133 |
134 | // --- Assert
135 | expect(expr).not.equal(null);
136 | if (!expr) return;
137 | expect(expr.type).equal(T_MEMBER_ACCESS_EXPRESSION);
138 | const memberAcc = expr as MemberAccessExpression;
139 | expect(memberAcc.member).eq("b");
140 | expect(memberAcc.obj.type).equal(c.exp);
141 | });
142 | });
143 |
144 | const spreadCases = [
145 | { src: "...[1, 2, 3]", exp: T_ARRAY_LITERAL },
146 | { src: "...apple", exp: T_IDENTIFIER },
147 | ];
148 | spreadCases.forEach((c) => {
149 | it(`Spread: ${c.src}`, () => {
150 | // --- Arrange
151 | const wParser = new Parser(c.src);
152 |
153 | // --- Act
154 | const expr = wParser.parseExpr();
155 |
156 | // --- Assert
157 | expect(expr).not.equal(null);
158 | if (!expr) return;
159 | expect(expr.type).equal(T_SPREAD_EXPRESSION);
160 | const spread = expr as SpreadExpression;
161 | expect(spread.expr.type).equal(c.exp);
162 | });
163 | });
164 |
165 | const prefixCases = [
166 | { src: "++i", op: "++", exp: T_IDENTIFIER },
167 | { src: "++j[2]", op: "++", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
168 | { src: "--i", op: "--", exp: T_IDENTIFIER },
169 | { src: "--j[2]", op: "--", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
170 | ];
171 | prefixCases.forEach((c) => {
172 | it(`Prefix: ${c.src}`, () => {
173 | // --- Arrange
174 | const wParser = new Parser(c.src);
175 |
176 | // --- Act
177 | const expr = wParser.parseExpr();
178 |
179 | // --- Assert
180 | expect(expr).not.equal(null);
181 | if (!expr) return;
182 | expect(expr.type).equal(T_PREFIX_OP_EXPRESSION);
183 | const prefixExpr = expr as PrefixOpExpression;
184 | expect(prefixExpr.expr.type).equal(c.exp);
185 | expect(prefixExpr.op).equal(c.op);
186 | });
187 | });
188 |
189 | const postfixCases = [
190 | { src: "i++", op: "++", exp: T_IDENTIFIER },
191 | { src: "j[2]++", op: "++", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
192 | { src: "i--", op: "--", exp: T_IDENTIFIER },
193 | { src: "j[2]--", op: "--", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
194 | ];
195 | postfixCases.forEach((c) => {
196 | it(`Postfix: ${c.src}`, () => {
197 | // --- Arrange
198 | const wParser = new Parser(c.src);
199 |
200 | // --- Act
201 | const expr = wParser.parseExpr();
202 |
203 | // --- Assert
204 | expect(expr).not.equal(null);
205 | if (!expr) return;
206 | expect(expr.type).equal(T_POSTFIX_OP_EXPRESSION);
207 | const postfixExpr = expr as PostfixOpExpression;
208 | expect(postfixExpr.expr.type).equal(c.exp);
209 | expect(postfixExpr.op).equal(c.op);
210 | });
211 | });
212 | });
213 |
214 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/AutoComplete/AutoComplete.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./AutoComplete.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import { MemoizedItem } from "../container-helpers";
6 | import {
7 | dPlaceholder,
8 | dInitialValue,
9 | dMaxLength,
10 | dAutoFocus,
11 | dRequired,
12 | dReadonly,
13 | dEnabled,
14 | dValidationStatus,
15 | dComponent,
16 | dDidChange,
17 | dGotFocus,
18 | dLostFocus,
19 | dMulti,
20 | createMetadata,
21 | d,
22 | } from "../metadata-helpers";
23 | import { AutoComplete, defaultProps } from "./AutoCompleteNative";
24 |
25 | const COMP = "AutoComplete";
26 |
27 | export const AutoCompleteMd = createMetadata({
28 | status: "experimental",
29 | description:
30 | "`AutoComplete` is a searchable dropdown input that allows users to type and " +
31 | "filter through options, with support for single or multiple selections. Unlike " +
32 | "a basic [`Select`](/components/Select), it provides type-ahead functionality " +
33 | "and can allow users to create new options.",
34 | props: {
35 | placeholder: dPlaceholder(),
36 | initialValue: dInitialValue(),
37 | maxLength: dMaxLength(),
38 | autoFocus: {
39 | ...dAutoFocus(),
40 | defaultValue: defaultProps.autoFocus,
41 | },
42 | required: {
43 | ...dRequired(),
44 | defaultValue: defaultProps.required,
45 | },
46 | readOnly: {
47 | ...dReadonly(),
48 | defaultValue: defaultProps.readOnly,
49 | },
50 | enabled: {
51 | ...dEnabled(),
52 | defaultValue: defaultProps.enabled,
53 | },
54 | initiallyOpen: d(
55 | `This property determines whether the dropdown list is open when the component is first rendered.`,
56 | null,
57 | "boolean",
58 | defaultProps.initiallyOpen,
59 | ),
60 | creatable: d(
61 | `This property allows the user to create new items that are not present in the list of options.`,
62 | null,
63 | "boolean",
64 | defaultProps.creatable,
65 | ),
66 | validationStatus: {
67 | ...dValidationStatus(),
68 | defaultValue: defaultProps.validationStatus,
69 | },
70 | dropdownHeight: d("This property sets the height of the dropdown list."),
71 | multi: {
72 | ...dMulti(),
73 | defaultValue: defaultProps.multi,
74 | },
75 | optionTemplate: dComponent(
76 | `This property enables the customization of list items. To access the attributes of ` +
77 | `a list item use the \`$item\` context variable.`,
78 | ),
79 | emptyListTemplate: dComponent(
80 | "This property defines the template to display when the list of options is empty.",
81 | ),
82 | },
83 | events: {
84 | gotFocus: dGotFocus(COMP),
85 | lostFocus: dLostFocus(COMP),
86 | didChange: dDidChange(COMP),
87 | itemCreated: {
88 | description:
89 | "This event is triggered when a new item is created by the user " +
90 | "(if `creatable` is enabled).",
91 | },
92 | },
93 | apis: {
94 | focus: {
95 | description: `This method focuses the ${COMP} component.`,
96 | signature: "focus()",
97 | },
98 | value: {
99 | description:
100 | "This API allows you to get or set the value of the component. If no value is set, " +
101 | "it will retrieve `undefined`.",
102 | signature: "get value(): any",
103 | },
104 | setValue: {
105 | description:
106 | "This API allows you to set the value of the component. If the value is not valid, " +
107 | "the component will not update its internal state.",
108 | signature: "setValue(value: any)",
109 | parameters: {
110 | value: "The value to set.",
111 | },
112 | },
113 | },
114 | contextVars: {
115 | $item: d(
116 | "This context value represents an item when you define an option item template. " +
117 | "Use `$item.value` and `$item.label` to refer to the value and label of the " +
118 | "particular option.",
119 | ),
120 | },
121 | themeVars: parseScssVar(styles.themeVars),
122 | defaultThemeVars: {
123 | [`backgroundColor-menu-${COMP}`]: "$color-surface-raised",
124 | [`boxShadow-menu-${COMP}`]: "$boxShadow-md",
125 | [`borderRadius-menu-${COMP}`]: "$borderRadius",
126 | [`borderWidth-menu-${COMP}`]: "1px",
127 | [`borderColor-menu-${COMP}`]: "$borderColor",
128 | [`backgroundColor-${COMP}-badge`]: "$color-primary-500",
129 | [`fontSize-${COMP}-badge`]: "$fontSize-sm",
130 | [`paddingHorizontal-${COMP}-badge`]: "$space-2_5",
131 | [`paddingVertical-${COMP}-badge`]: "$space-0_5",
132 | [`borderRadius-${COMP}-badge`]: "$borderRadius",
133 | [`paddingHorizontal-item-${COMP}`]: "$space-2",
134 | [`paddingVertical-item-${COMP}`]: "$space-2",
135 | [`paddingHorizontal-${COMP}`]: "$space-2",
136 | [`paddingVertical-${COMP}`]: "$space-2",
137 | [`opacity-text-item-${COMP}--disabled`]: "0.5",
138 | [`opacity-${COMP}--disabled`]: "0.5",
139 | [`backgroundColor-${COMP}-badge--hover`]: "$color-primary-400",
140 | [`backgroundColor-${COMP}-badge--active`]: "$color-primary-500",
141 | [`textColor-item-${COMP}--disabled`]: "$color-surface-200",
142 | [`textColor-${COMP}-badge`]: "$const-color-surface-50",
143 | [`backgroundColor-item-${COMP}`]: "$backgroundColor-dropdown-item",
144 | [`backgroundColor-item-${COMP}--hover`]: "$backgroundColor-dropdown-item--hover",
145 | [`backgroundColor-item-${COMP}--active`]: "$backgroundColor-dropdown-item--active",
146 | // Default borderColor-Input--disabled is too light so the disabled component is barely visible
147 | [`borderColor-${COMP}--disabled`]: "initial",
148 | },
149 | });
150 |
151 | export const autoCompleteComponentRenderer = createComponentRenderer(
152 | COMP,
153 | AutoCompleteMd,
154 | ({
155 | node,
156 | state,
157 | updateState,
158 | extractValue,
159 | renderChild,
160 | lookupEventHandler,
161 | registerComponentApi,
162 | className,
163 | }) => {
164 | return (
165 | <AutoComplete
166 | multi={extractValue.asOptionalBoolean(node.props.multi)}
167 | className={className}
168 | updateState={updateState}
169 | initialValue={extractValue(node.props.initialValue)}
170 | value={state?.value}
171 | creatable={extractValue.asOptionalBoolean(node.props.creatable)}
172 | autoFocus={extractValue.asOptionalBoolean(node.props.autoFocus)}
173 | enabled={extractValue.asOptionalBoolean(node.props.enabled)}
174 | placeholder={extractValue.asOptionalString(node.props.placeholder)}
175 | validationStatus={extractValue(node.props.validationStatus)}
176 | onDidChange={lookupEventHandler("didChange")}
177 | onFocus={lookupEventHandler("gotFocus")}
178 | onBlur={lookupEventHandler("lostFocus")}
179 | onItemCreated={lookupEventHandler("itemCreated")}
180 | registerComponentApi={registerComponentApi}
181 | emptyListTemplate={renderChild(node.props.emptyListTemplate)}
182 | dropdownHeight={extractValue(node.props.dropdownHeight)}
183 | readOnly={extractValue.asOptionalBoolean(node.props.readOnly)}
184 | initiallyOpen={extractValue.asOptionalBoolean(node.props.initiallyOpen)}
185 | optionRenderer={
186 | node.props.optionTemplate
187 | ? (item, val, inTrigger) => {
188 | return (
189 | <MemoizedItem
190 | node={node.props.optionTemplate}
191 | item={item}
192 | context={{
193 | $selectedValue: val,
194 | $inTrigger: inTrigger,
195 | }}
196 | renderChild={renderChild}
197 | />
198 | );
199 | }
200 | : undefined
201 | }
202 | >
203 | {renderChild(node.children)}
204 | </AutoComplete>
205 | );
206 | },
207 | );
208 |
```