This is page 23 of 190. Use http://codebase.md/xmlui-org/xmlui/main.tsx?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── small-tires-beg.md
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog-optimized.yml
│ ├── deploy-blog-swa.yml
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs-swa.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── an-advanced-codefence.gif
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── codefence-runner.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ ├── lorem-ipsum.png
│ │ │ │ ├── playground-checkbox-source.png
│ │ │ │ ├── playground.png
│ │ │ │ ├── use-xmlui-mcp-to-find-a-howto.png
│ │ │ │ └── xmlui-demo-gallery.png
│ │ │ ├── introducing-xmlui.md
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ ├── xmlui-playground.md
│ │ │ └── xmlui-powered-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── icons
│ │ │ │ ├── github.svg
│ │ │ │ └── rss.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ ├── staticwebapp.config.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── LinkButton.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ └── Separator.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── HelloMd.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── control-cache-invalidation.md
│ │ │ │ ├── debounce-user-input-for-api-calls.md
│ │ │ │ ├── debounce-with-changelistener.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── icons
│ │ │ │ ├── github.svg
│ │ │ │ └── rss.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ ├── staticwebapp.config.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── LinkButton.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── Separator.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── tsconfig.json
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── Animation.tsx
│ │ ├── AnimationNative.tsx
│ │ ├── FadeAnimation.tsx
│ │ ├── FadeInAnimation.tsx
│ │ ├── FadeOutAnimation.tsx
│ │ ├── index.tsx
│ │ ├── ScaleAnimation.tsx
│ │ └── SlideInAnimation.tsx
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── HelloWorld.module.scss
│ │ ├── HelloWorld.tsx
│ │ ├── HelloWorldNative.tsx
│ │ └── index.tsx
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── IPhoneFrame.module.scss
│ │ ├── IPhoneFrame.tsx
│ │ ├── MacOSAppFrame.module.scss
│ │ ├── MacOSAppFrame.tsx
│ │ ├── WindowsAppFrame.module.scss
│ │ └── WindowsAppFrame.tsx
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── LazyPdfNative.tsx
│ │ ├── Pdf.module.scss
│ │ └── Pdf.tsx
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── hooks
│ │ │ ├── usePlayground.ts
│ │ │ └── useToast.ts
│ │ ├── index.tsx
│ │ ├── playground
│ │ │ ├── Box.module.scss
│ │ │ ├── Box.tsx
│ │ │ ├── CodeSelector.module.scss
│ │ │ ├── CodeSelector.tsx
│ │ │ ├── ConfirmationDialog.module.scss
│ │ │ ├── ConfirmationDialog.tsx
│ │ │ ├── Editor.tsx
│ │ │ ├── Header.module.scss
│ │ │ ├── Header.tsx
│ │ │ ├── Playground.tsx
│ │ │ ├── PlaygroundContent.module.scss
│ │ │ ├── PlaygroundContent.tsx
│ │ │ ├── PlaygroundNative.module.scss
│ │ │ ├── PlaygroundNative.tsx
│ │ │ ├── Preview.tsx
│ │ │ ├── StandalonePlayground.tsx
│ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ ├── ThemeSwitcher.module.scss
│ │ │ ├── ThemeSwitcher.tsx
│ │ │ └── utils.ts
│ │ ├── providers
│ │ │ ├── Toast.module.scss
│ │ │ └── ToastProvider.tsx
│ │ ├── state
│ │ │ └── store.ts
│ │ ├── themes
│ │ │ └── theme.ts
│ │ └── utils
│ │ └── helpers.ts
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Search.module.scss
│ │ └── Search.tsx
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Spreadsheet.tsx
│ │ └── SpreadsheetNative.tsx
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ └── src
│ ├── Carousel
│ │ ├── Carousel.module.scss
│ │ ├── Carousel.tsx
│ │ ├── CarouselContext.tsx
│ │ └── CarouselNative.tsx
│ ├── FancyButton
│ │ ├── FancyButton.module.scss
│ │ ├── FancyButton.tsx
│ │ └── FancyButton.xmlui
│ ├── Hello
│ │ ├── Hello.tsx
│ │ ├── Hello.xmlui
│ │ └── Hello.xmlui.xs
│ ├── HeroSection
│ │ ├── HeroSection.module.scss
│ │ ├── HeroSection.spec.ts
│ │ ├── HeroSection.tsx
│ │ └── HeroSectionNative.tsx
│ ├── index.tsx
│ ├── ScrollToTop
│ │ ├── ScrollToTop.module.scss
│ │ ├── ScrollToTop.tsx
│ │ └── ScrollToTopNative.tsx
│ └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.cjs
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── component-metadata.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── theme-variables-refactoring.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── extract-component-metadata.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── generate-metadata-markdown.js
│ ├── get-langserver-metadata.js
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.module.scss
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ ├── ContentSeparatorNative.tsx
│ │ │ └── test-padding.xmlui
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.ts
│ │ └── infrastructure
│ │ ├── index.html
│ │ ├── main.tsx
│ │ ├── public
│ │ │ ├── mockServiceWorker.js
│ │ │ ├── resources
│ │ │ │ ├── bell.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── flower-640x480.jpg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ └── txt.svg
│ │ │ └── serve.json
│ │ └── TestBed.tsx
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.json
├── tsdown.config.ts
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/tools/create-xmlui-hello-world/index.js:
--------------------------------------------------------------------------------
```javascript
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const { execSync } = require('child_process');
6 |
7 | console.log('Creating minimal XMLUI test app for HelloWorld component...\n');
8 |
9 | // Get project name from command line or use default
10 | const projectName = process.argv[2] || 'test-hello-world';
11 | const projectPath = path.resolve(process.argv[2] ? process.argv[2] : path.join(process.env.HOME || process.env.USERPROFILE, projectName));
12 |
13 | // Create project directory
14 | if (!fs.existsSync(projectPath)) {
15 | fs.mkdirSync(projectPath, { recursive: true });
16 | fs.mkdirSync(path.join(projectPath, 'xmlui'), { recursive: true });
17 | } else {
18 | console.log(`Directory ${projectPath} already exists!`);
19 | process.exit(1);
20 | }
21 |
22 | // Check if we need to build the standalone XMLUI engine
23 | const xmluiStandalonePath = path.join(__dirname, '../../xmlui/dist/standalone/xmlui-standalone.umd.js');
24 | const docsStandalonePath = path.join(__dirname, '../../docs/public/resources/files/for-download/xmlui/xmlui-standalone.umd.js');
25 |
26 | let standaloneSource = null;
27 |
28 | if (fs.existsSync(docsStandalonePath)) {
29 | standaloneSource = docsStandalonePath;
30 | console.log('Using existing XMLUI standalone from docs...');
31 | } else if (fs.existsSync(xmluiStandalonePath)) {
32 | standaloneSource = xmluiStandalonePath;
33 | console.log('Using existing XMLUI standalone from build...');
34 | } else {
35 | console.log('Building XMLUI standalone engine...');
36 | try {
37 | execSync('npm run build:xmlui-standalone', {
38 | cwd: path.join(__dirname, '../../xmlui'),
39 | stdio: 'inherit'
40 | });
41 | standaloneSource = xmluiStandalonePath;
42 | console.log('XMLUI standalone built successfully!');
43 | } catch (error) {
44 | console.log('Failed to build XMLUI standalone. Please run: cd xmlui && npm run build:xmlui-standalone');
45 | process.exit(1);
46 | }
47 | }
48 |
49 | // Copy the standalone XMLUI engine
50 | const xmluiLatestPath = path.join(projectPath, 'xmlui/xmlui-latest.js');
51 | fs.copyFileSync(standaloneSource, xmluiLatestPath);
52 | console.log('Copied XMLUI engine to xmlui/xmlui-latest.js');
53 |
54 | // Check if HelloWorld extension exists and copy it
55 | const helloWorldSource = path.join(__dirname, '../../packages/xmlui-hello-world/dist/xmlui-hello-world.js');
56 | const xmluiHelloWorldPath = path.join(projectPath, 'xmlui/xmlui-hello-world.js');
57 |
58 | if (fs.existsSync(helloWorldSource)) {
59 | fs.copyFileSync(helloWorldSource, xmluiHelloWorldPath);
60 | console.log('Copied HelloWorld extension to xmlui/xmlui-hello-world.js');
61 | } else {
62 | console.log('HelloWorld extension not found. You may need to build it first:');
63 | console.log(' cd packages/xmlui-hello-world && npm run build:extension');
64 | // Create a placeholder file
65 | fs.writeFileSync(xmluiHelloWorldPath, '// HelloWorld extension not found - please build it first\n');
66 | }
67 |
68 | // Main.xmlui
69 | const mainXmlui = `<App xmlns:Extensions="component-ns:XMLUIExtensions">
70 | <VStack gap="2rem" padding="2rem">
71 | <Heading>HelloWorld Component Test</Heading>
72 | <Extensions:HelloWorld message="Hello from XMLUI!" />
73 | </VStack>
74 | </App>`;
75 |
76 | // index.html
77 | const indexHtml = `<!DOCTYPE html>
78 | <html lang="en">
79 | <head>
80 | <meta charset="UTF-8" />
81 | <meta name="viewport" content="width=device-width, initial-scale=1.0" />
82 | <title>HelloWorld Extension Test</title>
83 | <script src="xmlui/xmlui-latest.js"></script>
84 | <script src="xmlui/xmlui-hello-world.js"></script>
85 | </head>
86 | <body>
87 | </body>
88 | </html>`;
89 |
90 | // Write files
91 | fs.writeFileSync(path.join(projectPath, 'Main.xmlui'), mainXmlui);
92 | fs.writeFileSync(path.join(projectPath, 'index.html'), indexHtml);
93 |
94 | console.log(`\n Created minimal XMLUI test app at: ${projectPath}`);
95 | console.log(` Project structure:`);
96 | console.log(` ${path.basename(projectPath)}/`);
97 | console.log(` ├── Main.xmlui`);
98 | console.log(` ├── index.html`);
99 | console.log(` └── xmlui/`);
100 | console.log(` ├── xmlui-latest.js`);
101 | console.log(` └── xmlui-hello-world.js`);
102 |
103 | console.log(`\n To run the test app:`);
104 | console.log(` cd ${projectPath}`);
105 | console.log(` python3 -m http.server`);
106 | console.log(` # Or`);
107 | console.log(` npx serve`);
108 |
109 | if (!fs.existsSync(helloWorldSource)) {
110 | console.log(`\n Note: HelloWorld extension needs to be built first. Run:`);
111 | console.log(` cd packages/xmlui-hello-world && npm run build:extension`);
112 | }
113 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Stack/CVStack.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { test, expect } from "../../testing/fixtures";
2 |
3 | // =============================================================================
4 | // BASIC FUNCTIONALITY TESTS
5 | // =============================================================================
6 |
7 | test.describe("Basic Functionality", () => {
8 | test("renders items vertically and centers them", async ({ initTestBed, page }) => {
9 | await initTestBed(`
10 | <CVStack testId="cvstack" width="200px" height="200px" backgroundColor="lightgray">
11 | <Stack testId="item1" height="32px" width="32px" backgroundColor="red" />
12 | <Stack testId="item2" height="32px" width="32px" backgroundColor="blue" />
13 | <Stack testId="item3" height="32px" width="32px" backgroundColor="green" />
14 | </CVStack>
15 | `);
16 |
17 | const cvstack = page.getByTestId("cvstack");
18 | const item1 = page.getByTestId("item1");
19 | const item2 = page.getByTestId("item2");
20 | const item3 = page.getByTestId("item3");
21 |
22 | await expect(cvstack).toBeVisible();
23 | await expect(item1).toBeVisible();
24 | await expect(item2).toBeVisible();
25 | await expect(item3).toBeVisible();
26 |
27 | // Get bounding boxes to verify vertical layout and centering
28 | const cvstackBox = await cvstack.boundingBox();
29 | const item1Box = await item1.boundingBox();
30 | const item2Box = await item2.boundingBox();
31 | const item3Box = await item3.boundingBox();
32 |
33 | // Verify items are stacked vertically (item2 should be below item1, item3 below item2)
34 | expect(item2Box!.y).toBeGreaterThan(item1Box!.y + item1Box!.height - 1); // -1 for floating point tolerance
35 | expect(item3Box!.y).toBeGreaterThan(item2Box!.y + item2Box!.height - 1); // -1 for floating point tolerance
36 |
37 | // Verify items are horizontally centered within the container
38 | const cvstackCenterX = cvstackBox!.x + cvstackBox!.width / 2;
39 | const item1CenterX = item1Box!.x + item1Box!.width / 2;
40 | const item2CenterX = item2Box!.x + item2Box!.width / 2;
41 | const item3CenterX = item3Box!.x + item3Box!.width / 2;
42 |
43 | expect(Math.abs(item1CenterX - cvstackCenterX)).toBeLessThan(1);
44 | expect(Math.abs(item2CenterX - cvstackCenterX)).toBeLessThan(1);
45 | expect(Math.abs(item3CenterX - cvstackCenterX)).toBeLessThan(1);
46 |
47 | // Verify the entire content is vertically centered within the container
48 | const allItemsTopY = item1Box!.y;
49 | const allItemsBottomY = item3Box!.y + item3Box!.height;
50 | const allItemsHeight = allItemsBottomY - allItemsTopY;
51 | const cvstackCenterY = cvstackBox!.y + cvstackBox!.height / 2;
52 | const contentCenterY = allItemsTopY + allItemsHeight / 2;
53 |
54 | expect(Math.abs(contentCenterY - cvstackCenterY)).toBeLessThan(1);
55 | });
56 |
57 | test("renders empty CVStack", async ({ initTestBed, page }) => {
58 | await initTestBed(`<CVStack testId="cvstack"></CVStack>`);
59 |
60 | const cvstack = page.getByTestId("cvstack");
61 | await expect(cvstack).toBeAttached();
62 | await expect(cvstack).toBeEmpty();
63 | });
64 |
65 | test("ignores orientation property", async ({ initTestBed, page }) => {
66 | await initTestBed(`
67 | <CVStack testId="cvstack" orientation="horizontal" width="200px" height="100px" backgroundColor="lightgray">
68 | <Stack testId="item1" height="32px" width="32px" backgroundColor="red" />
69 | <Stack testId="item2" height="32px" width="32px" backgroundColor="blue" />
70 | </CVStack>
71 | `);
72 |
73 | const cvstack = page.getByTestId("cvstack");
74 | const item1 = page.getByTestId("item1");
75 | const item2 = page.getByTestId("item2");
76 |
77 | await expect(item1).toBeVisible();
78 | await expect(item2).toBeVisible();
79 |
80 | // Get bounding boxes to verify still renders vertically and centered despite orientation="horizontal"
81 | const cvstackBox = await cvstack.boundingBox();
82 | const item1Box = await item1.boundingBox();
83 | const item2Box = await item2.boundingBox();
84 |
85 | // Verify items are still stacked vertically (orientation prop should be ignored)
86 | expect(item2Box!.y).toBeGreaterThan(item1Box!.y + item1Box!.height - 1);
87 |
88 | // Verify items are still horizontally centered
89 | const cvstackCenterX = cvstackBox!.x + cvstackBox!.width / 2;
90 | const item1CenterX = item1Box!.x + item1Box!.width / 2;
91 | expect(Math.abs(item1CenterX - cvstackCenterX)).toBeLessThan(1);
92 | });
93 | });
94 |
```
--------------------------------------------------------------------------------
/xmlui/src/index.scss:
--------------------------------------------------------------------------------
```scss
1 | @use "./components-core/theming/themes.scss" as t;
2 |
3 | /* 1. Declare the order of all layers in your application.
4 | 'dynamic' is last, so it will override all other layers.
5 | IF YOU ARE CHANGING THE ORDER, MAKE SURE TO UPDATE THE ORDER IN NestedApp, too.
6 | */
7 | @layer reset, base, components, dynamic;
8 |
9 | @layer base {
10 | /************************/
11 | /* Normalization */
12 | /************************/
13 |
14 | /* Normal box-sizing model */
15 | *, *::before, *::after {
16 | box-sizing: border-box;
17 | }
18 |
19 | /* Remove default margin */
20 | * {
21 | margin: 0;
22 | }
23 |
24 | /* Allow percentage-based heights in the application */
25 | html, body {
26 | height: 100%;
27 | }
28 |
29 | #root{
30 | min-height: 100%;
31 | height: 100%;
32 | }
33 |
34 |
35 | html {
36 | -webkit-text-size-adjust: 100%;
37 | tab-size: 4;
38 | line-height: 1.7;
39 | }
40 |
41 | body {
42 | line-height: inherit;
43 | margin: 0;
44 |
45 | }
46 |
47 | /* Create a root stacking context */
48 | #root, #__next {
49 | isolation: isolate;
50 | }
51 |
52 | //tailwind css preflight
53 | *, :before, :after {
54 | box-sizing: border-box;
55 | border: 0 solid #e5e7eb
56 | }
57 |
58 | hr {
59 | color: inherit;
60 | border-top-width: 1px;
61 | height: 0
62 | }
63 |
64 | abbr:where([title]) {
65 | -webkit-text-decoration: underline dotted;
66 | text-decoration: underline dotted
67 | }
68 |
69 | h1, h2, h3, h4, h5, h6 {
70 | // font-size: inherit;
71 | font-weight: inherit
72 | }
73 |
74 | a {
75 | color: inherit;
76 | -webkit-text-decoration: inherit;
77 | text-decoration: inherit
78 | }
79 |
80 | b, strong {
81 | font-weight: bolder
82 | }
83 |
84 | code, kbd, samp, pre {
85 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
86 | font-size: 1em
87 | }
88 |
89 | small {
90 | font-size: 80%
91 | }
92 |
93 | sub, sup {
94 | vertical-align: baseline;
95 | font-size: 75%;
96 | line-height: 0;
97 | position: relative
98 | }
99 |
100 | sub {
101 | bottom: -.25em
102 | }
103 |
104 | sup {
105 | top: -.5em
106 | }
107 |
108 | table {
109 | text-indent: 0;
110 | border-color: inherit;
111 | border-collapse: collapse
112 | }
113 |
114 | button, input, optgroup, select, textarea {
115 | font-feature-settings: inherit;
116 | font-variation-settings: inherit;
117 | font-family: inherit;
118 | font-size: 100%;
119 | font-weight: inherit;
120 | line-height: inherit;
121 | color: inherit;
122 | margin: 0;
123 | padding: 0
124 | }
125 |
126 | button, select {
127 | text-transform: none
128 | }
129 |
130 | button {
131 | -webkit-appearance: button;
132 | background-color: transparent;
133 | background-image: none
134 | }
135 |
136 | [type=button] {
137 | -webkit-appearance: button;
138 | background-color: transparent;
139 | background-image: none
140 | }
141 |
142 | [type=reset] {
143 | -webkit-appearance: button;
144 | background-color: transparent;
145 | background-image: none
146 | }
147 |
148 | [type=submit] {
149 | -webkit-appearance: button;
150 | background-color: transparent;
151 | background-image: none
152 | }
153 |
154 | :-moz-focusring {
155 | outline: auto
156 | }
157 |
158 | :-moz-ui-invalid {
159 | box-shadow: none
160 | }
161 |
162 | *:focus-visible {
163 | outline: t.$focus-outline;
164 | outline-offset: t.$outlineOffset--focus;
165 | }
166 |
167 | progress {
168 | vertical-align: baseline
169 | }
170 |
171 | ::-webkit-inner-spin-button {
172 | height: auto
173 | }
174 |
175 | ::-webkit-outer-spin-button {
176 | height: auto
177 | }
178 |
179 | [type=search] {
180 | -webkit-appearance: textfield;
181 | outline-offset: -2px
182 | }
183 |
184 | ::-webkit-search-decoration {
185 | -webkit-appearance: none
186 | }
187 |
188 | ::-webkit-file-upload-button {
189 | -webkit-appearance: button;
190 | font: inherit
191 | }
192 |
193 | summary {
194 | display: list-item
195 | }
196 |
197 | blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre {
198 | margin: 0
199 | }
200 |
201 | fieldset {
202 | margin: 0;
203 | padding: 0
204 | }
205 |
206 | legend {
207 | padding: 0
208 | }
209 |
210 | // ol, ul, menu {
211 | // margin: 0;
212 | // padding: 0;
213 | // list-style: none
214 | // }
215 |
216 | dialog {
217 | padding: 0
218 | }
219 |
220 | textarea {
221 | resize: vertical
222 | }
223 |
224 | input::-ms-input-placeholder {
225 | opacity: 1;
226 | color: #9ca3af
227 | }
228 |
229 | input::placeholder {
230 | opacity: 1;
231 | color: #9ca3af
232 | }
233 |
234 | textarea::-ms-input-placeholder {
235 | opacity: 1;
236 | color: #9ca3af
237 | }
238 |
239 | textarea::placeholder {
240 | opacity: 1;
241 | color: #9ca3af
242 | }
243 |
244 | button, [role=button] {
245 | cursor: pointer
246 | }
247 |
248 | :disabled {
249 | cursor: default
250 | }
251 |
252 | img, svg, video, canvas, audio, iframe, embed, object {
253 | vertical-align: middle;
254 | display: block
255 | }
256 |
257 | img, video {
258 | max-width: 100%;
259 | height: auto
260 | }
261 |
262 | [hidden] {
263 | display: none
264 | }
265 | }
266 |
```
--------------------------------------------------------------------------------
/xmlui/tests/components-core/scripts-runner/eval-tree-func-decl.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, expect, it } from "vitest";
2 |
3 | import { evalBindingExpression } from "../../../src/components-core/script-runner/eval-tree-sync";
4 | import {createEvalContext} from "./test-helpers";
5 |
6 | describe("Evaluate function expressions (exp)", () => {
7 | it("Function decl #1", () => {
8 | // --- Arrange
9 | const source = "(function (x) {return 2 * x})(4)";
10 | const context = createEvalContext({});
11 |
12 | // --- Act
13 | const value = evalBindingExpression(source, context);
14 |
15 | // --- Arrange
16 | expect(value).equal(8);
17 | });
18 |
19 | it("Function decl #2", () => {
20 | // --- Arrange
21 | const source = "(function (x, y) {return x + y})(1, 2)";
22 | const context = createEvalContext({});
23 |
24 | // --- Act
25 | const value = evalBindingExpression(source, context);
26 |
27 | // --- Arrange
28 | expect(value).equal(3);
29 | });
30 |
31 | it("Function decl #3", () => {
32 | // --- Arrange
33 | const source = "(function myFunc(x, y) { return x + y })(1, 2)";
34 | const context = createEvalContext({});
35 |
36 | // --- Act
37 | const value = evalBindingExpression(source, context);
38 |
39 | // --- Arrange
40 | expect(value).equal(3);
41 | });
42 |
43 | it("Function decl #4", () => {
44 | // --- Arrange
45 | const source = "(function (x) { return (++x.h) })(count)";
46 | const context = createEvalContext({
47 | localContext: {
48 | count: { h: 3 }
49 | }
50 | });
51 |
52 | // --- Act
53 | const value = evalBindingExpression(source, context);
54 |
55 | // --- Arrange
56 | expect(value).equal(4);
57 | });
58 |
59 | it("Function decl #5", () => {
60 | // --- Arrange
61 | const source = "(function (x) { return x += 2; })(count)";
62 | const context = createEvalContext({
63 | localContext: {
64 | count: 3
65 | }
66 | });
67 |
68 | // --- Act
69 | const value = evalBindingExpression(source, context);
70 |
71 | // --- Arrange
72 | expect(value).equal(5);
73 | });
74 |
75 | it("Function decl #6", () => {
76 | // --- Arrange
77 | const source = "(function (x) { return x += 2 })(count + 4)";
78 | const context = createEvalContext({
79 | localContext: {
80 | count: 3
81 | }
82 | });
83 |
84 | // --- Act
85 | const value = evalBindingExpression(source, context);
86 |
87 | // --- Arrange
88 | expect(value).equal(9);
89 | });
90 |
91 | it("Function decl #7", () => {
92 | // --- Arrange
93 | const source = "[1,2,3,4,5].filter(function (x) { return x % 2 === 0; })[1]";
94 | const context = createEvalContext({
95 | localContext: {
96 | count: 3
97 | }
98 | });
99 |
100 | // --- Act
101 | const value = evalBindingExpression(source, context);
102 |
103 | // --- Arrange
104 | expect(value).equal(4);
105 | });
106 |
107 | it("Function decl #8", () => {
108 | // --- Arrange
109 | const source = "containsArray.array.filter(function (item) { return item % 2 === 0 })[1]";
110 | const context = createEvalContext({
111 | localContext: {
112 | containsArray: {
113 | array: [5, 4, 3, 2, 1]
114 | }
115 | }
116 | });
117 |
118 | // --- Act
119 | const value = evalBindingExpression(source, context);
120 |
121 | // --- Arrange
122 | expect(value).equal(2);
123 | });
124 |
125 | it("Function decl #9", () => {
126 | // --- Arrange
127 | const source = "array.reduce(function (acc, item) { return acc + item }, 0)";
128 | const context = createEvalContext({
129 | localContext: {
130 | array: [5, 4, 3, 2, 1]
131 | }
132 | });
133 |
134 | // --- Act
135 | const value = evalBindingExpression(source, context);
136 |
137 | // --- Arrange
138 | expect(value).equal(15);
139 | });
140 |
141 | it("Function decl with rest #1", () => {
142 | // --- Arrange
143 | const source = "(function (...a) { return a[0] + a[1] })(1, 2)";
144 | const context = createEvalContext({});
145 |
146 | // --- Act
147 | const value = evalBindingExpression(source, context);
148 |
149 | // --- Arrange
150 | expect(value).equal(3);
151 | });
152 |
153 | it("Function decl with rest #2", () => {
154 | // --- Arrange
155 | const source = "(function (x, ...a) { return x + a[0] + a[1] })(1, 2, 3)";
156 | const context = createEvalContext({});
157 |
158 | // --- Act
159 | const value = evalBindingExpression(source, context);
160 |
161 | // --- Arrange
162 | expect(value).equal(6);
163 | });
164 |
165 | it("Function decl reccursive #1", () => {
166 | // --- Arrange
167 | const source = "(function factorial(n) { return n <= 0 ? 1 : n * factorial(n - 1)})(3)";
168 | const context = createEvalContext({});
169 |
170 | // --- Act
171 | const value = evalBindingExpression(source, context);
172 |
173 | // --- Arrange
174 | expect(value).equal(6);
175 | });
176 | });
177 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Accordion/AccordionNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { forwardRef, useCallback, useEffect, useMemo, useState, type ForwardedRef } from "react";
2 | import * as RAccordion from "@radix-ui/react-accordion";
3 | import classnames from "classnames";
4 |
5 | import styles from "./Accordion.module.scss";
6 |
7 | import type { RegisterComponentApiFn } from "../../abstractions/RendererDefs";
8 | import { noop } from "../../components-core/constants";
9 | import { AccordionContext } from "../../components/Accordion/AccordionContext";
10 |
11 | type Props = {
12 | className?: string;
13 | style?: React.CSSProperties;
14 | children?: React.ReactNode;
15 | triggerPosition?: "start" | "end";
16 | collapsedIcon?: string;
17 | expandedIcon?: string;
18 | hideIcon?: boolean;
19 | rotateExpanded?: string;
20 | registerComponentApi?: RegisterComponentApiFn;
21 | onDisplayDidChange?: (changedValue: string[]) => void;
22 | };
23 |
24 | export const defaultProps: Pick<
25 | Props,
26 | "hideIcon" | "collapsedIcon" | "triggerPosition" | "rotateExpanded"
27 | > = {
28 | hideIcon: false,
29 | collapsedIcon: "chevrondown",
30 | triggerPosition: "end",
31 | rotateExpanded: "180deg",
32 | };
33 |
34 | export const AccordionComponent = forwardRef(function AccordionComponent(
35 | {
36 | className,
37 | style,
38 | children,
39 | hideIcon = defaultProps.hideIcon,
40 | expandedIcon,
41 | collapsedIcon = defaultProps.collapsedIcon,
42 | triggerPosition = defaultProps.triggerPosition,
43 | onDisplayDidChange = noop,
44 | registerComponentApi,
45 | rotateExpanded = defaultProps.rotateExpanded,
46 | ...rest
47 | }: Props,
48 | forwardedRef: ForwardedRef<HTMLDivElement>,
49 | ) {
50 | const [expandedItems, setExpandedItems] = useState<string[]>([]);
51 | const [itemElements, setItemElements] = useState<Set<string>>(new Set());
52 |
53 | const collapseItem = useCallback(
54 | (id: string) => {
55 | setExpandedItems((prev) => prev.filter((item) => item !== `${id}`));
56 | },
57 | [setExpandedItems],
58 | );
59 |
60 | const expandItem = useCallback(
61 | (id: string) => {
62 | if (!expandedItems.includes(`${id}`)) {
63 | setExpandedItems((prev) => [...prev, `${id}`]);
64 | }
65 | },
66 | [setExpandedItems, expandedItems],
67 | );
68 |
69 | const toggleItem = useCallback(
70 | (id: string) => {
71 | if (expandedItems.includes(`${id}`)) {
72 | collapseItem(id);
73 | } else {
74 | expandItem(id);
75 | }
76 | },
77 | [collapseItem, expandItem, expandedItems],
78 | );
79 |
80 | const register = useCallback(
81 | (id: string) => {
82 | setItemElements((prev) => {
83 | prev.add(id);
84 | return prev;
85 | });
86 | },
87 | [setItemElements],
88 | );
89 |
90 | const unRegister = useCallback(
91 | (id: string) => {
92 | setItemElements((prev) => {
93 | prev.delete(id);
94 | return prev;
95 | });
96 | },
97 | [setItemElements],
98 | );
99 |
100 | const focusItem = useCallback(
101 | (id: string) => {
102 | if (itemElements.has(`trigger_${id}`)) {
103 | const trigger = document.getElementById(`trigger_${id}`);
104 | if (trigger) {
105 | trigger.focus();
106 | }
107 | }
108 | },
109 | [itemElements],
110 | );
111 |
112 | const isExpanded = useCallback(
113 | (id: string) => {
114 | return expandedItems.includes(`${id}`);
115 | },
116 | [expandedItems],
117 | );
118 |
119 | useEffect(() => {
120 | registerComponentApi?.({
121 | expanded: isExpanded,
122 | expand: expandItem,
123 | collapse: collapseItem,
124 | toggle: toggleItem,
125 | focus: focusItem,
126 | });
127 | }, [registerComponentApi, expandItem, collapseItem, toggleItem, focusItem, isExpanded]);
128 |
129 | const contextValue = useMemo(
130 | () => ({
131 | register,
132 | unRegister,
133 | expandItem,
134 | expandedItems,
135 | hideIcon,
136 | expandedIcon,
137 | collapsedIcon,
138 | triggerPosition,
139 | rotateExpanded,
140 | }),
141 | [
142 | register,
143 | unRegister,
144 | expandedItems,
145 | hideIcon,
146 | expandedIcon,
147 | collapsedIcon,
148 | triggerPosition,
149 | expandItem,
150 | rotateExpanded,
151 | ],
152 | );
153 |
154 | useEffect(() => {
155 | onDisplayDidChange?.(expandedItems);
156 | }, [expandedItems, onDisplayDidChange]);
157 |
158 | return (
159 | <AccordionContext.Provider value={contextValue}>
160 | <RAccordion.Root
161 | {...rest}
162 | style={style}
163 | ref={forwardedRef}
164 | value={expandedItems}
165 | type="multiple"
166 | className={classnames(styles.root, className)}
167 | onValueChange={(value) => setExpandedItems(value)}
168 | >
169 | {children}
170 | </RAccordion.Root>
171 | </AccordionContext.Provider>
172 | );
173 | });
174 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Switch/Switch.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "../Toggle/Toggle.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import {
6 | createMetadata,
7 | dAutoFocus,
8 | dClick,
9 | dDidChange,
10 | dEnabled,
11 | dGotFocus,
12 | dInitialValue,
13 | dInternal,
14 | dLostFocus,
15 | dReadonly,
16 | dRequired,
17 | dValidationStatus,
18 | } from "../metadata-helpers";
19 | import { defaultProps, Toggle } from "../Toggle/Toggle";
20 |
21 | const COMP = "Switch";
22 |
23 | export const SwitchMd = createMetadata({
24 | status: "stable",
25 | description: "`Switch` enables users to toggle between two states: on and off.",
26 | parts: {
27 | label: {
28 | description: "The label displayed for the switch.",
29 | },
30 | input: {
31 | description: "The switch input area.",
32 | },
33 | },
34 | props: {
35 | required: dRequired(),
36 | initialValue: dInitialValue(defaultProps.initialValue),
37 | autoFocus: dAutoFocus(),
38 | readOnly: dReadonly(),
39 | enabled: dEnabled(),
40 | validationStatus: dValidationStatus(defaultProps.validationStatus),
41 | description: dInternal(
42 | `(*** NOT IMPLEMENTED YET ***) This optional property displays an alternate description ` +
43 | `of the ${COMP} besides its label.`,
44 | ),
45 | },
46 | events: {
47 | click: dClick(COMP),
48 | gotFocus: dGotFocus(COMP),
49 | lostFocus: dLostFocus(COMP),
50 | didChange: dDidChange(COMP),
51 | },
52 | apis: {
53 | value: {
54 | description:
55 | `This property holds the current value of the ${COMP}, which can be either ` +
56 | `"true" (on) or "false" (off).`,
57 | signature: "get value():boolean",
58 | },
59 | setValue: {
60 | description: `This API sets the value of the \`${COMP}\`. You can use it to programmatically change the value.`,
61 | signature: "setValue(value: boolean): void",
62 | parameters: {
63 | value: "The new value to set. Can be either true (on) or false (off).",
64 | },
65 | },
66 | },
67 | themeVars: parseScssVar(styles.themeVars),
68 | limitThemeVarsToComponent: true,
69 | defaultThemeVars: {
70 | [`borderColor-checked-${COMP}--error`]: `$borderColor-${COMP}--error`,
71 | [`backgroundColor-checked-${COMP}--error`]: `$borderColor-${COMP}--error`,
72 | [`borderColor-checked-${COMP}--warning`]: `$borderColor-${COMP}--warning`,
73 | [`backgroundColor-checked-${COMP}--warning`]: `$borderColor-${COMP}--warning`,
74 | [`borderColor-checked-${COMP}--success`]: `$borderColor-${COMP}--success`,
75 | [`backgroundColor-checked-${COMP}--success`]: `$borderColor-${COMP}--success`,
76 | [`backgroundColor-${COMP}`]: "$color-surface-0",
77 | [`borderColor-${COMP}`]: "$color-surface-200",
78 | [`borderWidth-${COMP}`]: "1px",
79 | [`backgroundColor-indicator-${COMP}`]: "$color-surface-400",
80 | [`backgroundColor-${COMP}-indicator--disabled`]: "$backgroundColor-primary",
81 | [`backgroundColor-indicator-checked-${COMP}`]: "$backgroundColor-primary",
82 | [`borderColor-checked-${COMP}`]: "$color-primary-500",
83 | [`backgroundColor-checked-${COMP}`]: "$color-primary-500",
84 | [`backgroundColor-${COMP}--disabled`]: "$color-surface-200",
85 |
86 | dark: {
87 | [`backgroundColor-indicator-${COMP}`]: "$color-surface-500",
88 | },
89 | },
90 | });
91 |
92 | export const switchComponentRenderer = createComponentRenderer(
93 | COMP,
94 | SwitchMd,
95 | ({
96 | node,
97 | extractValue,
98 | className,
99 | updateState,
100 | state,
101 | lookupEventHandler,
102 | registerComponentApi,
103 | }) => {
104 | return (
105 | <Toggle
106 | enabled={extractValue.asOptionalBoolean(node.props.enabled)}
107 | className={className}
108 | initialValue={extractValue.asOptionalBoolean(
109 | node.props.initialValue,
110 | defaultProps.initialValue,
111 | )}
112 | value={state?.value}
113 | readOnly={extractValue.asOptionalBoolean(node.props.readOnly)}
114 | validationStatus={extractValue(node.props.validationStatus)}
115 | updateState={updateState}
116 | autoFocus={extractValue.asOptionalBoolean(node.props.autoFocus)}
117 | onClick={lookupEventHandler("click")}
118 | onDidChange={lookupEventHandler("didChange")}
119 | onFocus={lookupEventHandler("gotFocus")}
120 | onBlur={lookupEventHandler("lostFocus")}
121 | required={extractValue.asOptionalBoolean(node.props.required)}
122 | variant="switch"
123 | registerComponentApi={registerComponentApi}
124 | />
125 | );
126 | },
127 | );
128 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Stack/CHStack.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { test, expect } from "../../testing/fixtures";
2 |
3 | // =============================================================================
4 | // BASIC FUNCTIONALITY TESTS
5 | // =============================================================================
6 |
7 | test.describe("Basic Functionality", () => {
8 | test("renders items horizontally and centers them", async ({ initTestBed, page }) => {
9 | await initTestBed(`
10 | <CHStack testId="chstack" width="200px" height="200px" backgroundColor="lightgray">
11 | <Stack testId="item1" height="32px" width="32px" backgroundColor="red" />
12 | <Stack testId="item2" height="32px" width="32px" backgroundColor="blue" />
13 | <Stack testId="item3" height="32px" width="32px" backgroundColor="green" />
14 | </CHStack>
15 | `);
16 |
17 | const chstack = page.getByTestId("chstack");
18 | const item1 = page.getByTestId("item1");
19 | const item2 = page.getByTestId("item2");
20 | const item3 = page.getByTestId("item3");
21 |
22 | await expect(chstack).toBeVisible();
23 | await expect(item1).toBeVisible();
24 | await expect(item2).toBeVisible();
25 | await expect(item3).toBeVisible();
26 |
27 | // Get bounding boxes to verify horizontal layout and centering
28 | const chstackBox = await chstack.boundingBox();
29 | const item1Box = await item1.boundingBox();
30 | const item2Box = await item2.boundingBox();
31 | const item3Box = await item3.boundingBox();
32 |
33 | // Verify items are stacked horizontally (item2 should be to the right of item1, item3 to the right of item2)
34 | expect(item2Box!.x).toBeGreaterThan(item1Box!.x + item1Box!.width - 1); // -1 for floating point tolerance
35 | expect(item3Box!.x).toBeGreaterThan(item2Box!.x + item2Box!.width - 1); // -1 for floating point tolerance
36 |
37 | // Verify items are vertically centered within the container
38 | const chstackCenterY = chstackBox!.y + chstackBox!.height / 2;
39 | const item1CenterY = item1Box!.y + item1Box!.height / 2;
40 | const item2CenterY = item2Box!.y + item2Box!.height / 2;
41 | const item3CenterY = item3Box!.y + item3Box!.height / 2;
42 |
43 | expect(Math.abs(item1CenterY - chstackCenterY)).toBeLessThan(1);
44 | expect(Math.abs(item2CenterY - chstackCenterY)).toBeLessThan(1);
45 | expect(Math.abs(item3CenterY - chstackCenterY)).toBeLessThan(1);
46 |
47 | // Verify the entire content is horizontally centered within the container
48 | const allItemsLeftX = item1Box!.x;
49 | const allItemsRightX = item3Box!.x + item3Box!.width;
50 | const allItemsWidth = allItemsRightX - allItemsLeftX;
51 | const chstackCenterX = chstackBox!.x + chstackBox!.width / 2;
52 | const contentCenterX = allItemsLeftX + allItemsWidth / 2;
53 |
54 | expect(Math.abs(contentCenterX - chstackCenterX)).toBeLessThan(1);
55 | });
56 |
57 | test("renders empty CHStack", async ({ initTestBed, page }) => {
58 | await initTestBed(`<CHStack testId="chstack"></CHStack>`);
59 |
60 | const chstack = page.getByTestId("chstack");
61 | await expect(chstack).toBeAttached();
62 | await expect(chstack).toBeEmpty();
63 | });
64 |
65 | test("ignores orientation property", async ({ initTestBed, page }) => {
66 | await initTestBed(`
67 | <CHStack testId="chstack" orientation="vertical" width="200px" height="100px" backgroundColor="lightgray">
68 | <Stack testId="item1" height="32px" width="32px" backgroundColor="red" />
69 | <Stack testId="item2" height="32px" width="32px" backgroundColor="blue" />
70 | </CHStack>
71 | `);
72 |
73 | const chstack = page.getByTestId("chstack");
74 | const item1 = page.getByTestId("item1");
75 | const item2 = page.getByTestId("item2");
76 |
77 | await expect(item1).toBeVisible();
78 | await expect(item2).toBeVisible();
79 |
80 | // Get bounding boxes to verify still renders horizontally and centered despite orientation="vertical"
81 | const chstackBox = await chstack.boundingBox();
82 | const item1Box = await item1.boundingBox();
83 | const item2Box = await item2.boundingBox();
84 |
85 | // Verify items are still stacked horizontally (orientation prop should be ignored)
86 | expect(item2Box!.x).toBeGreaterThan(item1Box!.x + item1Box!.width - 1);
87 |
88 | // Verify items are still vertically centered
89 | const chstackCenterY = chstackBox!.y + chstackBox!.height / 2;
90 | const item1CenterY = item1Box!.y + item1Box!.height / 2;
91 | expect(Math.abs(item1CenterY - chstackCenterY)).toBeLessThan(1);
92 | });
93 | });
94 |
```
--------------------------------------------------------------------------------
/xmlui/bin/viteConfig.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { defineConfig } from "vite";
2 | import type { UserConfig } from "vite";
3 | import react from "@vitejs/plugin-react";
4 | import svgr from "vite-plugin-svgr";
5 | import { default as ViteYaml } from "@modyfi/vite-plugin-yaml";
6 | import { default as ViteXmlui } from "./vite-xmlui-plugin";
7 | import * as path from "path";
8 |
9 | type ViteConfigData = {
10 | flatDist?: boolean;
11 | withRelativeRoot?: boolean;
12 | flatDistUiPrefix?: string;
13 | };
14 |
15 | export async function getViteConfig({
16 | flatDist = false,
17 | withRelativeRoot = false,
18 | flatDistUiPrefix = "",
19 | }: ViteConfigData = {}) {
20 | //TODO finish this (merge smart)
21 | let overrides: UserConfig = {};
22 | try {
23 | const configOverrides = await import(process.cwd() + `/vite.config-overrides`);
24 | overrides = configOverrides.default || {};
25 | } catch (e) {
26 | // console.error(e);
27 | }
28 |
29 | return defineConfig({
30 | plugins: [react(), svgr(), ViteYaml(), ViteXmlui({}), ...(overrides.plugins || [])],
31 | base: withRelativeRoot ? "" : undefined,
32 | // experimental: {
33 | // renderBuiltUrl: (filename, {type, hostType, hostId}) =>{
34 | // if (type === 'asset') {
35 | // // return { runtime: `window.__toCdnUrl(${JSON.stringify(filename)})` }
36 | // return 'https://static.themohoz.com/xmlui/v0.31/' + filename
37 | // } else {
38 | // return { relative: true }
39 | // }
40 | // }
41 | // },
42 | define: overrides.define,
43 | resolve: {
44 | alias: overrides.resolve?.alias,
45 | extensions: [
46 | ".js",
47 | ".ts",
48 | ".jsx",
49 | ".tsx",
50 | ".json",
51 | ".xmlui",
52 | ".xmlui.xs",
53 | ".xs",
54 | ...(overrides.resolve?.extensions || []),
55 | ],
56 | },
57 | css: {
58 | ...overrides.css,
59 | preprocessorOptions: {
60 | ...overrides.css?.preprocessorOptions,
61 | scss: {
62 | ...overrides.css?.preprocessorOptions?.scss,
63 | silenceDeprecations: ["import", "global-builtin", "new-global"],
64 | },
65 | },
66 | },
67 | optimizeDeps: {
68 | extensions: [".xmlui", ".xmlui.xs", ".xs"],
69 | ...overrides.optimizeDeps,
70 | },
71 | build: {
72 | rollupOptions: {
73 | input: path.resolve(process.cwd(), "index.html"),
74 | output: {
75 | assetFileNames: (assetInfo) => {
76 | const extType = assetInfo.name?.split(".").pop();
77 | if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType!)) {
78 | return flatDist
79 | ? `${flatDistUiPrefix}internal_img_[name].[hash][extname]`
80 | : `internal/img/[name].[hash][extname]`;
81 | }
82 | if (assetInfo.name === "index.css") {
83 | return flatDist
84 | ? `${flatDistUiPrefix}internal_[name].[hash][extname]`
85 | : `internal/[name].[hash][extname]`;
86 | }
87 | return flatDist
88 | ? `${flatDistUiPrefix}internal_chunks_[name].[hash][extname]`
89 | : `internal/chunks/[name].[hash][extname]`;
90 | },
91 | chunkFileNames: flatDist
92 | ? `${flatDistUiPrefix}internal_chunks_[name].[hash].js`
93 | : "internal/chunks/[name].[hash].js",
94 | entryFileNames: flatDist
95 | ? `${flatDistUiPrefix}internal_[name].[hash].js`
96 | : "internal/[name].[hash].js",
97 | },
98 | // treeshake: {
99 | // preset: "recommended",
100 | // moduleSideEffects: (id: string, external: boolean)=>{
101 | // if(id.includes(path.resolve(process.cwd(), 'index.html'))){ //otherwise tree shaking doesn't work
102 | // return true;
103 | // }
104 | // if(id.includes(path.resolve(process.cwd(), "src", 'main.tsx'))){ //otherwise tree shaking doesn't work
105 | // return true;
106 | // }
107 | // return false;
108 | // }
109 | // }
110 | // assetFileNames: (assetInfo) => {
111 | // let extType = assetInfo.name?.split(".").pop();
112 | // if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType!)) {
113 | // return `img/[name][extname]`;
114 | // }
115 | // if (assetInfo.name === "index.css") {
116 | // return `[name][extname]`;
117 | // }
118 | // return `chunks/[name][extname]`;
119 | // },
120 | // chunkFileNames: "chunks/[name].js",
121 | // entryFileNames: "[name].js",
122 | // },
123 | },
124 | },
125 | });
126 | }
127 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Card/Card.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./Card.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import { createMetadata, dClick } from "../metadata-helpers";
6 | import { orientationOptionMd } from "../abstractions";
7 | import { Card, defaultProps } from "./CardNative";
8 |
9 | const COMP = "Card";
10 |
11 | export const CardMd = createMetadata({
12 | status: "stable",
13 | description:
14 | "`Card` is a versatile container that groups related content with a visual " +
15 | "boundary, typically featuring background color, padding, borders, and rounded " +
16 | "corners. It's ideal for organizing information, creating sections, and " +
17 | "establishing visual hierarchy in your interface.",
18 | props: {
19 | avatarUrl: {
20 | description:
21 | `The url for an avarar image. If not specified, but [\`showAvatar\`](#showAvatar) is true, ${COMP} will show the ` +
22 | `first letters of the [\`title\`](#title).`,
23 | type: "string",
24 | },
25 | showAvatar: {
26 | description: `Indicates whether the avatar should be displayed`,
27 | valueType: "boolean",
28 | defaultValue: defaultProps.showAvatar,
29 | },
30 | avatarSize: {
31 | description: `This prop sets the size of the avatar. The default value is \`sm\`.`,
32 | availableValues: ["xs", "sm", "md", "lg"],
33 | valueType: "string",
34 | },
35 | title: {
36 | description:
37 | "This prop sets the pre-styled title. If the property is not set, no title " +
38 | "is displayed in the Card.",
39 | valueType: "string",
40 | },
41 | subtitle: {
42 | description:
43 | "This prop sets the pre-styled subtitle. If the property is not set, no subtitle " +
44 | "is displayed in the Card.",
45 | valueType: "string",
46 | },
47 | linkTo: {
48 | description:
49 | "This optional property wraps the title in a \`Link\` component that is clickable to navigate.",
50 | valueType: "string",
51 | },
52 | orientation: {
53 | description:
54 | `An optional property that governs the ${COMP}'s orientation ` +
55 | `(whether the ${COMP} lays out its children in a row or a column). ` +
56 | `If the orientation is set to \`horizontal\`, the ${COMP} will display ` +
57 | `its children in a row, except for its [\`title\`](#title) and [\`subtitle\`](#subtitle).`,
58 | availableValues: orientationOptionMd,
59 | valueType: "string",
60 | defaultValue: defaultProps.orientation,
61 | },
62 | },
63 | events: {
64 | click: dClick(COMP),
65 | },
66 | themeVars: parseScssVar(styles.themeVars),
67 | defaultThemeVars: {
68 | [`padding-${COMP}`]: "$space-4",
69 | [`border-${COMP}`]: "1px solid $borderColor",
70 | [`borderRadius-${COMP}`]: "$borderRadius",
71 | [`boxShadow-${COMP}`]: "none",
72 | [`backgroundColor-${COMP}`]: "$color-surface-raised",
73 | [`gap-${COMP}`]: "var(--stack-gap-default)",
74 | [`gap-title-${COMP}`]: "$gap-none",
75 | [`gap-avatar-${COMP}`]: "$gap-normal",
76 | [`verticalAlignment-title-${COMP}`]: "center",
77 | },
78 | themeVarDescriptions: {
79 | [`gap-${COMP}`]: "The gap between the component's children.",
80 | [`gap-title-${COMP}`]: "The gap between the title and the subtitle",
81 | [`gap-avatar-${COMP}`]: "The gap between the avatar and the title panel",
82 | [`horizontalAlignment-title-${COMP}`]:
83 | "The horizontal alignment of panel with the title and subtitle",
84 | [`verticalAlignment-title-${COMP}`]:
85 | "The vertical alignment of the title and subtitle to the avatar",
86 | },
87 | });
88 |
89 | export const cardComponentRenderer = createComponentRenderer(
90 | "Card",
91 | CardMd,
92 | ({ node, extractValue, renderChild, className }) => {
93 | return (
94 | <Card
95 | className={className}
96 | title={extractValue.asOptionalString(node.props.title)}
97 | linkTo={extractValue.asOptionalString(node.props.linkTo)}
98 | subtitle={extractValue.asOptionalString(node.props.subtitle)}
99 | avatarUrl={extractValue.asOptionalString(node.props.avatarUrl)}
100 | showAvatar={extractValue.asOptionalBoolean(node.props.showAvatar)}
101 | avatarSize={extractValue.asOptionalString(node.props.avatarSize)}
102 | orientation={extractValue.asOptionalString(node.props.orientation)}
103 | >
104 | {renderChild(node.children, {
105 | type: "Stack",
106 | orientation: "vertical",
107 | })}
108 | </Card>
109 | );
110 | },
111 | );
112 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/ColorPicker/ColorPickerNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import type { CSSProperties, ForwardedRef } from "react";
2 | import { useEffect, useId, useTransition } from "react";
3 | import { forwardRef, useCallback, useRef } from "react";
4 | import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs";
5 | import { noop } from "../../components-core/constants";
6 | import type { ValidationStatus } from "../abstractions";
7 | import { useEvent } from "../../components-core/utils/misc";
8 | import styles from "./ColorPicker.module.scss";
9 | import classnames from "classnames";
10 | import { composeRefs } from "@radix-ui/react-compose-refs";
11 | import { PART_INPUT } from "../../components-core/parts";
12 |
13 | type Props = {
14 | id?: string;
15 | value?: string;
16 | initialValue?: string;
17 | style?: CSSProperties;
18 | className?: string;
19 | onDidChange?: (newValue: string) => void;
20 | onFocus?: () => void;
21 | onBlur?: () => void;
22 | updateState?: UpdateStateFn;
23 | registerComponentApi?: RegisterComponentApiFn;
24 | autoFocus?: boolean;
25 | tabIndex?: number;
26 | required?: boolean;
27 | readOnly?: boolean;
28 | enabled?: boolean;
29 | validationStatus?: ValidationStatus;
30 | };
31 |
32 | export const defaultProps: Pick<
33 | Props,
34 | "initialValue" | "value" | "enabled" | "validationStatus"
35 | > = {
36 | initialValue: "#000000",
37 | value: "#000000",
38 | enabled: true,
39 | validationStatus: "none",
40 | };
41 |
42 | export const ColorPicker = forwardRef(
43 | (
44 | {
45 | id,
46 | style,
47 | className,
48 | updateState,
49 | onDidChange = noop,
50 | onFocus = noop,
51 | onBlur = noop,
52 | registerComponentApi,
53 | enabled = defaultProps.enabled,
54 | readOnly,
55 | value = defaultProps.value,
56 | autoFocus,
57 | tabIndex = 0,
58 | required,
59 | validationStatus = defaultProps.validationStatus,
60 | initialValue = defaultProps.initialValue,
61 | ...rest
62 | }: Props,
63 | forwardedRef: ForwardedRef<HTMLInputElement>,
64 | ) => {
65 | const [isPending, startTransition] = useTransition();
66 | const inputRef = useRef<HTMLInputElement>(null);
67 | const composedRef = forwardedRef ? composeRefs(forwardedRef, inputRef) : inputRef;
68 |
69 | const updateValue = useCallback(
70 | (value: string) => {
71 | updateState({ value });
72 | onDidChange(value);
73 | },
74 | [onDidChange, updateState],
75 | );
76 |
77 |
78 | const updateValueWithTransition = useCallback(
79 | (value: string, immediate = false) => {
80 | if (immediate) {
81 | updateValue(value);
82 | } else {
83 | startTransition(() => {
84 | updateValue(value);
85 | });
86 | }
87 | },
88 | [updateValue, startTransition],
89 | );
90 |
91 | const onInputChange = useCallback(
92 | (event: any) => {
93 | updateValueWithTransition(event.target.value);
94 | },
95 | [updateValueWithTransition],
96 | );
97 |
98 | useEffect(() => {
99 | updateState({ value: initialValue }, { initial: true });
100 | }, [initialValue, updateState]);
101 |
102 | // --- Manage obtaining and losing the focus
103 | const handleOnFocus = useCallback(() => {
104 | onFocus?.();
105 | }, [onFocus]);
106 |
107 | const handleOnBlur = useCallback(() => {
108 | onBlur?.();
109 | }, [onBlur]);
110 |
111 | const focus = useCallback(() => {
112 | inputRef.current?.focus();
113 | }, []);
114 |
115 | const setValue = useEvent((newValue) => {
116 | updateValueWithTransition(newValue, true); // Immediate update for programmatic changes
117 | });
118 |
119 | useEffect(() => {
120 | registerComponentApi?.({
121 | focus,
122 | setValue,
123 | });
124 | }, [focus, registerComponentApi, setValue]);
125 |
126 | {/* Produces a 7 character RGB color output in hex as a string type */ }
127 | return (
128 | <input
129 | {...rest}
130 | id={id}
131 | className={classnames(className,styles.colorInput, {
132 | [styles.disabled]: !enabled,
133 | [styles.error]: validationStatus === "error",
134 | [styles.warning]: validationStatus === "warning",
135 | [styles.valid]: validationStatus === "valid",
136 | })}
137 | data-part-id={PART_INPUT}
138 | style={style}
139 | disabled={!enabled}
140 | onFocus={handleOnFocus}
141 | onChange={onInputChange}
142 | readOnly={readOnly}
143 | autoFocus={autoFocus}
144 | tabIndex={tabIndex}
145 | onBlur={handleOnBlur}
146 | required={required}
147 | type="color"
148 | inputMode="text"
149 | ref={composedRef}
150 | value={value}
151 | />
152 | );
153 | },
154 | );
155 |
156 | ColorPicker.displayName = "ColorPicker";
157 |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/utils/base64-utils.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Convert Uint8Array to base64 string without using btoa.
3 | * This approach handles all Unicode characters correctly.
4 | */
5 | export function uint8ArrayToBase64(bytes: Uint8Array): string {
6 | const base64abc = [
7 | "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
8 | "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9 | "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
10 | "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
11 | "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"
12 | ];
13 |
14 | let result = '';
15 | let i;
16 | const l = bytes.length;
17 |
18 | for (i = 2; i < l; i += 3) {
19 | result += base64abc[bytes[i - 2] >> 2];
20 | result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
21 | result += base64abc[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)];
22 | result += base64abc[bytes[i] & 0x3f];
23 | }
24 |
25 | if (i === l + 1) {
26 | // 1 byte left
27 | result += base64abc[bytes[i - 2] >> 2];
28 | result += base64abc[(bytes[i - 2] & 0x03) << 4];
29 | result += "==";
30 | }
31 |
32 | if (i === l) {
33 | // 2 bytes left
34 | result += base64abc[bytes[i - 2] >> 2];
35 | result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
36 | result += base64abc[(bytes[i - 1] & 0x0f) << 2];
37 | result += "=";
38 | }
39 |
40 | return result;
41 | }
42 |
43 | /**
44 | * Encode string to base64 value (handles Unicode properly).
45 | * This is a safe alternative to btoa() that works with Unicode characters.
46 | */
47 | export function encodeToBase64(value: string | number | boolean | object | null): string {
48 | if (value === null || value === undefined) {
49 | return "";
50 | }
51 |
52 | const valueToString = typeof value === "object" ? JSON.stringify(value) : value.toString();
53 |
54 | if (typeof window !== 'undefined') {
55 | // Use TextEncoder to handle Unicode properly
56 | const encoder = new TextEncoder();
57 | const data = encoder.encode(valueToString);
58 | return uint8ArrayToBase64(data);
59 | }
60 |
61 | // Node.js environment
62 | const buff = Buffer.from(valueToString, 'utf8');
63 | return buff.toString('base64');
64 | }
65 |
66 | /**
67 | * Convert base64 string to Uint8Array without using atob.
68 | * Use this for binary data (e.g., compressed data).
69 | */
70 | export function base64ToUint8Array(base64: string): Uint8Array {
71 | const base64abc = [
72 | "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
73 | "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
74 | "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
75 | "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
76 | "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"
77 | ];
78 |
79 | // Create lookup table
80 | const lookup = new Uint8Array(256);
81 | for (let i = 0; i < base64abc.length; i++) {
82 | lookup[base64abc[i].charCodeAt(0)] = i;
83 | }
84 |
85 | // Remove padding
86 | let paddingLength = 0;
87 | if (base64.endsWith("==")) {
88 | paddingLength = 2;
89 | } else if (base64.endsWith("=")) {
90 | paddingLength = 1;
91 | }
92 |
93 | const length = base64.length;
94 | const bufferLength = (length * 3) / 4 - paddingLength;
95 | const bytes = new Uint8Array(bufferLength);
96 |
97 | let p = 0;
98 | for (let i = 0; i < length; i += 4) {
99 | const encoded1 = lookup[base64.charCodeAt(i)];
100 | const encoded2 = lookup[base64.charCodeAt(i + 1)];
101 | const encoded3 = lookup[base64.charCodeAt(i + 2)];
102 | const encoded4 = lookup[base64.charCodeAt(i + 3)];
103 |
104 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
105 | if (p < bufferLength) {
106 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
107 | }
108 | if (p < bufferLength) {
109 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
110 | }
111 | }
112 |
113 | return bytes;
114 | }
115 |
116 | /**
117 | * Decode base64 value to string (handles Unicode properly).
118 | * This is a safe alternative to atob() that works with Unicode characters.
119 | */
120 | export function decodeFromBase64(value: string | number | boolean | object | null): string | null {
121 | if (!value) {
122 | return null;
123 | }
124 |
125 | const valueToString = typeof value === "object" ? JSON.stringify(value) : value.toString();
126 |
127 | if (typeof window !== "undefined") {
128 | // Decode from base64 and handle Unicode properly
129 | const bytes = base64ToUint8Array(valueToString);
130 | const decoder = new TextDecoder();
131 | return decoder.decode(bytes);
132 | }
133 |
134 | // Node.js environment
135 | const buff = Buffer.from(valueToString, "base64");
136 | return buff.toString("utf8");
137 | }
138 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Stack/Stack.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | **Key features:**
4 | - **Dynamic orientation**: Switch between horizontal and vertical layouts programmatically
5 | - **Comprehensive alignment**: Precise control over both horizontal and vertical child positioning
6 | - **Flexible spacing**: Configurable gaps between elements with theme-aware sizing
7 | - **Content wrapping**: Automatic wrapping when space constraints require it
8 | - **Order control**: Reverse child element order with the reverse property
9 | - **Foundation for variants**: Powers HStack, VStack, CHStack, and CVStack specialized components
10 |
11 | For common scenarios, consider the specialized variants: [HStack](/components/HStack) (horizontal), [VStack](/components/VStack) (vertical), [CHStack](/components/CHStack) (centered horizontal), and [CVStack](/components/CVStack) (centered vertical).
12 |
13 | %-DESC-END
14 |
15 | %-PROP-START gap
16 |
17 | In the following example we use pixels, characters (shorthand `ch`), and the `em` CSS unit size which is a relative size to the font size of the element (See size values).
18 |
19 | ```xmlui-pg copy {3, 10} display name="Example: gap"
20 | <App>
21 | <Stack orientation="horizontal" backgroundColor="cyan"
22 | gap="80px">
23 | <Stack height="40px" width="40px" backgroundColor="red" />
24 | <Stack height="40px" width="40px" backgroundColor="green" />
25 | <Stack height="40px" width="40px" backgroundColor="blue" />
26 | <Stack height="40px" width="40px" backgroundColor="yellow" />
27 | </Stack>
28 | <Stack orientation="horizontal" backgroundColor="cyan"
29 | gap="12ch">
30 | <Stack height="40px" width="40px" backgroundColor="red" />
31 | <Stack height="40px" width="40px" backgroundColor="green" />
32 | <Stack height="40px" width="40px" backgroundColor="blue" />
33 | <Stack height="40px" width="40px" backgroundColor="yellow" />
34 | </Stack>
35 | </App>
36 | ```
37 |
38 | %-PROP-END
39 |
40 | %-PROP-START horizontalAlignment
41 |
42 | >[!INFO]
43 | > The `start` and `end` values can be affected by i18n if the layout is in a right-to-left writing style.
44 |
45 | ```xmlui-pg copy {3} display name="Example: horizontalAlignment"
46 | <App>
47 | <Stack width="100%" horizontalAlignment="center" backgroundColor="cyan">
48 | <Stack width="36px" height="36px" backgroundColor="red" />
49 | </Stack>
50 | </App>
51 | ```
52 |
53 | %-PROP-END
54 |
55 | %-PROP-START reverse
56 |
57 | Default is **false**, which indicates a left-to-right layout.
58 |
59 | ```xmlui-pg copy display name="Example: reverse"
60 | <App>
61 | <Stack backgroundColor="cyan">
62 | <Stack gap="10px" orientation="horizontal">
63 | <Stack height="40px" width="40px" backgroundColor="red" />
64 | <Stack height="40px" width="40px" backgroundColor="green" />
65 | <Stack height="40px" width="40px" backgroundColor="blue" />
66 | </Stack>
67 | <Stack reverse="true" orientation="horizontal">
68 | <Stack height="40px" width="40px" backgroundColor="red" />
69 | <Stack height="40px" width="40px" backgroundColor="green" />
70 | <Stack height="40px" width="40px" backgroundColor="blue" />
71 | </Stack>
72 | </Stack>
73 | </App>
74 | ```
75 |
76 | %-PROP-END
77 |
78 | %-PROP-START verticalAlignment
79 |
80 | ```xmlui-pg copy {2} display name="Example: verticalAlignment"
81 | <App>
82 | <Stack height="100px" verticalAlignment="end" backgroundColor="cyan">
83 | <Stack width="36px" height="36px" backgroundColor="red" />
84 | </Stack>
85 | </App>
86 | ```
87 |
88 | %-PROP-END
89 |
90 | %-PROP-START wrapContent
91 |
92 | Optional boolean which wraps the content if set to true and the available space is not big enough. Works in all orientations.
93 |
94 | ```xmlui-pg copy display name="Example: wrapContent"
95 | <App>
96 | <Stack wrapContent="true" width="140px" orientation="horizontal" backgroundColor="cyan">
97 | <Stack height="40px" width="40px" backgroundColor="blue" />
98 | <Stack height="40px" width="40px" backgroundColor="blue" />
99 | <Stack height="40px" width="40px" backgroundColor="blue" />
100 | <Stack height="40px" width="40px" backgroundColor="blue" />
101 | </Stack>
102 | </App>
103 | ```
104 |
105 | %-PROP-END
106 |
107 | %-EVENT-START click
108 |
109 | Describes the logic that fires when the component is clicked.
110 |
111 | ```xmlui-pg copy display name="Example: click"
112 | <App>
113 | <HStack var.shown="{false}">
114 | <Stack height="40px" width="40px" backgroundColor="red" onClick="shown = !shown" />
115 | <Stack when="{shown}" height="40px" width="40px" backgroundColor="blue" />
116 | </HStack>
117 | </App>
118 | ```
119 |
120 | %-EVENT-END
121 |
122 | ## Styling
123 |
124 | `Stack` is a layout container; its purpose is to render its nested child components.
125 | `Stack` has no theme variables to change its visual appearance.
126 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/NavGroup/NavGroup.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./NavGroup.module.scss";
2 | import navLinkStyles from "../NavLink/NavLink.module.scss";
3 |
4 | import { createComponentRenderer } from "../../components-core/renderers";
5 | import { parseScssVar } from "../../components-core/theming/themeVars";
6 | import { Icon } from "../Icon/IconNative";
7 | import { createMetadata, d, dEnabled, dLabel } from "../metadata-helpers";
8 | import { defaultProps, NavGroup } from "./NavGroupNative";
9 |
10 | const COMP = "NavGroup";
11 |
12 | export const NavGroupMd = createMetadata({
13 | status: "stable",
14 | description:
15 | "`NavGroup` creates collapsible containers for organizing related navigation " +
16 | "items into hierarchical menu structures. It groups `NavLink` components and " +
17 | "other `NavGroup` components, providing expandable submenus with customizable " +
18 | "icons and states.",
19 | props: {
20 | label: dLabel(),
21 | initiallyExpanded: d(
22 | "This property defines whether the group is initially expanded or collapsed. If not " +
23 | "defined, the group is collapsed by default.",
24 | ),
25 | enabled: dEnabled(),
26 | to: {
27 | description: `This property defines an optional navigation link.`,
28 | valueType: "string",
29 | },
30 | icon: {
31 | description: `This property defines an optional icon to display along with the \`${COMP}\` label.`,
32 | valueType: "string",
33 | },
34 | iconHorizontalExpanded: {
35 | description:
36 | "Set a custom icon to display when the navigation menu is expanded, " +
37 | "is in a **horizontal** app layout, and is in a navigation submenu.",
38 | valueType: "string",
39 | defaultValue: defaultProps.iconHorizontalExpanded,
40 | },
41 | iconVerticalExpanded: {
42 | description:
43 | "Set a custom icon to display when the navigation menu is expanded, " +
44 | "is in a **vertical** app layout, or is in a **horizontal** layout and is the top-level navigation item in the menu.",
45 | valueType: "string",
46 | defaultValue: defaultProps.iconVerticalExpanded,
47 | },
48 | iconHorizontalCollapsed: {
49 | description:
50 | "Set a custom icon to display when the navigation menu is collapsed, " +
51 | "is in a **horizontal** app layout, and is in a navigation submenu.",
52 | valueType: "string",
53 | defaultValue: defaultProps.iconHorizontalCollapsed,
54 | },
55 | iconVerticalCollapsed: {
56 | description:
57 | "Set a custom icon to display when the navigation menu is collapsed, " +
58 | "is in a **vertical** app layout, or is in a **horizontal** layout and is the top-level navigation item in the menu.",
59 | valueType: "string",
60 | defaultValue: defaultProps.iconVerticalCollapsed,
61 | },
62 | noIndicator: {
63 | description:
64 | `This Boolean property controls whether to hide the visual indicator for active and ` +
65 | `hovered states. When set to \`true\`, the indicator line will not be displayed on the ` +
66 | `\`${COMP}\` toggle button.`,
67 | valueType: "boolean",
68 | defaultValue: defaultProps.noIndicator,
69 | },
70 | },
71 | themeVars: parseScssVar(styles.themeVars),
72 | defaultThemeVars: {
73 | [`backgroundColor-dropdown-${COMP}`]: "$backgroundColor-primary",
74 | [`borderRadius-dropdown-${COMP}`]: "$borderRadius",
75 | [`boxShadow-dropdown-${COMP}`]: "$boxShadow-spread",
76 | },
77 | });
78 |
79 | export const navGroupComponentRenderer = createComponentRenderer(
80 | COMP,
81 | NavGroupMd,
82 | ({ node, extractValue, renderChild }) => {
83 | return (
84 | <NavGroup
85 | label={extractValue.asDisplayText(node.props.label)}
86 | disabled={!extractValue.asOptionalBoolean(node.props.enabled ?? true)}
87 | to={extractValue.asOptionalString(node.props.to)}
88 | icon={<Icon name={extractValue.asString(node.props.icon)} className={navLinkStyles.icon} />}
89 | node={node}
90 | initiallyExpanded={extractValue.asOptionalBoolean(node.props.initiallyExpanded)}
91 | noIndicator={extractValue.asOptionalBoolean(node.props.noIndicator)}
92 | renderChild={renderChild}
93 | iconHorizontalExpanded={extractValue.asOptionalString(node.props.iconHorizontalExpanded)}
94 | iconVerticalExpanded={extractValue.asOptionalString(node.props.iconVerticalExpanded)}
95 | iconHorizontalCollapsed={extractValue.asOptionalString(node.props.iconHorizontalCollapsed)}
96 | iconVerticalCollapsed={extractValue.asOptionalString(node.props.iconVerticalCollapsed)}
97 | />
98 | );
99 | },
100 | );
101 |
```
--------------------------------------------------------------------------------
/packages/xmlui-playground/src/playground/PlaygroundNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { useEffect, useId, useMemo, useReducer } from "react";
2 | import {
3 | appDescriptionInitialized,
4 | optionsInitialized,
5 | PlaygroundContext,
6 | playgroundReducer,
7 | } from "../state/store";
8 | import { INITIAL_PLAYGROUND_STATE, preprocessCode } from "../utils/helpers";
9 | import styles from "./PlaygroundNative.module.scss";
10 | import type { ApiInterceptorDefinition, ThemeDefinition, ThemeTone } from "xmlui";
11 | import { ErrorBoundary } from "xmlui";
12 | import { PlaygroundContent } from "./PlaygroundContent";
13 | import { Header } from "./Header";
14 |
15 | type PlaygroundProps = {
16 | name: string;
17 | description?: string;
18 | app: string;
19 | api?: ApiInterceptorDefinition;
20 | themes?: ThemeDefinition[];
21 | defaultTheme?: string;
22 | defaultTone?: ThemeTone;
23 | resources?: any;
24 | components: string[];
25 | previewOnly?: boolean;
26 | height?: number | string;
27 | initialEditorHeight?: string;
28 | swapped?: boolean;
29 | horizontal?: boolean;
30 | allowStandalone?: boolean;
31 | fixedTheme?: boolean;
32 | };
33 |
34 | const EMPTY_ARRAY: any[] = [];
35 | const EMPTY_OBJECT = {};
36 |
37 | export const Playground = ({
38 | name,
39 | description,
40 | app,
41 | themes = EMPTY_ARRAY,
42 | defaultTheme,
43 | defaultTone,
44 | resources = EMPTY_OBJECT,
45 | previewOnly = false,
46 | components = EMPTY_ARRAY,
47 | height,
48 | initialEditorHeight = "50%",
49 | swapped = false,
50 | horizontal = false,
51 | allowStandalone = true,
52 | api,
53 | fixedTheme = false,
54 | }: PlaygroundProps) => {
55 | const id = useId();
56 |
57 | const initializationProps = useMemo(
58 | () => ({
59 | name,
60 | description,
61 | app,
62 | themes,
63 | defaultTheme,
64 | defaultTone,
65 | resources,
66 | previewOnly,
67 | components,
68 | initialEditorHeight,
69 | swapped,
70 | horizontal,
71 | allowStandalone,
72 | api,
73 | fixedTheme,
74 | }),
75 | [
76 | name,
77 | description,
78 | app,
79 | themes,
80 | defaultTheme,
81 | defaultTone,
82 | resources,
83 | previewOnly,
84 | components,
85 | initialEditorHeight,
86 | swapped,
87 | horizontal,
88 | allowStandalone,
89 | api,
90 | fixedTheme,
91 | ],
92 | );
93 |
94 | useEffect(() => {
95 | if (initializationProps.app) {
96 | dispatch(
97 | appDescriptionInitialized({
98 | config: {
99 | name: initializationProps.name,
100 | description: initializationProps.description,
101 | logo: null,
102 | appGlobals: {},
103 | resources: initializationProps.resources,
104 | themes: initializationProps.themes,
105 | defaultTone: initializationProps.defaultTone,
106 | defaultTheme: initializationProps.defaultTheme,
107 | },
108 | components: initializationProps.components.map((c) => preprocessCode(c)),
109 | app: preprocessCode(initializationProps.app),
110 | api: initializationProps.api,
111 | }),
112 | );
113 |
114 | dispatch(
115 | optionsInitialized({
116 | orientation: initializationProps.horizontal ? "horizontal" : "vertical",
117 | swapped: initializationProps.swapped,
118 | activeTheme: initializationProps.defaultTheme,
119 | activeTone: initializationProps.defaultTone,
120 | content: "app",
121 | previewMode: initializationProps.previewOnly,
122 | allowStandalone: initializationProps.allowStandalone,
123 | id: 0,
124 | language: "xmlui",
125 | fixedTheme: initializationProps.fixedTheme,
126 | }),
127 | );
128 | }
129 | }, [initializationProps]);
130 |
131 | const [playgroundState, dispatch] = useReducer(playgroundReducer, INITIAL_PLAYGROUND_STATE);
132 |
133 | const playgroundContextValue = useMemo(() => {
134 | return {
135 | playgroundId: id,
136 | status: playgroundState.status,
137 | options: playgroundState.options,
138 | text: playgroundState.text,
139 | originalAppDescription: playgroundState.originalAppDescription,
140 | appDescription: playgroundState.appDescription,
141 | dispatch,
142 | error: playgroundState.error,
143 | };
144 | }, [
145 | id,
146 | playgroundState.status,
147 | playgroundState.options,
148 | playgroundState.text,
149 | playgroundState.originalAppDescription,
150 | playgroundState.appDescription,
151 | playgroundState.error,
152 | ]);
153 |
154 | return (
155 | <PlaygroundContext.Provider value={playgroundContextValue}>
156 | <ErrorBoundary>
157 | <div className={styles.playground}>
158 | <Header />
159 | <PlaygroundContent height={height} initialPrimarySize={initialEditorHeight} />
160 | </div>
161 | </ErrorBoundary>
162 | </PlaygroundContext.Provider>
163 | );
164 | };
165 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/IFrame/IFrame.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./IFrame.module.scss";
2 |
3 | import { parseScssVar } from "../../components-core/theming/themeVars";
4 | import { createComponentRenderer } from "../../components-core/renderers";
5 | import { createMetadata, d } from "../metadata-helpers";
6 | import { IFrame } from "./IFrameNative";
7 |
8 | const COMP = "IFrame";
9 |
10 | export const IFrameMd = createMetadata({
11 | status: "stable",
12 | description:
13 | "`IFrame` embeds external content from another HTML document into the current page. " +
14 | "It provides security controls through sandbox and allow attributes, and supports " +
15 | "features like fullscreen display and referrer policy configuration.",
16 | props: {
17 | src: d(
18 | "Specifies the URL of the document to embed in the iframe. " +
19 | "Either `src` or `srcdoc` should be specified, but not both.",
20 | ),
21 | srcdoc: d(
22 | "Specifies the HTML content to display in the iframe. " +
23 | "Either `src` or `srcdoc` should be specified, but not both.",
24 | ),
25 | allow: d(
26 | "Specifies the permissions policy for the iframe. " +
27 | "Controls which features (like camera, microphone, geolocation) the embedded content can use.",
28 | ),
29 | name: d(
30 | "Specifies a name for the iframe, which can be used as a target for links and forms.",
31 | ),
32 | referrerPolicy: {
33 | description: "Controls how much referrer information is sent when fetching the iframe content.",
34 | type: "string",
35 | availableValues: [
36 | { value: "no-referrer", description: "Never send referrer information" },
37 | { value: "no-referrer-when-downgrade", description: "Send referrer only for same-security destinations" },
38 | { value: "origin", description: "Send only the origin as referrer" },
39 | { value: "origin-when-cross-origin", description: "Send full URL for same-origin, origin only for cross-origin" },
40 | { value: "same-origin", description: "Send referrer only for same-origin requests" },
41 | { value: "strict-origin", description: "Send origin only for same-security destinations" },
42 | { value: "strict-origin-when-cross-origin", description: "Full URL for same-origin, origin for cross-origin same-security" },
43 | { value: "unsafe-url", description: "Always send full URL as referrer" },
44 | ],
45 | },
46 | sandbox: d(
47 | "Applies extra restrictions to the content in the iframe. " +
48 | "Value is a space-separated list of sandbox flags (e.g., 'allow-scripts allow-same-origin').",
49 | ),
50 | },
51 | events: {
52 | load: {
53 | description: `This event is triggered when the ${COMP} content has finished loading.`,
54 | },
55 | },
56 | apis: {
57 | postMessage: {
58 | description: "This method sends a message to the content window of the iframe.",
59 | signature: "postMessage(message: any, targetOrigin?: string): void",
60 | parameters: {
61 | message: "The message to send to the iframe's content window.",
62 | targetOrigin: "The origin to which the message should be sent. Defaults to '*'.",
63 | },
64 | },
65 | getContentWindow: {
66 | description: "This method returns the content window of the iframe element.",
67 | signature: "getContentWindow(): Window | null",
68 | },
69 | getContentDocument: {
70 | description: "This method returns the content document of the iframe element.",
71 | signature: "getContentDocument(): Document | null",
72 | },
73 | },
74 | themeVars: parseScssVar(styles.themeVars),
75 | defaultThemeVars: {
76 | [`width-${COMP}`]: "100%",
77 | [`height-${COMP}`]: "300px",
78 | [`borderRadius-${COMP}`]: "$borderRadius",
79 | [`border-${COMP}`]: "1px solid $borderColor",
80 | },
81 | });
82 |
83 | export const iframeComponentRenderer = createComponentRenderer(
84 | COMP,
85 | IFrameMd,
86 | ({ node, extractValue, className, extractResourceUrl, lookupEventHandler, registerComponentApi }) => {
87 | return (
88 | <IFrame
89 | src={extractResourceUrl(node.props.src)}
90 | srcdoc={extractValue.asOptionalString(node.props.srcdoc)}
91 | allow={extractValue.asOptionalString(node.props.allow)}
92 | name={extractValue.asOptionalString(node.props.name)}
93 | referrerPolicy={extractValue.asOptionalString(node.props.referrerPolicy) as any}
94 | sandbox={extractValue.asOptionalString(node.props.sandbox)}
95 | className={className}
96 | onLoad={lookupEventHandler("load")}
97 | registerComponentApi={registerComponentApi}
98 | />
99 | );
100 | },
101 | );
102 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/NestedApp/AppWithCodeView.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./AppWithCodeView.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import { AppWithCodeViewNative } from "./AppWithCodeViewNative";
6 | import { defaultProps } from "./defaultProps";
7 | import { createMetadata } from "../metadata-helpers";
8 |
9 | const COMP = "AppWithCodeView";
10 |
11 | export const AppWithCodeViewMd = createMetadata({
12 | status: "stable",
13 | description: `The ${COMP} component displays a combination of markdown content and a nested xmlui app.
14 | It supports both side-by-side and stacked layouts.`,
15 | props: {
16 | markdown: {
17 | description: "The markdown content to be displayed alongside the app",
18 | valueType: "string",
19 | },
20 | splitView: {
21 | description: "Whether to render the markdown and app side by side or stacked vertically",
22 | valueType: "boolean",
23 | },
24 | app: {
25 | description: "The source markup of the app to be nested",
26 | },
27 | api: {
28 | description: "This property defines an optional emulated API to be used with the nested app.",
29 | },
30 | components: {
31 | description:
32 | "This property defines an optional list of components to be used with the nested app.",
33 | defaultValue: defaultProps.components,
34 | },
35 | config: {
36 | description: "You can define the nested app's configuration with this property.",
37 | },
38 | activeTheme: {
39 | description:
40 | "This property defines the active theme for the nested app. " +
41 | "If not set, the default theme is used.",
42 | },
43 | activeTone: {
44 | description:
45 | "This property defines the active tone for the nested app. " +
46 | "If not set, the default tone is used.",
47 | },
48 | title: {
49 | description: "The optional title of the nested app. If not set, no title is displayed.",
50 | },
51 | height: {
52 | description:
53 | "The height of the nested app. If not set, the height is determined automatically.",
54 | },
55 | allowPlaygroundPopup: {
56 | description:
57 | "This property defines whether the nested app can be opened in the xmlui playground.",
58 | valueType: "boolean",
59 | defaultValue: defaultProps.allowPlaygroundPopup,
60 | },
61 | withFrame: {
62 | description: "This property defines whether the nested app should be displayed with a frame.",
63 | valueType: "boolean",
64 | defaultValue: defaultProps.withFrame,
65 | },
66 | allowReset: {
67 | description: "This property defines whether the reset button should be displayed in the header.",
68 | valueType: "boolean",
69 | defaultValue: defaultProps.allowReset,
70 | },
71 | },
72 | themeVars: parseScssVar(styles.themeVars),
73 | defaultThemeVars: {},
74 | });
75 |
76 | export const appWithCodeViewComponentRenderer = createComponentRenderer(
77 | COMP,
78 | AppWithCodeViewMd,
79 | ({ node, extractValue, renderChild }) => {
80 | let renderedChildren = "";
81 |
82 | // 1. Static content prop fallback
83 | if (!renderedChildren) {
84 | renderedChildren = extractValue.asString(node.props.markdown);
85 | }
86 |
87 | // 2. "data" property fallback
88 | if (!renderedChildren && typeof (node.props as any).data === "string") {
89 | renderedChildren = extractValue.asString((node.props as any).data);
90 | }
91 |
92 | // 3. Children fallback
93 | if (!renderedChildren) {
94 | (node.children ?? []).forEach((child) => {
95 | const renderedChild = renderChild(child);
96 | console.log("renderedChild", renderedChild);
97 | if (typeof renderedChild === "string") {
98 | renderedChildren += renderedChild;
99 | }
100 | });
101 | }
102 |
103 | return (
104 | <AppWithCodeViewNative
105 | markdown={renderedChildren}
106 | splitView={extractValue.asOptionalBoolean(node.props?.splitView)}
107 | app={node.props?.app}
108 | api={extractValue(node.props?.api)}
109 | components={extractValue(node.props?.components)}
110 | config={extractValue(node.props?.config)}
111 | activeTheme={extractValue(node.props?.activeTheme)}
112 | activeTone={extractValue(node.props?.activeTone)}
113 | title={extractValue(node.props?.title)}
114 | height={extractValue(node.props?.height)}
115 | allowPlaygroundPopup={extractValue.asOptionalBoolean(node.props?.allowPlaygroundPopup)}
116 | withFrame={extractValue.asOptionalBoolean(node.props?.withFrame)}
117 | allowReset={extractValue.asOptionalBoolean(node.props?.allowReset)}
118 | />
119 | );
120 | },
121 | );
122 |
```