This is page 28 of 183. Use http://codebase.md/xmlui-org/xmlui/xmlui-latest.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-optimized.yml
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── an-advanced-codefence.gif
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── codefence-runner.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ ├── lorem-ipsum.png
│ │ │ │ ├── playground-checkbox-source.png
│ │ │ │ ├── playground.png
│ │ │ │ ├── use-xmlui-mcp-to-find-a-howto.png
│ │ │ │ └── xmlui-demo-gallery.png
│ │ │ ├── introducing-xmlui.md
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ ├── xmlui-playground.md
│ │ │ └── xmlui-powered-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── FancyButton.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── control-cache-invalidation.md
│ │ │ │ ├── debounce-user-input-for-api-calls.md
│ │ │ │ ├── debounce-with-changelistener.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── tsconfig.json
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── Animation.tsx
│ │ ├── AnimationNative.tsx
│ │ ├── FadeAnimation.tsx
│ │ ├── FadeInAnimation.tsx
│ │ ├── FadeOutAnimation.tsx
│ │ ├── index.tsx
│ │ ├── ScaleAnimation.tsx
│ │ └── SlideInAnimation.tsx
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── HelloWorld.module.scss
│ │ ├── HelloWorld.tsx
│ │ ├── HelloWorldNative.tsx
│ │ └── index.tsx
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── IPhoneFrame.module.scss
│ │ ├── IPhoneFrame.tsx
│ │ ├── MacOSAppFrame.module.scss
│ │ ├── MacOSAppFrame.tsx
│ │ ├── WindowsAppFrame.module.scss
│ │ └── WindowsAppFrame.tsx
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── LazyPdfNative.tsx
│ │ ├── Pdf.module.scss
│ │ └── Pdf.tsx
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── hooks
│ │ │ ├── usePlayground.ts
│ │ │ └── useToast.ts
│ │ ├── index.tsx
│ │ ├── playground
│ │ │ ├── Box.module.scss
│ │ │ ├── Box.tsx
│ │ │ ├── CodeSelector.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
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Search.module.scss
│ │ └── Search.tsx
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Spreadsheet.tsx
│ │ └── SpreadsheetNative.tsx
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ └── src
│ ├── Carousel
│ │ ├── Carousel.module.scss
│ │ ├── Carousel.tsx
│ │ ├── CarouselContext.tsx
│ │ └── CarouselNative.tsx
│ ├── FancyButton
│ │ ├── FancyButton.module.scss
│ │ ├── FancyButton.tsx
│ │ └── FancyButton.xmlui
│ ├── Hello
│ │ ├── Hello.tsx
│ │ ├── Hello.xmlui
│ │ └── Hello.xmlui.xs
│ ├── HeroSection
│ │ ├── HeroSection.module.scss
│ │ ├── HeroSection.spec.ts
│ │ ├── HeroSection.tsx
│ │ └── HeroSectionNative.tsx
│ ├── index.tsx
│ ├── ScrollToTop
│ │ ├── ScrollToTop.module.scss
│ │ ├── ScrollToTop.tsx
│ │ └── ScrollToTopNative.tsx
│ └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.cjs
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── 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
├── 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.js
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.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.js
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.bin.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/docs/content/components/Slot.md:
--------------------------------------------------------------------------------
```markdown
1 | # Slot [#slot]
2 |
3 | Placeholder in a reusable component. Signs the slot where the component's injected children should be rendered.
4 |
5 | ## Using Slot [#using-slot]
6 |
7 | You can add `Slot` to a user-defined component as a placeholder. When you refer to the particular component in the markup, the children are transposed to the `Slot`.
8 |
9 | ```xmlui-pg name="Using Slot"
10 | ---app copy display {3-5}
11 | <App name="XMLUI Hello World">
12 | <ActionBar>
13 | <Button label="Create" onClick="window.alert('Create clicked')" />
14 | <Button label="Edit" onClick="window.alert('Edit clicked')" />
15 | <Button label="Delete" onClick="window.alert('Delete clicked')" />
16 | </ActionBar>
17 | </App>
18 | ---desc
19 | The app flows down three buttons to the `ActionBar` to render.
20 | ---comp copy display {5}
21 | <Component name="ActionBar">
22 | <Card>
23 | <H3>Use these actions</H3>
24 | <HStack>
25 | <Slot />
26 | </HStack>
27 | </Card>
28 | </Component>
29 | ---desc
30 | `ActionBar` renders the passed children by replacing `Slot` with them.
31 | ```
32 |
33 | ## Default Slot content [#default-slot-content]
34 |
35 | You can provide default content for the `Slot`. If the user-defined component does not have any children, XMLUI will render the default content.
36 |
37 | ```xmlui-pg
38 | ---app copy display name="Define default Slot content"
39 | <App>
40 | <ActionBar />
41 | </App>
42 | ---comp copy display {6}
43 | <Component name="ActionBar">
44 | <Card>
45 | <H3>Use these actions</H3>
46 | <HStack>
47 | <Slot>
48 | <Button label="Default" onClick="window.alert('Default clicked')" />
49 | </Slot>
50 | </HStack>
51 | </Card>
52 | </Component>
53 | ```
54 |
55 | ## Named Slots [#named-slots]
56 |
57 | You can add multiple slots to a user-defined component; you can have a default slot and several *named* slots. Slot names should end with `template`, and you can use the `<property>` markup syntax to declare their values.
58 |
59 | ```xmlui-pg
60 | ---app copy display name="Using named Slots" {4, 7, 9-11}
61 | <App>
62 | <ActionBar>
63 | <property name="headerTemplate">
64 | <H2>Click one of these actions</H2>
65 | </property>
66 | <property name="footerTemplate">
67 | <Text>Footer content goes here</Text>
68 | </property>
69 | <Button label="Create" onClick="window.alert('Create clicked')" />
70 | <Button label="Edit" onClick="window.alert('Edit clicked')" />
71 | <Button label="Delete" onClick="window.alert('Delete clicked')" />
72 | </ActionBar>
73 | </App>
74 | ---desc
75 | This app passes a header template and a footer template slot to the `ActionBar` component and also declares buttons to render.
76 | ---comp copy display {3-5, 7-9, 11}
77 | <Component name="ActionBar">
78 | <Card>
79 | <Slot name="headerTemplate">
80 | <H3>Use these actions</H3>
81 | </Slot>
82 | <HStack>
83 | <Slot>
84 | <Button label="Default" onClick="window.alert('Default clicked')" />
85 | </Slot>
86 | </HStack>
87 | <Slot name="footerTemplate" />
88 | </Card>
89 | </Component>
90 | ---desc
91 | XMLUI finds the appropriate slots by their name and transposes their content received from the app. Just like the default slot, named slots can have default content.
92 | ```
93 |
94 | > [!WARN] XMLUI will display an error message when the `Slot` name does not end with "Template".
95 |
96 | ## Template properties [#template-properties]
97 |
98 | The user-defined component can provide properties for the actual template.
99 |
100 | ```xmlui-pg
101 | ---app copy display name="Using template properties" /header/ /name="headerTemplate"/ /$processedHeader/
102 | <App>
103 | <ActionBar header="Action Bar Example">
104 | <property name="headerTemplate">
105 | <Text variant="title">{$processedHeader}</Text>
106 | </property>
107 | <Button label="Create" onClick="window.alert('Create clicked')" />
108 | <Button label="Edit" onClick="window.alert('Edit clicked')" />
109 | <Button label="Delete" onClick="window.alert('Delete clicked')" />
110 | </ActionBar>
111 | </App>
112 | ---desc
113 | The app passes a `header` property value to the `ActionBar` component. `Actionbar` utilizes this property, transforms it, and passes it back to the template in the `$processedHeader` context variable so that the app can use it. `$processHeader` is available only within the `headerTemplate` slot.
114 | ---comp copy display /transformedHeader/ /processedHeader="{transformedHeader}"/
115 | <Component name="ActionBar">
116 | <Card var.transformedHeader="*** {$props.header.toUpperCase()} ***">
117 | <Slot name="headerTemplate" processedHeader="{transformedHeader}" >
118 | <H3>{transformedHeader}</H3>
119 | </Slot>
120 | <HStack>
121 | <Slot>
122 | <Button label="Default" onClick="window.alert('Default clicked')" />
123 | </Slot>
124 | </HStack>
125 | </Card>
126 | </Component>
127 | ---desc
128 | `Actionbar` transforms the `header` property and stores it internally in the `transformedHeader` variable. It utilizes the value in the default header definition and also passes it back to the actual template definition with the `processedHeader` name. XMLUI creates the `$processedHeader` context variable from this name.
129 | ```
130 |
131 | ## Properties [#properties]
132 |
133 | ### `name` [#name]
134 |
135 | This optional property defines the name of the slot.
136 |
137 | ## Events [#events]
138 |
139 | This component does not have any events.
140 |
141 | ## Exposed Methods [#exposed-methods]
142 |
143 | This component does not expose any methods.
144 |
145 | ## Styling [#styling]
146 |
147 | This component does not have any styles.
148 |
```
--------------------------------------------------------------------------------
/tools/create-app/templates/default/ts/public/resources/xmlui-logo.svg:
--------------------------------------------------------------------------------
```
1 | <svg width="115" height="35" viewBox="0 0 46 14" fill="none"
2 | xmlns="http://www.w3.org/2000/svg">
3 | <path d="M19.8926 3.32031V9.57227H23.4199C23.7012 9.57227 23.916 9.64062 24.0645 9.77734C24.2168 9.91406 24.293 10.0859 24.293 10.293C24.293 10.5039 24.2188 10.6758 24.0703 10.8086C23.9219 10.9375 23.7051 11.002 23.4199 11.002H19.2188C18.8398 11.002 18.5664 10.918 18.3984 10.75C18.2344 10.582 18.1523 10.3105 18.1523 9.93555V3.32031C18.1523 2.96875 18.2305 2.70508 18.3867 2.5293C18.5469 2.35352 18.7559 2.26562 19.0137 2.26562C19.2754 2.26562 19.4863 2.35352 19.6465 2.5293C19.8105 2.70117 19.8926 2.96484 19.8926 3.32031Z" fill="#008EE5" />
4 | <path d="M11.0918 9.70898L9.71484 4.23633V10.166C9.71484 10.4941 9.64062 10.7402 9.49219 10.9043C9.34766 11.0684 9.1543 11.1504 8.91211 11.1504C8.67773 11.1504 8.48633 11.0703 8.33789 10.9102C8.18945 10.7461 8.11523 10.498 8.11523 10.166V3.36914C8.11523 2.99414 8.21289 2.74219 8.4082 2.61328C8.60352 2.48047 8.86719 2.41406 9.19922 2.41406H9.73828C10.0625 2.41406 10.2969 2.44336 10.4414 2.50195C10.5898 2.56055 10.6992 2.66602 10.7695 2.81836C10.8398 2.9707 10.9199 3.21875 11.0098 3.5625L12.2578 8.26758L13.5059 3.5625C13.5957 3.21875 13.6758 2.9707 13.7461 2.81836C13.8164 2.66602 13.9238 2.56055 14.0684 2.50195C14.2168 2.44336 14.4531 2.41406 14.7773 2.41406H15.3164C15.6484 2.41406 15.9121 2.48047 16.1074 2.61328C16.3027 2.74219 16.4004 2.99414 16.4004 3.36914V10.166C16.4004 10.4941 16.3262 10.7402 16.1777 10.9043C16.0332 11.0684 15.8379 11.1504 15.5918 11.1504C15.3613 11.1504 15.1719 11.0684 15.0234 10.9043C14.875 10.7402 14.8008 10.4941 14.8008 10.166V4.23633L13.4238 9.70898C13.334 10.0645 13.2598 10.3262 13.2012 10.4941C13.1465 10.6582 13.043 10.8086 12.8906 10.9453C12.7383 11.082 12.5273 11.1504 12.2578 11.1504C12.0547 11.1504 11.8828 11.1055 11.7422 11.0156C11.6016 10.9297 11.4922 10.8184 11.4141 10.6816C11.3359 10.5449 11.2734 10.3945 11.2266 10.2305C11.1836 10.0625 11.1387 9.88867 11.0918 9.70898Z" fill="#008EE5" />
5 | <path d="M0.621094 9.33203L2.54297 6.52539L0.925781 4.0293C0.773438 3.78711 0.658203 3.58008 0.580078 3.4082C0.505859 3.23242 0.46875 3.06445 0.46875 2.9043C0.46875 2.74023 0.541016 2.59375 0.685547 2.46484C0.833984 2.33203 1.01367 2.26562 1.22461 2.26562C1.4668 2.26562 1.6543 2.33789 1.78711 2.48242C1.92383 2.62305 2.11133 2.88672 2.34961 3.27344L3.63867 5.35938L5.01562 3.27344C5.12891 3.09766 5.22461 2.94727 5.30273 2.82227C5.38477 2.69727 5.46289 2.59375 5.53711 2.51172C5.61133 2.42969 5.69336 2.36914 5.7832 2.33008C5.87695 2.28711 5.98438 2.26562 6.10547 2.26562C6.32422 2.26562 6.50195 2.33203 6.63867 2.46484C6.7793 2.59375 6.84961 2.74805 6.84961 2.92773C6.84961 3.18945 6.69922 3.54492 6.39844 3.99414L4.70508 6.52539L6.52734 9.33203C6.69141 9.57812 6.81055 9.7832 6.88477 9.94727C6.95898 10.1074 6.99609 10.2598 6.99609 10.4043C6.99609 10.541 6.96289 10.666 6.89648 10.7793C6.83008 10.8926 6.73633 10.9824 6.61523 11.0488C6.49414 11.1152 6.35742 11.1484 6.20508 11.1484C6.04102 11.1484 5.90234 11.1133 5.78906 11.043C5.67578 10.9766 5.58398 10.8926 5.51367 10.791C5.44336 10.6895 5.3125 10.4922 5.12109 10.1992L3.60938 7.82031L2.00391 10.2695C1.87891 10.4648 1.78906 10.6016 1.73438 10.6797C1.68359 10.7578 1.62109 10.834 1.54688 10.9082C1.47266 10.9824 1.38477 11.041 1.2832 11.084C1.18164 11.127 1.0625 11.1484 0.925781 11.1484C0.714844 11.1484 0.539062 11.084 0.398438 10.9551C0.261719 10.8262 0.193359 10.6387 0.193359 10.3926C0.193359 10.1035 0.335938 9.75 0.621094 9.33203Z" fill="#008EE5" />
6 | <rect x="27" width="19" height="14" rx="2" fill="#008EE5" />
7 | <path d="M40.125 10.0879V3.32031C40.125 2.96875 40.2051 2.70508 40.3652 2.5293C40.5254 2.35352 40.7324 2.26562 40.9863 2.26562C41.248 2.26562 41.459 2.35352 41.6191 2.5293C41.7832 2.70117 41.8652 2.96484 41.8652 3.32031V10.0879C41.8652 10.4434 41.7832 10.709 41.6191 10.8848C41.459 11.0605 41.248 11.1484 40.9863 11.1484C40.7363 11.1484 40.5293 11.0605 40.3652 10.8848C40.2051 10.7051 40.125 10.4395 40.125 10.0879Z" fill="white" />
8 | <path d="M30.9492 7.45117V3.32031C30.9492 2.96875 31.0273 2.70508 31.1836 2.5293C31.3438 2.35352 31.5527 2.26562 31.8105 2.26562C32.0801 2.26562 32.293 2.35352 32.4492 2.5293C32.6094 2.70508 32.6895 2.96875 32.6895 3.32031V7.54492C32.6895 8.02539 32.7422 8.42773 32.8477 8.75195C32.957 9.07227 33.1484 9.32227 33.4219 9.50195C33.6953 9.67773 34.0781 9.76562 34.5703 9.76562C35.25 9.76562 35.7305 9.58594 36.0117 9.22656C36.293 8.86328 36.4336 8.31445 36.4336 7.58008V3.32031C36.4336 2.96484 36.5117 2.70117 36.668 2.5293C36.8242 2.35352 37.0332 2.26562 37.2949 2.26562C37.5566 2.26562 37.7676 2.35352 37.9277 2.5293C38.0918 2.70117 38.1738 2.96484 38.1738 3.32031V7.45117C38.1738 8.12305 38.1074 8.68359 37.9746 9.13281C37.8457 9.58203 37.5996 9.97656 37.2363 10.3164C36.9238 10.6055 36.5605 10.8164 36.1465 10.9492C35.7324 11.082 35.248 11.1484 34.6934 11.1484C34.0332 11.1484 33.4648 11.0781 32.9883 10.9375C32.5117 10.793 32.123 10.5723 31.8223 10.2754C31.5215 9.97461 31.3008 9.5918 31.1602 9.12695C31.0195 8.6582 30.9492 8.09961 30.9492 7.45117Z" fill="white" />
9 | </svg>
10 |
```
--------------------------------------------------------------------------------
/docs/public/pages/howto/update-ui-optimistically.md:
--------------------------------------------------------------------------------
```markdown
1 | # Update UI optimistically
2 |
3 | For immediate user feedback, use reactive variables like `localFavorited` and `localFavoritesCount` to update UI state instantly while API calls run in the background.
4 |
5 | ```xmlui-pg copy display name="Click the Like button - immediate feedback"
6 | ---app display /localFavorited/ /localFavoritesCount/ {37-52}
7 | <App>
8 | <APICall
9 | id="favoritePost"
10 | method="post"
11 | url="/api/posts/{$param}/favorite"
12 | inProgressNotificationMessage="Favoriting post..."
13 | completedNotificationMessage="Post favorited!" />
14 | <APICall
15 | id="unfavoritePost"
16 | method="post"
17 | url="/api/posts/{$param}/unfavorite"
18 | inProgressNotificationMessage="Unfavoriting post..."
19 | completedNotificationMessage="Post unfavorited!" />
20 | <DataSource
21 | id="timelineData"
22 | url="/api/timeline"
23 | method="GET" />
24 | <VStack gap="$space-4" padding="$space-4">
25 | <Items data="{timelineData}">
26 | <Card var.localFavorited="{null}" var.localFavoritesCount="{null}">
27 | <VStack>
28 | <Text>{$item.author}</Text>
29 | <Text>{$item.content}</Text>
30 | <HStack gap="$space-4" verticalAlignment="center">
31 | <HStack gap="$space-1" verticalAlignment="center">
32 | <SocialButton icon="reply" />
33 | <Text variant="caption">{$item.replies_count}</Text>
34 | </HStack>
35 | <HStack gap="$space-1" verticalAlignment="center">
36 | <SocialButton icon="trending-up" />
37 | <Text variant="caption">{$item.reblogs_count}</Text>
38 | </HStack>
39 | <HStack gap="$space-1" verticalAlignment="center">
40 | <SocialButton
41 | icon="like"
42 | themeColor="{(localFavorited !== null ? localFavorited : $item.favourited) ? 'attention' : 'secondary'}">
43 | <event name="click">
44 | // Get current state (local takes precedence)
45 | const currentFavorited = localFavorited !== null ? localFavorited : $item.favourited;
46 | const currentCount = localFavoritesCount !== null ? localFavoritesCount : ($item.favourites_count || 0);
47 | // Update UI optimistically
48 | localFavorited = !currentFavorited;
49 | localFavoritesCount = currentFavorited ?
50 | Math.max(0, currentCount - 1) :
51 | currentCount + 1;
52 | // Make API call
53 | if (currentFavorited) {
54 | unfavoritePost.execute($item.id).then(() => timelineData.refetch());
55 | } else {
56 | favoritePost.execute($item.id).then(() => timelineData.refetch());
57 | }
58 | </event>
59 | </SocialButton>
60 | <Text variant="caption">
61 | {localFavoritesCount !== null ? localFavoritesCount : ($item.favourites_count || 0)}
62 | </Text>
63 | </HStack>
64 | </HStack>
65 | </VStack>
66 | </Card>
67 | </Items>
68 | </VStack>
69 | </App>
70 | ---comp display {8}
71 | <Component name="SocialButton">
72 | <Button
73 | borderRadius="50%"
74 | icon="{$props.icon}"
75 | variant="outlined"
76 | themeColor="{$props.themeColor || 'secondary'}"
77 | size="xs"
78 | onClick="{emitEvent('click')}" />
79 | </Component>
80 | ---api
81 | {
82 | "apiUrl": "/api",
83 | "initialize": "$state.posts = [
84 | {
85 | id: '1',
86 | content: 'This is a great post about XMLUI!',
87 | author: 'John Developer',
88 | favourited: false,
89 | favourites_count: 5,
90 | replies_count: 2,
91 | reblogs_count: 1
92 | },
93 | {
94 | id: '2',
95 | content: 'Learning optimistic UI updates!',
96 | author: 'Jane Designer',
97 | favourited: true,
98 | favourites_count: 12,
99 | replies_count: 4,
100 | reblogs_count: 3
101 | }
102 | ]",
103 | "operations": {
104 | "get-timeline": {
105 | "url": "/timeline",
106 | "method": "get",
107 | "handler": "return $state.posts"
108 | },
109 | "favorite-post": {
110 | "url": "/posts/:id/favorite",
111 | "method": "post",
112 | "pathParamTypes": {
113 | "id": "string"
114 | },
115 | "handler": "
116 | delay(2000);
117 | const post = $state.posts.find(p => p.id === $pathParams.id);
118 | if (post) {
119 | post.favourited = true;
120 | post.favourites_count += 1;
121 | }
122 | "
123 | },
124 | "unfavorite-post": {
125 | "url": "/posts/:id/unfavorite",
126 | "method": "post",
127 | "pathParamTypes": {
128 | "id": "string"
129 | },
130 | "handler": "
131 | delay(2000);
132 | const post = $state.posts.find(p => p.id === $pathParams.id);
133 | if (post) {
134 | post.favourited = false;
135 | post.favourites_count -= 1;
136 | }
137 | "
138 | }
139 | }
140 | }
141 | ```
142 |
143 | The relationship between `onClick="{emitEvent('click')}"` in the `SocialButton` component and the `<event name="click">` handler in the main app demonstrates event propagation in XMLUI.
144 |
145 | The parent component catches the emitted click event and implements the optimistic UI update. This separation allows for:
146 |
147 | - Component reuse. `SocialButton` can be used anywhere without knowing what action the click should perform.
148 | - Flexible event handling. Different instances of `SocialButton` can handle clicks differently.
149 |
```
--------------------------------------------------------------------------------
/xmlui/src/parsers/scripting/TokenTrait.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { TokenType } from "./TokenType";
2 |
3 | // Describes the special traits of a token
4 | type TokenTrait = {
5 | expressionStart?: boolean;
6 | isAssignment?: boolean;
7 | canBeUnary?: boolean;
8 | keywordLike?: boolean;
9 | isPropLiteral?: boolean;
10 | };
11 |
12 | // Individual traits of tokens
13 | export const tokenTraits: Record<TokenType, TokenTrait> = {
14 | [TokenType.Eof]: {},
15 | [TokenType.Ws]: {},
16 | [TokenType.DollarLBrace]: {},
17 | [TokenType.Backtick]: {expressionStart: true},
18 | [TokenType.BlockComment]: {},
19 | [TokenType.EolComment]: {},
20 | [TokenType.Unknown]: {},
21 | [TokenType.LParent]: { expressionStart: true },
22 | [TokenType.RParent]: {},
23 | [TokenType.Identifier]: { expressionStart: true, keywordLike: true, isPropLiteral: true },
24 | [TokenType.Exponent]: {},
25 | [TokenType.Divide]: {},
26 | [TokenType.Multiply]: {},
27 | [TokenType.Remainder]: {},
28 | [TokenType.Plus]: { expressionStart: true, canBeUnary: true },
29 | [TokenType.Minus]: { expressionStart: true, canBeUnary: true },
30 | [TokenType.BitwiseXor]: {},
31 | [TokenType.BitwiseOr]: {},
32 | [TokenType.LogicalOr]: {},
33 | [TokenType.BitwiseAnd]: {},
34 | [TokenType.LogicalAnd]: {},
35 | [TokenType.Assignment]: { isAssignment: true },
36 | [TokenType.AddAssignment]: { isAssignment: true },
37 | [TokenType.SubtractAssignment]: { isAssignment: true },
38 | [TokenType.ExponentAssignment]: { isAssignment: true },
39 | [TokenType.MultiplyAssignment]: { isAssignment: true },
40 | [TokenType.DivideAssignment]: { isAssignment: true },
41 | [TokenType.RemainderAssignment]: { isAssignment: true },
42 | [TokenType.ShiftLeftAssignment]: { isAssignment: true },
43 | [TokenType.ShiftRightAssignment]: { isAssignment: true },
44 | [TokenType.SignedShiftRightAssignment]: { isAssignment: true },
45 | [TokenType.BitwiseAndAssignment]: { isAssignment: true },
46 | [TokenType.BitwiseXorAssignment]: { isAssignment: true },
47 | [TokenType.BitwiseOrAssignment]: { isAssignment: true },
48 | [TokenType.LogicalAndAssignment]: { isAssignment: true },
49 | [TokenType.LogicalOrAssignment]: { isAssignment: true },
50 | [TokenType.NullCoalesceAssignment]: { isAssignment: true },
51 | [TokenType.Semicolon]: {},
52 | [TokenType.Comma]: {},
53 | [TokenType.Colon]: {},
54 | [TokenType.LSquare]: { expressionStart: true },
55 | [TokenType.RSquare]: {},
56 | [TokenType.QuestionMark]: {},
57 | [TokenType.NullCoalesce]: {},
58 | [TokenType.OptionalChaining]: {},
59 | [TokenType.BinaryNot]: { expressionStart: true, canBeUnary: true },
60 | [TokenType.LBrace]: { expressionStart: true },
61 | [TokenType.RBrace]: {},
62 | [TokenType.Equal]: {},
63 | [TokenType.StrictEqual]: {},
64 | [TokenType.LogicalNot]: { expressionStart: true, canBeUnary: true },
65 | [TokenType.NotEqual]: {},
66 | [TokenType.StrictNotEqual]: {},
67 | [TokenType.LessThan]: {},
68 | [TokenType.LessThanOrEqual]: {},
69 | [TokenType.ShiftLeft]: {},
70 | [TokenType.GreaterThan]: {},
71 | [TokenType.GreaterThanOrEqual]: {},
72 | [TokenType.ShiftRight]: {},
73 | [TokenType.SignedShiftRight]: {},
74 | [TokenType.Dot]: {},
75 | [TokenType.Spread]: { expressionStart: true, isPropLiteral: true },
76 | [TokenType.Global]: { expressionStart: true },
77 | [TokenType.DecimalLiteral]: { expressionStart: true, isPropLiteral: true},
78 | [TokenType.HexadecimalLiteral]: { expressionStart: true, isPropLiteral: true },
79 | [TokenType.BinaryLiteral]: { expressionStart: true, isPropLiteral: true },
80 | [TokenType.RealLiteral]: { expressionStart: true, isPropLiteral: true },
81 | [TokenType.StringLiteral]: { expressionStart: true, isPropLiteral: true },
82 | [TokenType.IncOp]: { expressionStart: true },
83 | [TokenType.DecOp]: { expressionStart: true },
84 | [TokenType.Infinity]: { expressionStart: true, keywordLike: true },
85 | [TokenType.NaN]: { expressionStart: true, keywordLike: true },
86 | [TokenType.True]: { expressionStart: true, keywordLike: true, isPropLiteral: true},
87 | [TokenType.False]: { expressionStart: true, keywordLike: true, isPropLiteral: true},
88 | [TokenType.Typeof]: { expressionStart: true, canBeUnary: true, keywordLike: true },
89 | [TokenType.Null]: { expressionStart: true, keywordLike: true },
90 | [TokenType.Undefined]: { expressionStart: true, keywordLike: true },
91 | [TokenType.In]: { keywordLike: true },
92 | [TokenType.Let]: { keywordLike: true },
93 | [TokenType.Const]: { keywordLike: true },
94 | [TokenType.Var]: { keywordLike: true },
95 | [TokenType.If]: { keywordLike: true },
96 | [TokenType.Else]: { keywordLike: true },
97 | [TokenType.Arrow]: { keywordLike: true },
98 | [TokenType.Return]: { keywordLike: true },
99 | [TokenType.Break]: { keywordLike: true },
100 | [TokenType.Continue]: { keywordLike: true },
101 | [TokenType.Do]: { keywordLike: true },
102 | [TokenType.While]: { keywordLike: true },
103 | [TokenType.For]: { keywordLike: true },
104 | [TokenType.Of]: { keywordLike: true },
105 | [TokenType.Throw]: { keywordLike: true },
106 | [TokenType.Try]: { keywordLike: true },
107 | [TokenType.Catch]: { keywordLike: true },
108 | [TokenType.Finally]: { keywordLike: true },
109 | [TokenType.Switch]: { keywordLike: true },
110 | [TokenType.Case]: { keywordLike: true },
111 | [TokenType.Default]: { keywordLike: true },
112 | [TokenType.Delete]: { expressionStart: true, canBeUnary: true, keywordLike: true },
113 | [TokenType.Function]: { keywordLike: true, expressionStart: true },
114 | [TokenType.As]: { keywordLike: true },
115 | };
116 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Tabs/Tabs.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./Tabs.module.scss";
2 |
3 | import { parseScssVar } from "../../components-core/theming/themeVars";
4 | import { createComponentRenderer } from "../../components-core/renderers";
5 |
6 | import { MemoizedItem } from "../container-helpers";
7 | import { Tabs, defaultProps } from "./TabsNative";
8 | import { createMetadata, d, dComponent, dDidChange } from "../metadata-helpers";
9 |
10 | const COMP = "Tabs";
11 |
12 | export const TabsMd = createMetadata({
13 | status: "experimental",
14 | description:
15 | "`Tabs` enables users to switch among content panels using clickable tab headers. " +
16 | "It provides an efficient way to present multiple related sections in a single " +
17 | "interface area, with each tab containing distinct content defined by " +
18 | "[TabItem](/components/TabItem) components.",
19 | props: {
20 | activeTab: d(
21 | `This property indicates the index of the active tab. The indexing starts from 0, ` +
22 | `representing the starting (leftmost) tab. If not set, the first tab is selected by default.`,
23 | ),
24 | orientation: {
25 | description:
26 | `This property indicates the orientation of the component. In horizontal orientation, ` +
27 | `the tab sections are laid out on the left side of the content panel, while in vertical ` +
28 | `orientation, the buttons are at the top. Note: This property is ignored when ` +
29 | `accordionView is set to true.`,
30 | availableValues: ["horizontal", "vertical"],
31 | defaultValue: defaultProps.orientation,
32 | valueType: "string",
33 | },
34 | tabAlignment: {
35 | description:
36 | `This property controls how tabs are aligned within the tab header container in ` +
37 | `horizontal orientation. Use 'start' to align tabs to the left, 'end' to align to the ` +
38 | `right, 'center' to center the tabs, and 'stretch' to make tabs fill the full width ` +
39 | `of the header. Note: This property is ignored when orientation is set to 'vertical' ` +
40 | `or when accordionView is enabled.`,
41 | availableValues: ["start", "end", "center", "stretch"],
42 | defaultValue: defaultProps.tabAlignment,
43 | valueType: "string",
44 | },
45 | accordionView: {
46 | description:
47 | `When enabled, displays tabs in an accordion-like view where tab headers are stacked ` +
48 | `vertically and only the active tab's content is visible. Each tab header remains visible ` +
49 | `and clicking a header opens its content while closing others. When enabled, the ` +
50 | `orientation property is ignored.`,
51 | defaultValue: defaultProps.accordionView,
52 | valueType: "boolean",
53 | },
54 | headerTemplate: {
55 | ...dComponent(`This property declares the template for the clickable tab area.`),
56 | },
57 | },
58 | events: {
59 | didChange: dDidChange(COMP),
60 | },
61 | apis: {
62 | next: {
63 | description: `This method selects the next tab. If the current tab is the last one, it wraps around to the first tab.`,
64 | signature: "next(): void",
65 | },
66 | prev: {
67 | description: `This method selects the previous tab. If the current tab is the first one, it wraps around to the last tab.`,
68 | signature: "prev(): void",
69 | },
70 | setActiveTabIndex: {
71 | description: `This method sets the active tab by index (0-based).`,
72 | signature: "setActiveTabIndex(index: number): void",
73 | },
74 | setActiveTabById: {
75 | description: `This method sets the active tab by its ID.`,
76 | signature: "setActiveTabById(id: string): void",
77 | },
78 | },
79 | themeVars: parseScssVar(styles.themeVars),
80 | defaultThemeVars: {
81 | // [`backgroundColor-${COMP}`]: "transparent",
82 | [`borderStyle-${COMP}`]: "solid",
83 | [`borderColor-${COMP}`]: "$borderColor",
84 | [`borderColor-active-${COMP}`]: "$color-primary",
85 | [`borderWidth-${COMP}`]: "2px",
86 | // [`backgroundColor-trigger-${COMP}`]: "transparent",
87 | [`backgroundColor-trigger-${COMP}--hover`]: "$color-surface-100",
88 | [`padding-trigger-${COMP}`]: "$space-4",
89 | // [`backgroundColor-list-${COMP}`]: "$color-primary-50",
90 | // [`textColor-trigger-${COMP}`]: "$color-primary-100",
91 | },
92 | });
93 |
94 | export const tabsComponentRenderer = createComponentRenderer(
95 | COMP,
96 | TabsMd,
97 | ({ extractValue, node, renderChild, className, registerComponentApi, lookupEventHandler }) => {
98 | return (
99 | <Tabs
100 | id={node?.uid}
101 | className={className}
102 | headerRenderer={
103 | node?.props?.headerTemplate
104 | ? (item) => (
105 | <MemoizedItem
106 | node={node.props.headerTemplate! as any}
107 | itemKey="$header"
108 | contextVars={{
109 | $header: item,
110 | }}
111 | renderChild={renderChild}
112 | />
113 | )
114 | : undefined
115 | }
116 | activeTab={extractValue(node.props?.activeTab)}
117 | orientation={extractValue(node.props?.orientation)}
118 | tabAlignment={extractValue(node.props?.tabAlignment)}
119 | accordionView={extractValue(node.props?.accordionView)}
120 | onDidChange={lookupEventHandler("didChange")}
121 | registerComponentApi={registerComponentApi}
122 | >
123 | {renderChild(node.children)}
124 | </Tabs>
125 | );
126 | },
127 | );
128 |
```
--------------------------------------------------------------------------------
/xmlui/dev-docs/release-method.md:
--------------------------------------------------------------------------------
```markdown
1 | **Overall Philosophy:**
2 |
3 | * **`main` branch:** The source of truth for all development.
4 | * **Changesets:** Used for managing versioning and changelogs.
5 | * Can be added manually (`npx changeset add`).
6 | * Or, ideally, auto-generated from Conventional Commits on PRs (see `add-changeset.yml` below).
7 | * **Beta Releases:** Fully automated on every merge to `main` that includes new changesets. Published to npm with a `beta` tag.
8 | * **Stable Releases:** A manually triggered process that:
9 | 1. Creates a "Version Packages" Pull Request for review.
10 | 2. Upon merging this PR, publishes packages to npm (with the `latest` tag) and creates GitHub Releases.
11 | * **Handling Race Conditions for Stable Release:** If `main` changes significantly after the "Version Packages" PR is created, the safest approach is to close that PR and re-trigger the stable release process to generate a new, up-to-date "Version Packages" PR.
12 |
13 | ---
14 |
15 | **The Process Steps:**
16 |
17 | 1. **Development:**
18 | * Developers create feature branches from `main`.
19 | * They make code changes and write **Conventional Commit messages**.
20 | * **(Automation Option):** When a PR is opened/updated, the `add-changeset.yml` workflow runs, analyzes conventional commits, and automatically adds/updates `.changeset/*.md` files to the PR branch.
21 | * **(Manual Option):** Developer runs `npx changeset add` before committing changes that require a version bump, and commits the generated changeset file.
22 | * Developer opens/updates a Pull Request to `main`.
23 |
24 | 2. **Pull Request Merged to `main`:**
25 | * The `beta-release.yml` workflow triggers.
26 | * It consumes any `.changeset` files merged from the PR.
27 | * It versions affected packages with a beta suffix (e.g., `1.0.1-beta.shortsha`).
28 | * It publishes these beta versions to npm under the `beta` dist-tag.
29 | * It commits and pushes these snapshot version changes back to `main`.
30 |
31 | 3. **Preparing for a Stable Release (Manual Trigger):**
32 | * A designated person (release manager) decides it's time for a stable release.
33 | * **Pre-check:** Briefly check if `main` is relatively stable or communicate a short "quiet period" for merges to `main` to minimize conflicts with the version PR.
34 | * Go to GitHub Actions -> "Stable Release Process" workflow -> Run workflow.
35 | * The `stable-release.yml` workflow's `create_version_pr` job runs:
36 | * It uses `changesets/action` to consume all current `.changeset` files on `main`.
37 | * It generates version bumps, updates `CHANGELOG.md` files.
38 | * It commits these changes to a new branch (e.g., `changeset-release/main`).
39 | * It opens a "Version Packages for Release" Pull Request to `main`.
40 |
41 | 4. **Reviewing and Merging the "Version Packages" PR:**
42 | * The team reviews this PR. It shows exactly which packages will be versioned and what their changelogs will contain for the stable release.
43 | * **Critical Check:** Before merging, verify if `main` has received significant new changes (especially new changesets that have triggered new beta releases) *since this "Version Packages" PR was created*.
44 | * **If `main` has changed significantly:** Close the current "Version Packages" PR and its branch. Go back to Step 3 and re-trigger the "Stable Release Process" workflow to generate a fresh "Version Packages" PR based on the latest `main`.
45 | * **If `main` is stable or changes are minor/unrelated:** Proceed to merge the "Version Packages" PR.
46 |
47 | 5. **"Version Packages" PR is Merged to `main`:**
48 | * The `stable-release.yml` workflow's `publish_and_release` job triggers.
49 | * It uses `changesets/action` to:
50 | * Publish the versioned packages (now merged into `main`) to npm, this time to the `latest` dist-tag.
51 | * Create Git tags for each published package version.
52 | * Create corresponding GitHub Releases, populating them with content from the `CHANGELOG.md` files.
53 |
54 | ---
55 |
56 |
57 | **1. `.github/workflows/add-changeset.yml`**
58 | *Automates creation of changeset files from Conventional Commits in PRs.*
59 |
60 | ```yaml
61 | name: Add Changeset from Conventional Commits (NPM)
62 |
63 | on:
64 | pull_request:
65 | types: [opened, synchronize, reopened, ready_for_review]
66 |
67 | permissions:
68 | contents: write
69 | pull-requests: read
70 |
71 | jobs:
72 | add_changeset:
73 | if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]'
74 | runs-on: ubuntu-latest
75 | steps:
76 | - name: Checkout Repo
77 | uses: actions/checkout@v4
78 | with:
79 | fetch-depth: 0
80 |
81 | - name: Setup Node.js
82 | uses: actions/setup-node@v4
83 | with:
84 | node-version: '18' # Or your Node.js version
85 | cache: 'npm' # Use npm cache
86 |
87 | - name: Install Dependencies
88 | run: npm ci # Uses package-lock.json
89 |
90 | - name: Create Changeset File
91 | uses: tripsit/conventional-changesets-action@v4
92 | with:
93 | github-token: ${{ secrets.GITHUB_TOKEN }}
94 | commit-message: "chore: add generated changeset(s) [skip ci]"
95 | skip-ci: "true"
96 | # Configure type mappings if needed
97 | ```
98 |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/rendering/renderChild.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import type { ReactNode } from "react";
2 |
3 | import type { ComponentDef } from "../../abstractions/ComponentDefs";
4 | import type { InnerRendererContext } from "../abstractions/ComponentRenderer";
5 | import type { ComponentCleanupFn } from "../rendering/ContainerWrapper";
6 | import { shouldKeep, extractParam } from "../utils/extractParam";
7 | import { ComponentWrapper } from "./ComponentWrapper";
8 | import type { StatePartChangedFn } from "./ContainerWrapper";
9 |
10 | /**
11 | * This type represents the context in which the React component belonging to a
12 | * particular component definition is rendered with the `renderChild()` function.
13 | */
14 | export interface ChildRendererContext extends InnerRendererContext {
15 | statePartChanged: StatePartChangedFn;
16 | cleanup: ComponentCleanupFn;
17 | }
18 |
19 | /**
20 | * This function is the jolly-joker of the rendering process. It renders a child component
21 | * based on the specified context, which contains the component's definition, the current state,
22 | * and other necessary information.
23 | *
24 | * The function checks a few special cases:
25 | * - <Slot> with a single text node child: it renders the text in the context of the parent component.
26 | * - CDATA text nodes: it renders the text as is without parsing it.
27 | * - TextNode: it extracts the text from the node and renders it.
28 | *
29 | * In other cases, it extracts the component's ID and renders the component as a <ComponentNode>.
30 | *
31 | * As this function passes itself as a renderChild function to the <ComponentNode>, it can render
32 | * nested components recursively.
33 | */
34 | export function renderChild({
35 | node,
36 | state,
37 | dispatch,
38 | appContext,
39 | lookupAction,
40 | lookupSyncCallback,
41 | registerComponentApi,
42 | renderChild,
43 | statePartChanged,
44 | layoutContext,
45 | parentRenderContext,
46 | memoedVarsRef,
47 | cleanup,
48 | uidInfoRef,
49 | }: ChildRendererContext): ReactNode {
50 | // --- Render only visible components
51 | if (!shouldKeep(node.when, state, appContext)) {
52 | return null;
53 | }
54 |
55 | // --- We do not parse text nodes specified with CDATA to avoid whitespace collapsing
56 | const nodeValue = (node.props as any)?.value;
57 | if (node.type === "TextNodeCData") {
58 | return nodeValue ?? "";
59 | }
60 |
61 | // --- A TextNode value may contain nexted expressions, so we extract it.
62 | if (node.type === "TextNode") {
63 | const extractedValue = extractParam(state, nodeValue, appContext, true);
64 | return typeof extractedValue === "boolean" ? extractedValue.toString() : extractedValue;
65 | }
66 |
67 | // --- Rendering a Slot requires some preparations, as TextNode and
68 | // --- TextNodeCData are virtual nodes. Also, slots may have default templates
69 | // --- to render when no slot children are specified. The following section
70 | // --- handles these cases.
71 | if (node.type === "Slot") {
72 | // --- Check for special Slot cases
73 | let slotChildren: ComponentDef | ComponentDef[];
74 | const templateName = node.props?.name;
75 | // console.log("templateName", templateName);
76 | if (templateName) {
77 | // --- Let's check the validity of the slot name
78 | if (!templateName.endsWith("Template")) {
79 | throw new Error(
80 | `Slot name '${templateName}' is not valid. ` +
81 | "A named slot should use a name ending with 'Template'.",
82 | );
83 | }
84 |
85 | // --- Named slot: use a template property from the parent component
86 | slotChildren = parentRenderContext?.props?.[templateName];
87 | } else {
88 | // --- Children slot: use the children of the parent component
89 | slotChildren = parentRenderContext?.children;
90 | }
91 |
92 | if (!slotChildren) {
93 | // --- No children to render, let's try the default slot template (if there is any)
94 | slotChildren = node.children;
95 | }
96 |
97 | if (slotChildren) {
98 | const toRender = Array.isArray(slotChildren) ? slotChildren : [slotChildren];
99 | // --- Check for the virtual nodes. At this point, parentRendererContext is
100 | // --- undefined when the parent does not provide slot children. In this case,
101 | // --- the ComponentBed component will render the default slot template.
102 | if (toRender.length === 1 && parentRenderContext) {
103 | if (toRender[0].type === "TextNodeCData" || toRender[0].type === "TextNode") {
104 | // --- Preserve the text and render it in the parent context
105 | return parentRenderContext.renderChild(toRender);
106 | }
107 | }
108 | }
109 | }
110 |
111 | // --- In other cases, we extract the component ID, and then render the component.
112 | // --- A component's ID is generally a string with identifier syntax. However, some
113 | // --- internal components have IDs with expressions, so we evaluate them.
114 | const key = extractParam(state, node.uid, appContext, true);
115 |
116 | return (
117 | <ComponentWrapper
118 | key={key}
119 | resolvedKey={key}
120 | node={node}
121 | cleanup={cleanup}
122 | statePartChanged={statePartChanged}
123 | memoedVarsRef={memoedVarsRef}
124 | state={state}
125 | dispatch={dispatch}
126 | appContext={appContext}
127 | lookupAction={lookupAction}
128 | lookupSyncCallback={lookupSyncCallback}
129 | registerComponentApi={registerComponentApi}
130 | renderChild={renderChild}
131 | layoutContext={layoutContext}
132 | parentRenderContext={parentRenderContext}
133 | uidInfoRef={uidInfoRef}
134 | />
135 | );
136 | }
137 |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/utils/LruCache.ts:
--------------------------------------------------------------------------------
```typescript
1 | // ====================================================================================================================
2 | // Types to implement an LRU cache we use to provide stable
3 | // Implementation source: https://www.nickang.com/2021-11-28-how-to-implement-an-lru-cache-in-javascript/
4 |
5 | /**
6 | * A single node of the LRU cache
7 | */
8 | class DoublyLinkedNode {
9 | prev: DoublyLinkedNode | undefined;
10 | next: DoublyLinkedNode | undefined;
11 | constructor(public readonly value: any, public readonly key: string) {
12 | this.next = undefined;
13 | this.prev = undefined;
14 | }
15 | }
16 |
17 | /**
18 | * We keep values in the LRU cache in a doubly linked list
19 | */
20 | class DoublyLinkedList {
21 | head: DoublyLinkedNode | undefined;
22 | tail: DoublyLinkedNode | undefined;
23 | size = 0;
24 | constructor() {
25 | this.head = undefined;
26 | this.tail = undefined;
27 | this.size = 0;
28 | }
29 |
30 | /**
31 | * Adds a new node to the head of the list
32 | * @param node The node to add to head of list
33 | */
34 | unshift(node: DoublyLinkedNode) {
35 | // case 1: there is only a root node in the list
36 | // point node.prev to root node
37 | // point node.next to undefined
38 | // point DoublyLinkedList head to node
39 | // point DoublyLinkedList tail to node
40 | // increment DoublyLinkedList size by 1
41 | // case 2: there are data nodes in the list
42 | // point head node.prev to node
43 | // point node.next to head node
44 | // point node.prev to root node
45 | // point DoublyLinkedList head to node
46 | // increment DoublyLinkedList size by 1
47 |
48 | if (this.size === 0) {
49 | // case 1
50 | this.head = node;
51 | this.tail = node;
52 | this.size++;
53 | } else {
54 | // case 2
55 | this.head!.prev = node;
56 | node.next = this.head;
57 | node.prev = undefined;
58 | this.head = node;
59 | this.size++;
60 | }
61 | }
62 |
63 | /**
64 | * Remove least recently used node from tail
65 | */
66 | pop() {
67 | const node = this.tail;
68 | if (!node) {
69 | return undefined;
70 | } else if (this.head === this.tail) {
71 | this.head = undefined;
72 | this.tail = undefined;
73 | } else {
74 | this.tail!.prev!.next = undefined;
75 | }
76 | this.tail = node.prev;
77 | this.size--;
78 | return node;
79 | }
80 |
81 | /**
82 | * Moves the specified node to the head
83 | */
84 | moveToHead(node: DoublyLinkedNode) {
85 | if (node === this.head) {
86 | return;
87 | }
88 |
89 | if (node === this.head && node === this.tail) {
90 | return;
91 | }
92 |
93 | if (node === this.tail) {
94 | // set tail to tail node.prev
95 | this.tail = this.tail.prev;
96 | // set new tail node.next to undefined
97 | this.tail!.next = undefined;
98 | // set node.next to current head
99 | node.next = this.head;
100 | // set current head node.prev to node
101 | this.head!.prev = node;
102 | // set head to node
103 | this.head = node;
104 | // set node.prev to undefined
105 | node.prev = undefined;
106 | } else {
107 | // set node.prev.next to node.next
108 | node.prev!.next = node.next;
109 | // set node.next.prev to node.prev
110 | node.next!.prev = node.prev;
111 | // set node.next to current head
112 | node.next = this.head;
113 | // set current head node.prev to node
114 | this.head!.prev = node;
115 | // set node as new head
116 | this.head = node;
117 | // set node.prev to undefined
118 | node.prev = undefined;
119 | }
120 | }
121 | }
122 |
123 | /**
124 | * This class implements the LRU cache
125 | */
126 | export class LRUCache {
127 | private store: Record<string, DoublyLinkedNode> = {};
128 | list = new DoublyLinkedList();
129 | constructor(public readonly maxSize: number) {}
130 |
131 | /**
132 | * Gets the number of items stored in the cache
133 | */
134 | get length(): number {
135 | return this.list.size;
136 | }
137 |
138 | /**
139 | * Gets the value with the specified key
140 | * @param key
141 | */
142 | get(key: string): any {
143 | // case 1: node with this key found
144 | // update position of node to head of DoublyLinkedList
145 | // return existing node
146 | // case 2: node with this key not found (i.e. doesn't exist)
147 | // return undefined
148 |
149 | const existingNode = this.store[key];
150 | if (existingNode) {
151 | this.list.moveToHead(existingNode);
152 | }
153 | return existingNode?.value;
154 | }
155 |
156 | /**
157 | * Sets the value for a particular key within the cache
158 | * @param key Cache item key
159 | * @param value Cache item value
160 | */
161 | set(key: string, value: any): void {
162 | // case 1: search and found existing node with this key
163 | // use get() to obtain node
164 | // if not exist, go to case 2
165 | // if exist, let get() handle re-ordering in DoublyLinkedList
166 | // set node to hold new value
167 | const existingNode = this.get(key);
168 | if (existingNode) {
169 | existingNode.value = value;
170 | }
171 |
172 | // case 2: search and couldn't find existing node with this key
173 | // create new node
174 | // insert key-value pair into store
175 | // insert as new head of DoublyLinkedList
176 |
177 | const newNode = new DoublyLinkedNode(value, key);
178 | this.store[key] = newNode;
179 | this.list.unshift(newNode);
180 |
181 | if (this.hasReachedMaxSize()) {
182 | this.evictLeastRecentlyUsed();
183 | }
184 | }
185 |
186 | hasReachedMaxSize() {
187 | return this.list.size === this.maxSize + 1;
188 | }
189 |
190 | evictLeastRecentlyUsed() {
191 | const evictedNode = this.list.pop();
192 | delete this.store[evictedNode!.key];
193 | }
194 | }
195 |
```
--------------------------------------------------------------------------------
/xmlui/conventions/mermaid.md:
--------------------------------------------------------------------------------
```markdown
1 | # Mermaid Diagram Conventions
2 |
3 | This document captures best practices and lessons learned for creating effective Mermaid diagrams in XMLUI documentation.
4 |
5 | ## Block Diagrams vs Flowcharts
6 |
7 | ### When to Use Block Diagrams
8 | - **Container representations**: Block diagrams excel at showing rectangular containers with structured content
9 | - **Clean layouts**: Better for side-by-side arrangements without connecting lines
10 | - **Text-heavy content**: Superior handling of multi-line text and tree structures
11 | - **Consistent styling**: More predictable alignment and spacing
12 |
13 | ### When to Use Flowcharts
14 | - **Process flows**: When you need arrows and connections between elements
15 | - **Decision trees**: For branching logic and conditional paths
16 | - **Simple node relationships**: Basic parent-child relationships with connections
17 |
18 | ## Block Diagram Best Practices
19 |
20 | ### Basic Structure
21 | ```mermaid
22 | %%{init: {"block": {"padding": 8}}}%%
23 | block-beta
24 | columns 3
25 |
26 | block:GroupName:3
27 | ITEM["Content goes here"]
28 | end
29 |
30 | ITEM1["Item 1"]
31 | ITEM2["Item 2"]
32 | ITEM3["Item 3"]
33 | ```
34 |
35 | ### Configuration
36 | - Always include `%%{init: {"block": {"padding": 8}}}%%` for consistent padding
37 | - Use `columns N` to control horizontal layout (1 for single column, 3 for three-column grid)
38 | - Use `block:Name:N` syntax to span multiple columns
39 |
40 | ### Content Formatting
41 |
42 | #### Left-Aligned Text
43 | ```mermaid
44 | CONTAINER["<div style='text-align: left; width: 100%'>Title<br/>Content line 1<br/>Content line 2</div>"]
45 | ```
46 |
47 | #### Tree Structures
48 | Use ` ` entities for precise indentation:
49 | ```mermaid
50 | ITEM["<div style='text-align: left; width: 100%'>🏠 Container<br/>💡 State:<br/> ├─ item1: value<br/> └─ item2: value<br/>🌳 Tree:<br/> Root:<br/> ├─ Child 1<br/> └─ Child 2</div>"]
51 | ```
52 |
53 | #### Spacing Guidelines
54 | - **Root level items**: 5 ` ` entities to align with section headers
55 | - **Tree children**: 7-8 ` ` entities for proper indentation under parents
56 | - **Section headers**: Align with icon text, not the icon itself
57 |
58 | ## Styling
59 |
60 | ### CSS Classes
61 | ```mermaid
62 | classDef rootContainer fill:#f0f0f0,stroke:#888,stroke-width:2px,color:#333
63 | classDef innerContainer fill:#f8f8f8,stroke:#aaa,stroke-width:1px,color:#333
64 |
65 | class ITEM1,ITEM2 rootContainer
66 | class ITEM3 innerContainer
67 | ```
68 |
69 | ### Background Colors
70 | - **Light containers**: `fill:#f0f0f0` with `stroke:#888`
71 | - **Inner elements**: `fill:#f8f8f8` with `stroke:#aaa`
72 | - **Text color**: Always use `color:#333` for readability
73 |
74 | ### Padding
75 | - Apply padding directly in HTML: `<div style='padding: 0 15px;'>` for horizontal-only padding
76 | - Avoid CSS class padding - it may not work consistently in all Mermaid versions
77 |
78 | ## Layout Patterns
79 |
80 | ### Single Container
81 | ```mermaid
82 | %%{init: {"block": {"padding": 8}}}%%
83 | block-beta
84 | columns 1
85 |
86 | CONTAINER["<div style='text-align: left; width: 100%'>Content</div>"]
87 |
88 | classDef rootContainer fill:#f0f0f0,stroke:#888,stroke-width:2px,color:#333
89 | class CONTAINER rootContainer
90 | ```
91 |
92 | ### Multi-Container Grid
93 | ```mermaid
94 | %%{init: {"block": {"padding": 8}}}%%
95 | block-beta
96 | columns 3
97 |
98 | block:Header:3
99 | HEADER["Header spanning full width"]
100 | end
101 |
102 | ITEM1["Item 1"]
103 | ITEM2["Item 2"]
104 | ITEM3["Item 3"]
105 | ```
106 |
107 | ## Common Pitfalls
108 |
109 | ### Text Alignment Issues
110 | - **Problem**: Content appears centered even with left-align styling
111 | - **Solution**: Always use `<div style='text-align: left; width: 100%'>` wrapper
112 |
113 | ### Inconsistent Spacing
114 | - **Problem**: Tree items don't align properly
115 | - **Solution**: Count ` ` entities carefully - use consistent patterns (5 for root, 7-8 for children)
116 |
117 | ### Styling Not Applied
118 | - **Problem**: CSS classes don't affect appearance
119 | - **Solution**: Apply styles directly in HTML divs rather than relying solely on CSS classes
120 |
121 | ### Layout Breaks
122 | - **Problem**: Items don't arrange as expected
123 | - **Solution**: Use `columns N` consistently and test with different content lengths
124 |
125 | ## Icons and Emojis
126 |
127 | ### Standard Icons
128 | - 🏠 - Containers
129 | - 💡 - State/Data
130 | - 🌳 - Trees/Hierarchies
131 | - 📦 - Components
132 | - ⚙️ - Configuration
133 |
134 | ### Tree Symbols
135 | - `├─` - Branch item
136 | - `└─` - Final branch item
137 | - Use these consistently for hierarchical structures
138 |
139 | ## Testing Tips
140 |
141 | 1. **Always preview**: Mermaid rendering can vary between environments
142 | 2. **Check alignment**: Verify tree structures align properly at different zoom levels
143 | 3. **Test content length**: Ensure layout works with both short and long text
144 | 4. **Validate styling**: Confirm background colors and padding appear correctly
145 | 5. **Cross-platform**: Test on different operating systems if possible
146 |
147 | ## Migration from Flowcharts
148 |
149 | When converting existing flowcharts to block diagrams:
150 |
151 | 1. Replace `graph TB/LR` with `block-beta`
152 | 2. Remove connection arrows (`---`, `-->`)
153 | 3. Convert subgraphs to block groups
154 | 4. Add HTML div wrappers for text alignment
155 | 5. Apply consistent CSS styling
156 | 6. Test tree structure indentation
157 |
158 | This approach provides cleaner, more maintainable diagrams with better text formatting capabilities.
```
--------------------------------------------------------------------------------
/xmlui/src/components/Charts/LineChart/LineChart.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { defaultProps, LineChart } from "./LineChartNative";
2 | import { createComponentRenderer } from "../../../components-core/renderers";
3 | import { createMetadata, d } from "../../metadata-helpers";
4 | import { parseScssVar } from "../../../components-core/theming/themeVars";
5 | import styles from "./LineChart.module.scss";
6 | import { MemoizedItem } from "../../container-helpers";
7 |
8 | const COMP = "LineChart";
9 |
10 | export const LineChartMd = createMetadata({
11 | status: "experimental",
12 | description:
13 | "`LineChart` displays data as connected points over a continuous axis, ideal " +
14 | "for showing trends, changes over time, or relationships between variables. " +
15 | "Use it time series data, progress tracking, and comparing multiple data " +
16 | "series on the same scale.",
17 | docFolder: "Charts/LineChart",
18 | props: {
19 | data: {
20 | description:
21 | "The data to be displayed in the line chart." +
22 | "It needs to be an array of objects, where each object represents a data point.",
23 | },
24 | xKey: {
25 | description: "The key in the data objects used for labeling different data series.",
26 | valueType: "string",
27 | },
28 | yKeys: {
29 | description:
30 | "This property specifies the keys in the data objects that should be used for rendering the lines.",
31 | valueType: "string",
32 | },
33 | hideX: {
34 | description:
35 | "Determines whether the X-axis should be hidden. If set to (`true`), the axis will not be displayed.",
36 | valueType: "boolean",
37 | defaultValue: defaultProps.hideX,
38 | },
39 | hideY: {
40 | description:
41 | "Determines whether the Y-axis should be hidden. If set to (`true`), the axis will not be displayed.",
42 | valueType: "boolean",
43 | defaultValue: defaultProps.hideY,
44 | },
45 | hideTickX: {
46 | description:
47 | "Determines whether the X-axis ticks should be hidden. If set to (`true`), the ticks will not be displayed.",
48 | valueType: "boolean",
49 | defaultValue: defaultProps.hideTickX,
50 | },
51 | hideTickY: {
52 | description:
53 | "Determines whether the Y-axis ticks should be hidden. If set to (`true`), the ticks will not be displayed.",
54 | valueType: "boolean",
55 | defaultValue: defaultProps.hideTickY,
56 | },
57 | hideTooltip: {
58 | description:
59 | "Determines whether the tooltip should be hidden." +
60 | "If set to (`true`), no tooltip will be shown when hovering over data points.",
61 | valueType: "boolean",
62 | defaultValue: defaultProps.hideTooltip,
63 | },
64 | tickFormatterX: {
65 | description:
66 | "A function that formats the X-axis tick labels. It receives a tick value and returns a formatted string.",
67 | },
68 | tickFormatterY: {
69 | description:
70 | "A function that formats the Y-axis tick labels. It receives a tick value and returns a formatted string.",
71 | },
72 | showLegend: {
73 | description: "Determines whether the legend should be displayed.",
74 | valueType: "boolean",
75 | defaultValue: defaultProps.showLegend,
76 | },
77 | tooltipTemplate: {
78 | description: "This property allows replacing the default template to display a tooltip.",
79 | },
80 | marginTop: d("The top margin of the chart"),
81 | marginRight: d("The right margin of the chart"),
82 | marginBottom: d("The bottom margin of the chart"),
83 | marginLeft: d("The left margin of the chart"),
84 | },
85 | themeVars: parseScssVar(styles.themeVars),
86 | defaultThemeVars: {
87 | [`width-line-LineChart`]: "1px",
88 | },
89 | });
90 |
91 | export const lineChartComponentRenderer = createComponentRenderer(
92 | COMP,
93 | LineChartMd,
94 | ({ extractValue, node, className, lookupSyncCallback, renderChild }: any) => {
95 | return (
96 | <LineChart
97 | tickFormatterX={lookupSyncCallback(node.props?.tickFormatterX)}
98 | tickFormatterY={lookupSyncCallback(node.props?.tickFormatterY)}
99 | hideTickX={extractValue(node.props?.hideTickX)}
100 | hideTickY={extractValue(node.props?.hideTickY)}
101 | data={extractValue(node.props?.data)}
102 | className={className}
103 | dataKeys={extractValue(node.props?.yKeys)}
104 | nameKey={extractValue(node.props?.xKey)}
105 | hideX={extractValue(node.props?.hideX)}
106 | hideY={extractValue(node.props?.hideY)}
107 | hideTooltip={extractValue(node.props?.hideTooltip)}
108 | showLegend={extractValue.asOptionalBoolean(node.props?.showLegend)}
109 | marginTop={extractValue.asOptionalNumber(node.props?.marginTop)}
110 | marginRight={extractValue.asOptionalNumber(node.props?.marginRight)}
111 | marginBottom={extractValue.asOptionalNumber(node.props?.marginBottom)}
112 | marginLeft={extractValue.asOptionalNumber(node.props?.marginLeft)}
113 | tooltipRenderer={
114 | node.props.tooltipTemplate
115 | ? (tooltipData) => {
116 | return (
117 | <MemoizedItem
118 | node={node.props.tooltipTemplate}
119 | item={tooltipData}
120 | contextVars={{
121 | $tooltip: tooltipData,
122 | }}
123 | renderChild={renderChild}
124 | />
125 | );
126 | }
127 | : undefined
128 | }
129 | >
130 | {renderChild(node.children)}
131 | </LineChart>
132 | );
133 | },
134 | );
135 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/TimeInput/TimeInput.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | **Key features:**
4 | - **Time format support**: 12-hour and 24-hour formats with customizable display
5 | - **Precision control**: Configure precision for hours, minutes, and seconds
6 | - **Input validation**: Real-time validation with visual feedback for invalid times
7 | - **Accessibility**: Full keyboard navigation and screen reader support
8 | - **Localization**: Automatic AM/PM labels based on user locale
9 |
10 | %-DESC-END
11 |
12 | %-API-START setValue
13 |
14 | ```xmlui-pg copy {3, 9, 12} display name="Example: setValue"
15 | <App>
16 | <HStack>
17 | <Button
18 | label="Set Time to 14:30"
19 | onClick="picker.setValue('14:30')" />
20 | <Button
21 | label="Remove Time"
22 | onClick="picker.setValue('')" />
23 | </HStack>
24 | <TimeInput id="picker" />
25 | </App>
26 | ```
27 |
28 | %-API-END
29 |
30 | %-PROP-START initialValue
31 |
32 | ```xmlui-pg copy display name="Example: initialValue" height="120px"
33 | <App>
34 | <TimeInput initialValue="14:30:15" />
35 | </App>
36 | ```
37 |
38 | %-PROP-END
39 |
40 | %-PROP-START placeholder
41 |
42 | ```xmlui-pg copy display name="Example: placeholder" height="120px"
43 | <App>
44 | <TimeInput placeholder="Select a time" />
45 | </App>
46 | ```
47 |
48 | %-PROP-END
49 |
50 | %-PROP-START enabled
51 |
52 | ```xmlui-pg copy display name="Example: enabled" height="120px"
53 | <App>
54 | <TimeInput enabled="false" initialValue="14:30" />
55 | </App>
56 | ```
57 |
58 | %-PROP-END
59 |
60 | %-PROP-START validationStatus
61 |
62 | | Value | Description |
63 | | :-------- | :---------------------------------------------------- |
64 | | `valid` | Visual indicator for an input that is accepted |
65 | | `warning` | Visual indicator for an input that produced a warning |
66 | | `error` | Visual indicator for an input that produced an error |
67 |
68 | ```xmlui-pg copy display name="Example: validationStatus"
69 | <App>
70 | <TimeInput validationStatus="valid" initialValue="11:30" />
71 | <TimeInput validationStatus="warning" initialValue="11:30" />
72 | <TimeInput validationStatus="error" initialValue="11:30" />
73 | </App>
74 | ```
75 |
76 | %-PROP-END
77 |
78 | %-PROP-START format
79 |
80 | The `format` prop controls how time is displayed and which parts are editable. Based on Unicode Technical Standard #35.
81 |
82 | | Format | Description | Example |
83 | | :----- | :---------- | :------ |
84 | | `H:mm` | 24-hour format with hours and minutes | 14:30 |
85 | | `HH:mm:ss` | 24-hour format with hours, minutes, seconds | 14:30:15 |
86 | | `h:mm a` | 12-hour format with AM/PM | 2:30 PM |
87 | | `hh:mm:ss a` | 12-hour format with seconds and AM/PM | 02:30:15 PM |
88 |
89 | ```xmlui-pg copy display name="Example: format"
90 | <App>
91 | <TimeInput format="H:mm" initialValue="14:30" />
92 | <TimeInput format="h:mm a" initialValue="14:30" />
93 | <TimeInput format="HH:mm:ss" initialValue="14:30:15" />
94 | <TimeInput format="HH:mm:ss a" initialValue="14:30:15" />
95 | </App>
96 | ```
97 |
98 | %-PROP-END
99 |
100 | %-PROP-START clearable
101 |
102 | When enabled, it displays a clear button that allows users to reset the time picker back to its initial value. Change the time value in this app and then click the clear button:
103 |
104 | ```xmlui-pg copy display name="Example: clearable" /clearable/
105 | <App>
106 | <TimeInput initialValue="11:30" />
107 | <TimeInput clearable="true" initialValue="10:20" />
108 | </App>
109 | ```
110 |
111 | %-PROP-END
112 |
113 | %-PROP-START clearIcon
114 |
115 | ```xmlui-pg copy display name="Example: clearIcon" /clearIcon/
116 | <App>
117 | <TimeInput initialValue="11:30" clearIcon="trash" />
118 | </App>
119 | ```
120 |
121 | %-PROP-END
122 |
123 | %-PROP-START required
124 |
125 | Marks the time input as required for form validation.
126 |
127 | ```xmlui-pg copy display name="Example: required" height="120px"
128 | <App>
129 | <TimeInput required="true" />
130 | </App>
131 | ```
132 |
133 | %-PROP-END
134 |
135 | %-PROP-START mute
136 |
137 | When `true`, prevents audible beeps but still fires the `beep` event for programmatic handling.
138 |
139 | %-PROP-END
140 |
141 | %-PROP-START emptyCharacter
142 |
143 | Character to use as placeholder for empty time values. If longer than 1 character, uses the first character. Defaults to '-'.
144 |
145 | ```xmlui-pg copy display name="Example: emptyCharacter"
146 | <App>
147 | <TimeInput emptyCharacter="." />
148 | <TimeInput emptyCharacter="*" />
149 | <TimeInput emptyCharacter="abc" />
150 | </App>
151 | ```
152 |
153 | %-PROP-END
154 |
155 | %-EVENT-START didChange
156 |
157 | Fired when the time value changes. Receives the new time value as a parameter.
158 |
159 | > [!INFO] The time value changes when the edited input part (hour, minute, second) loses focus or the AM/PM selectro changes.
160 |
161 | ```xmlui-pg copy {2} display name="Example: didChange" height="180px"
162 | <App var.selectedTime="No time selected">
163 | <Text value="{selectedTime}" />
164 | <TimeInput
165 | format="h:m:s a"
166 | initialValue="07:30:05"
167 | onDidChange="(time) => selectedTime = time" />
168 | </App>
169 | ```
170 |
171 | %-EVENT-END
172 |
173 | %-EVENT-START gotFocus
174 |
175 | Fired when the time picker receives focus.
176 |
177 | ```xmlui-pg copy {4-5} display name="Example: gotFocus/lostFocus"
178 | <App var.isFocused="{false}">
179 | <Text value="{isFocused
180 | ? 'TimeInput focused' : 'TimeInput lost focus'}"
181 | />
182 | <TimeInput
183 | format="HH:mm:ss a"
184 | onGotFocus="isFocused = true"
185 | onLostFocus="isFocused = false"
186 | initialValue="14:30"
187 | />
188 | </App>
189 | ```
190 |
191 | %-EVENT-END
192 |
193 | %-EVENT-START invalidTime
194 |
195 | Fired when the user enters an invalid time value.
196 |
197 | ```xmlui-pg copy {2} display name="Example: invalidTime"
198 | <App var.errorMessage="">
199 | <Text value="{errorMessage}" />
200 | <TimeInput
201 | onInvalidTime="(error) => errorMessage = 'Invalid time entered'"
202 | onDidChange="errorMessage = ''" />
203 | </App>
204 | ```
205 |
206 | %-EVENT-END
207 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Checkbox/Checkbox.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | **Key features:**
4 | - **Flexible labeling**: Position labels on any side and support custom label templates
5 | - **Validation support**: Built-in validation states for form error handling
6 | - **Indeterminate state**: Special visual state for mixed selections (useful for "select all" scenarios)
7 |
8 | To bind data to a `Checkbox`, use the XMLUI [Forms infrastructure](/forms).
9 |
10 | ## Checkbox Values
11 |
12 | The `initialValue` and `value` properties of the checkbox are transformed to a Boolean value to display the checked (`true`) or unchecked (`false`) state with this logic:
13 | - `null` and `undefined` go to `false`.
14 | - If the property is Boolean, the property value is used as is.
15 | - If it is a number, `NaN` and `0` result in `false`; other values represent `true`.
16 | - If the property is a string, the empty string and the literal "false" string result in `false`; others result in `true`.
17 | - The empty array value goes to `false`; other array values result in `true`.
18 | - Object values with no properties result in `false`; other values represent `true`.
19 |
20 | %-DESC-END
21 |
22 | %-PROP-START enabled
23 |
24 | ```xmlui-pg copy display {4-5, 9-10} name="Example: enabled"
25 | <App>
26 | Enabled checkboxes:
27 | <HStack>
28 | <Checkbox initialValue="true" enabled="true" />
29 | <Checkbox initialValue="false" enabled="true" />
30 | </HStack>
31 | Disabled checkboxes:
32 | <HStack>
33 | <Checkbox initialValue="true" enabled="false" />
34 | <Checkbox initilaValue="false" enabled="false" />
35 | </HStack>
36 | </App>
37 | ```
38 |
39 | %-PROP-END
40 |
41 | %-PROP-START indeterminate
42 |
43 | This prop is commonly used if there are several other checkboxes linked to one checkbox and some items in that group of checkboxes are in a mixed state: at least one item has a different value compared to the rest.
44 |
45 | The following sample binds the state of two checkboxes to one and updates the state of the top checkbox accordingly. When the states of the bound checkboxes are different, the top checkbox is set to indeterminate:
46 |
47 | ```xmlui-pg copy display name="Example: indeterminate"
48 | ---app copy display {4}
49 | <App var.indeterminate="{false}">
50 | <Checkbox
51 | label="Indeterminate Checkbox"
52 | indeterminate="{indeterminate}"
53 | initialValue="{cb1.value}"
54 | readOnly="true" />
55 | <ChangeListener
56 | listenTo="{ { v1: cb1.value, v2: cb2.value } }"
57 | onDidChange="indeterminate = cb1.value !== cb2.value" />
58 | Group of checkboxes:
59 | <HStack>
60 | <Checkbox label="Checkbox #1" id="cb1" initialValue="true" />
61 | <Checkbox label="Checkbox #2" id="cb2" initialValue="false" />
62 | </HStack>
63 | </App>
64 | ---desc
65 | Try this sample by clicking the bottom group of checkboxes.
66 | ```
67 |
68 | %-PROP-END
69 |
70 | %-PROP-START label
71 |
72 | ```xmlui-pg copy display name="Example: label"
73 | <App>
74 | <Checkbox label="Example label" initialValue="true" />
75 | <Checkbox label="Another label" intialValue="false" />
76 | </App>
77 | ```
78 |
79 | %-PROP-END
80 |
81 | %-PROP-START labelPosition
82 |
83 | ```xmlui-pg copy display name="Example: labelPosition"
84 | <App>
85 | <Checkbox label="Top label" labelPosition="top" initialValue="true" />
86 | <Checkbox label="End label" labelPosition="end" initialValue="true" />
87 | <Checkbox label="Bottom label" labelPosition="bottom" initialValue="true" />
88 | <Checkbox label="Start label" labelPosition="start" initialValue="true" />
89 | </App>
90 | ```
91 |
92 | %-PROP-END
93 |
94 | %-PROP-START readOnly
95 |
96 | ```xmlui-pg copy {3} display name="Example: readOnly"
97 | <App>
98 | <Checkbox readOnly="true" label="Checked" initialValue="true" />
99 | <Checkbox readOnly="true" label="Unchecked" intialValue="false" />
100 | </App>
101 | ```
102 |
103 | %-PROP-END
104 |
105 | %-API-START value
106 |
107 | You can query this read-only API property to query the checkbox's current value (`true`: checked, `false`: unchecked).
108 |
109 | See an example in the `setValue` API method.
110 |
111 | %-API-END
112 |
113 | %-API-START setValue
114 |
115 | You can use this method to set the checkbox's current value programmatically (`true`: checked, `false`: unchecked).
116 |
117 | ```xmlui-pg copy {10,13,15} display name="Example: value and setValue"
118 | <App var.changes="">
119 | <Checkbox
120 | id="checkbox"
121 | readOnly="true"
122 | label="This checkbox can be set only programmatically"
123 | onDidChange="changes += '+'" />
124 | <HStack>
125 | <Button
126 | label="Check"
127 | onClick="checkbox.setValue(true)" />
128 | <Button
129 | label="Uncheck"
130 | onClick="checkbox.setValue(false)" />
131 | </HStack>
132 | <Text>The checkbox is {checkbox.value ? "checked" : "unchecked"}</Text>
133 | <Text value="Changes: {changes}" />
134 | </App>
135 | ```
136 |
137 | %-API-END
138 |
139 | %-EVENT-START didChange
140 |
141 | ```xmlui-pg copy display name="Example: didChange"
142 | <App verticalAlignment="center" var.changes="">
143 | <Checkbox label="Changeable" onDidChange="changes += '+'" />
144 | <Checkbox
145 | label="Readonly"
146 | readOnly="true"
147 | onDidChange="changes += '-'" />
148 | <Text value="Changes: {changes}" />
149 | </App>
150 | ```
151 |
152 | %-EVENT-END
153 |
154 | %-EVENT-START gotFocus
155 |
156 | Click the `Checkbox` in the example demo to change the label text. Note how clicking elsewhere resets the text to the original.
157 |
158 | ```xmlui-pg copy display name="Example: gotFocus/lostFocus"
159 | <App var.focused="{false}" verticalAlignment="center">
160 | <Checkbox
161 | value="true"
162 | onGotFocus="focused = true"
163 | onLostFocus="focused = false"
164 | />
165 | <Text value="{focused === true ? 'I am focused!' : 'I have lost the focus!'}" />
166 | </App>
167 | ```
168 |
169 | %-EVENT-END
170 |
171 | %-EVENT-START lostFocus
172 |
173 | (See the example above)
174 |
175 | %-EVENT-END
176 |
```
--------------------------------------------------------------------------------
/xmlui/src/language-server/services/common/metadata-utils.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { RiEmpathizeFill } from "react-icons/ri";
2 | import type { ComponentMetadata, ComponentPropertyMetadata } from "../../../abstractions/ComponentDefs"
3 | import { layoutOptionKeys } from "../../../components-core/descriptorHelper";
4 | import { onPrefixRegex, stripOnPrefix } from "../../../parsers/xmlui-parser";
5 | import { viewportSizeMd } from "../../../components/abstractions";
6 |
7 | type RestrictedComponentMetadata = Pick<ComponentMetadata, "description" | "status" | "props" | "events" | "apis" | "contextVars" | "allowArbitraryProps" | "shortDescription">
8 |
9 | export type ComponentMetadataCollection = Record<string, RestrictedComponentMetadata>
10 |
11 | export class MetadataProvider {
12 | constructor(private readonly metadataCollection: ComponentMetadataCollection) {}
13 |
14 | componentNames(): string[] {
15 | return Object.keys(this.metadataCollection);
16 | }
17 |
18 | getComponent(componentName: string): ComponentMetadataProvider | null {
19 | const providerData = this.metadataCollection[componentName];
20 | if (!providerData) {
21 | return null;
22 | }
23 |
24 | return new ComponentMetadataProvider(providerData);
25 | }
26 | }
27 |
28 | export type AttributeKind = "prop" | "event" | "api" | "implicit" | "layout"
29 | export type TaggedAttribute = { name: string, kind: AttributeKind };
30 |
31 | export class ComponentMetadataProvider {
32 | constructor(private readonly metadata: RestrictedComponentMetadata) {}
33 |
34 | /**
35 | * Retrieves the metadata for a given property, explicit or implicit.
36 | * @param name The name of the property.
37 | * @returns The metadata for the property, or `undefined` if not found.
38 | */
39 | getProp(name: string) {
40 | return this.metadata.props[name] ?? implicitPropsMetadata[name];
41 | }
42 |
43 | getAttr(name: string) {
44 | if (onPrefixRegex.test(name)){
45 | const eventName = stripOnPrefix(name)
46 | const event = this.metadata.events?.[eventName];
47 | if (event) {
48 | return event;
49 | }
50 | }
51 | const explicitProp = this.metadata.props?.[name];
52 | if (explicitProp) {
53 | return explicitProp;
54 | }
55 | const api = this.metadata.apis?.[name];
56 | if (api) {
57 | return api;
58 | }
59 |
60 | const layout = layoutMdForKey(name);
61 | if (layout) {
62 | return layout;
63 | }
64 | return implicitPropsMetadata[name];
65 | }
66 |
67 | getAttrForKind({ name, kind}: TaggedAttribute){
68 | switch (kind){
69 | case "api":
70 | return this.metadata.apis[name];
71 | case "event":
72 | return this.metadata.events[name];
73 | case "prop":
74 | return this.metadata.props[name];
75 | case "implicit":
76 | return implicitPropsMetadata[name];
77 | case "layout":
78 | return layoutMdForKey(name)
79 | }
80 | }
81 |
82 | getAllAttributes() {
83 | const attrNames: TaggedAttribute[] = [];
84 | for (const key of Object.keys(this.metadata.props ?? {})) {
85 | attrNames.push({ name: key, kind: "prop" });
86 | }
87 | for (const key of Object.keys(this.metadata.events ?? {})) {
88 | attrNames.push({ name: key, kind: "event" });
89 | }
90 | for (const key of Object.keys(this.metadata.apis ?? {})) {
91 | attrNames.push({ name: key, kind: "api" });
92 | }
93 | for (const layoutKey of layoutOptionKeys){
94 | attrNames.push({name: layoutKey, kind: "layout"})
95 | }
96 | for (const implicitPropKey of Object.keys(implicitPropsMetadata)){
97 | attrNames.push({name: implicitPropKey, kind: "implicit"})
98 | }
99 |
100 | return attrNames;
101 | }
102 |
103 | getEvent(name: string){
104 | return this.metadata.events?.[name];
105 | }
106 |
107 | getApi(name: string){
108 | return this.metadata.apis?.[name];
109 | }
110 |
111 | get events(): Record<string, string> {
112 | return this.metadata.events;
113 | }
114 |
115 | get apis(): Record<string, string> {
116 | return this.metadata.apis;
117 | }
118 |
119 | get contextVars(): Record<string, string> {
120 | return this.metadata.contextVars;
121 | }
122 |
123 | get allowArbitraryProps(): boolean {
124 | return this.metadata.allowArbitraryProps;
125 | }
126 |
127 | get shortDescription(): string {
128 | return this.metadata.shortDescription;
129 | }
130 |
131 | getMetadata(): RestrictedComponentMetadata {
132 | return this.metadata;
133 | }
134 | }
135 |
136 | function layoutMdForKey(name: string): ComponentPropertyMetadata {
137 | const metadata = {
138 | description: "Layout property. Not yet documented",
139 | };
140 | if (layoutOptionKeys.includes(name)){
141 | return metadata;
142 | }
143 | for(const size of viewportSizeMd){
144 | const suffix = "-" + ((size as {
145 | value: string | number;
146 | description: string;
147 | }).value);
148 |
149 | if(name.endsWith(suffix)){
150 | const nameWithoutSize = name.slice(0, -suffix.length);
151 | if(layoutOptionKeys.includes(nameWithoutSize)){
152 | return metadata;
153 | }
154 | }
155 | }
156 | return null;
157 | }
158 |
159 | const implicitPropsMetadata: Record<string, ComponentPropertyMetadata> = {
160 | inspect: {
161 | description: "Determines whether the component can be inspected or not",
162 | defaultValue: false,
163 | valueType: "boolean",
164 | },
165 | data: {
166 | description: "Specifies the data source for a component. Can be a URL string (fetched automatically), a DataSource or an expression to evaluate. Changes to this property trigger UI updates once data is loaded.",
167 | },
168 | when: {
169 | description: "Specifies a condition that must be met for the component to be displayed",
170 | defaultValue: true,
171 | valueType: "boolean",
172 | }
173 | };
174 |
175 | export function addOnPrefix(name: string) {
176 | return "on" + name[0].toUpperCase() + name.substring(1);
177 | }
178 |
```