This is page 35 of 141. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ └── config.json
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── layout-changes.md
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ └── lorem-ipsum.png
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ └── welcome-to-the-xmlui-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── HelloMd.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── Animation.tsx
│ │ │ ├── AnimationNative.tsx
│ │ │ ├── FadeAnimation.tsx
│ │ │ ├── FadeInAnimation.tsx
│ │ │ ├── FadeOutAnimation.tsx
│ │ │ ├── index.tsx
│ │ │ ├── ScaleAnimation.tsx
│ │ │ └── SlideInAnimation.tsx
│ │ └── tsconfig.json
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ ├── tsconfig.json
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── HelloWorld.module.scss
│ │ │ ├── HelloWorld.tsx
│ │ │ ├── HelloWorldNative.tsx
│ │ │ └── index.tsx
│ │ └── tsconfig.json
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── IPhoneFrame.module.scss
│ │ │ ├── IPhoneFrame.tsx
│ │ │ ├── MacOSAppFrame.module.scss
│ │ │ ├── MacOSAppFrame.tsx
│ │ │ ├── WindowsAppFrame.module.scss
│ │ │ └── WindowsAppFrame.tsx
│ │ └── tsconfig.json
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── LazyPdfNative.tsx
│ │ │ ├── Pdf.module.scss
│ │ │ └── Pdf.tsx
│ │ └── tsconfig.json
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── hooks
│ │ │ │ ├── usePlayground.ts
│ │ │ │ └── useToast.ts
│ │ │ ├── index.tsx
│ │ │ ├── playground
│ │ │ │ ├── Box.module.scss
│ │ │ │ ├── Box.tsx
│ │ │ │ ├── CodeSelector.tsx
│ │ │ │ ├── ConfirmationDialog.module.scss
│ │ │ │ ├── ConfirmationDialog.tsx
│ │ │ │ ├── Editor.tsx
│ │ │ │ ├── Header.module.scss
│ │ │ │ ├── Header.tsx
│ │ │ │ ├── Playground.tsx
│ │ │ │ ├── PlaygroundContent.module.scss
│ │ │ │ ├── PlaygroundContent.tsx
│ │ │ │ ├── PlaygroundNative.module.scss
│ │ │ │ ├── PlaygroundNative.tsx
│ │ │ │ ├── Preview.module.scss
│ │ │ │ ├── Preview.tsx
│ │ │ │ ├── Select.module.scss
│ │ │ │ ├── StandalonePlayground.tsx
│ │ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ │ ├── ThemeSwitcher.module.scss
│ │ │ │ ├── ThemeSwitcher.tsx
│ │ │ │ ├── ToneSwitcher.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── providers
│ │ │ │ ├── Toast.module.scss
│ │ │ │ └── ToastProvider.tsx
│ │ │ ├── state
│ │ │ │ └── store.ts
│ │ │ ├── themes
│ │ │ │ └── theme.ts
│ │ │ └── utils
│ │ │ └── helpers.ts
│ │ └── tsconfig.json
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Search.module.scss
│ │ │ └── Search.tsx
│ │ └── tsconfig.json
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Spreadsheet.tsx
│ │ │ └── SpreadsheetNative.tsx
│ │ └── tsconfig.json
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ ├── src
│ │ ├── Carousel
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── FancyButton
│ │ │ ├── FancyButton.module.scss
│ │ │ ├── FancyButton.tsx
│ │ │ └── FancyButton.xmlui
│ │ ├── Hello
│ │ │ ├── Hello.tsx
│ │ │ ├── Hello.xmlui
│ │ │ └── Hello.xmlui.xs
│ │ ├── HeroSection
│ │ │ ├── HeroSection.module.scss
│ │ │ ├── HeroSection.tsx
│ │ │ └── HeroSectionNative.tsx
│ │ ├── index.tsx
│ │ ├── ScrollToTop
│ │ │ ├── ScrollToTop.module.scss
│ │ │ ├── ScrollToTop.tsx
│ │ │ └── ScrollToTopNative.tsx
│ │ └── vite-env.d.ts
│ └── tsconfig.json
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── playwright.config.ts
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── get-langserver-metadata.mjs
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ ├── LabelListNative.module.scss
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.mjs
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.bin.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/components/SpaceFiller/SpaceFiller.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { test, expect } from "../../testing/fixtures";
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test.describe("Basic Functionality", () => {
test("renders without props", async ({ initTestBed, page }) => {
await initTestBed(`<SpaceFiller/>`);
const spaceFiller = page.getByTestId("test-id-component");
// SpaceFiller exists in DOM but may not be "visible" due to zero dimensions
await expect(spaceFiller).toBeAttached();
await expect(spaceFiller).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller).toHaveCSS("place-self", "stretch");
});
test("renders in HStack and pushes subsequent elements to the end", async ({ initTestBed, page }) => {
await initTestBed(`
<HStack>
<Stack width="36px" height="36px" backgroundColor="red" testId="red-box"/>
<SpaceFiller testId="spacer"/>
<Stack width="36px" height="36px" backgroundColor="blue" testId="blue-box"/>
</HStack>
`);
const redBox = page.getByTestId("red-box");
const blueBox = page.getByTestId("blue-box");
const spaceFiller = page.getByTestId("spacer");
await expect(spaceFiller).toBeAttached();
await expect(redBox).toBeVisible();
await expect(blueBox).toBeVisible();
// Verify the spaceFiller has flex properties
await expect(spaceFiller).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller).toHaveCSS("place-self", "stretch");
});
test("renders in VStack and pushes subsequent elements to the end", async ({ initTestBed, page }) => {
await initTestBed(`
<VStack>
<Stack width="36px" height="36px" backgroundColor="red" testId="red-box"/>
<SpaceFiller testId="spacer"/>
<Stack width="36px" height="36px" backgroundColor="blue" testId="blue-box"/>
</VStack>
`);
const redBox = page.getByTestId("red-box");
const blueBox = page.getByTestId("blue-box");
const spaceFiller = page.getByTestId("spacer");
await expect(spaceFiller).toBeAttached();
await expect(redBox).toBeVisible();
await expect(blueBox).toBeVisible();
// Verify the spaceFiller has flex properties
await expect(spaceFiller).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller).toHaveCSS("place-self", "stretch");
});
test("acts as line break in FlowLayout", async ({ initTestBed, page }) => {
await initTestBed(`
<FlowLayout>
<Stack width="20%" height="36px" backgroundColor="red" testId="red-box"/>
<SpaceFiller/>
<Stack width="20%" height="36px" backgroundColor="green" testId="green-box"/>
<Stack width="20%" height="36px" backgroundColor="blue" testId="blue-box"/>
</FlowLayout>
`);
const redBox = page.getByTestId("red-box");
const greenBox = page.getByTestId("green-box");
const blueBox = page.getByTestId("blue-box");
await expect(redBox).toBeVisible();
await expect(greenBox).toBeVisible();
await expect(blueBox).toBeVisible();
// Get bounding rectangles to verify line break behavior
const redRect = await redBox.boundingBox();
const greenRect = await greenBox.boundingBox();
const blueRect = await blueBox.boundingBox();
// Red box should be on the first line
// Green and blue boxes should be on the second line (lower y position)
// The SpaceFiller should cause a line break between red and green/blue
expect(redRect).not.toBeNull();
expect(greenRect).not.toBeNull();
expect(blueRect).not.toBeNull();
// Green and blue boxes should be on a different (lower) line than red
expect(greenRect!.y).toBeGreaterThan(redRect!.y);
expect(blueRect!.y).toBeGreaterThan(redRect!.y);
// Green and blue should be on the same line (similar y coordinates)
expect(Math.abs(greenRect!.y - blueRect!.y)).toBeLessThan(5);
});
test("multiple SpaceFillers distribute space evenly", async ({ initTestBed, page }) => {
await initTestBed(`
<HStack>
<Stack width="36px" height="36px" backgroundColor="red" testId="red-box"/>
<SpaceFiller testId="spacer1"/>
<Stack width="36px" height="36px" backgroundColor="green" testId="green-box"/>
<SpaceFiller testId="spacer2"/>
<Stack width="36px" height="36px" backgroundColor="blue" testId="blue-box"/>
</HStack>
`);
const redBox = page.getByTestId("red-box");
const greenBox = page.getByTestId("green-box");
const blueBox = page.getByTestId("blue-box");
const spaceFiller1 = page.getByTestId("spacer1");
const spaceFiller2 = page.getByTestId("spacer2");
await expect(spaceFiller1).toBeVisible();
await expect(spaceFiller2).toBeVisible();
await expect(redBox).toBeVisible();
await expect(greenBox).toBeVisible();
await expect(blueBox).toBeVisible();
// Both SpaceFillers should have the same flex properties
await expect(spaceFiller1).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller2).toHaveCSS("flex", "1 1 0px");
});
test("ignores layout properties", async ({ initTestBed, page }) => {
await initTestBed(`
<HStack>
<SpaceFiller testId="spacer" width="100px" height="100px" backgroundColor="red" padding="20px"/>
</HStack>
`);
const spaceFiller = page.getByTestId("spacer");
await expect(spaceFiller).toBeAttached();
// SpaceFiller should not apply any styling properties
await expect(spaceFiller).not.toHaveCSS("width", "100px");
await expect(spaceFiller).not.toHaveCSS("height", "100px");
await expect(spaceFiller).not.toHaveCSS("background-color", "rgb(255, 0, 0)");
await expect(spaceFiller).not.toHaveCSS("padding", "20px");
// But it should still have its core flex properties
await expect(spaceFiller).toHaveCSS("flex", "1 1 0px");
});
});
// =============================================================================
// OTHER EDGE CASE TESTS
// =============================================================================
test.describe("Other Edge Cases", () => {
test("renders correctly when nested in multiple containers", async ({ initTestBed, page }) => {
await initTestBed(`
<VStack>
<HStack>
<Stack width="36px" height="36px" backgroundColor="red"/>
<SpaceFiller testId="spacer1"/>
<Stack width="36px" height="36px" backgroundColor="blue"/>
</HStack>
<SpaceFiller testId="spacer2"/>
<HStack>
<Stack width="36px" height="36px" backgroundColor="green"/>
</HStack>
</VStack>
`);
const spaceFiller1 = page.getByTestId("spacer1");
const spaceFiller2 = page.getByTestId("spacer2");
await expect(spaceFiller1).toBeAttached();
await expect(spaceFiller2).toBeAttached();
// Both should have the correct CSS properties
await expect(spaceFiller1).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller2).toHaveCSS("flex", "1 1 0px");
});
test("works in containers with no other children", async ({ initTestBed, page }) => {
await initTestBed(`
<HStack>
<SpaceFiller testId="spacer"/>
</HStack>
`);
const spaceFiller = page.getByTestId("spacer");
await expect(spaceFiller).toBeAttached();
await expect(spaceFiller).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller).toHaveCSS("place-self", "stretch");
});
test("handles container with different alignment settings", async ({ initTestBed, page }) => {
await initTestBed(`
<HStack justifyContent="center" alignItems="flex-end">
<Stack width="36px" height="36px" backgroundColor="red"/>
<SpaceFiller testId="spacer"/>
<Stack width="36px" height="36px" backgroundColor="blue"/>
</HStack>
`);
const spaceFiller = page.getByTestId("spacer");
await expect(spaceFiller).toBeAttached();
// SpaceFiller should maintain its flex properties regardless of parent alignment
await expect(spaceFiller).toHaveCSS("flex", "1 1 0px");
await expect(spaceFiller).toHaveCSS("place-self", "stretch");
});
});
```
--------------------------------------------------------------------------------
/xmlui/src/components/TimeInput/utils.ts:
--------------------------------------------------------------------------------
```typescript
// Merged utility functions for TimeInput component
import getUserLocale from 'get-user-locale';
// ============================================================================
// Types (from types.ts)
// ============================================================================
export type Range<T> = [T, T];
export type AmPmType = 'am' | 'pm';
export type ClassName = string | null | undefined | (string | null | undefined)[];
export type Detail = 'hour' | 'minute' | 'second';
export type LooseValuePiece = string | Date | null;
export type LooseValue = LooseValuePiece | Range<LooseValuePiece>;
export type Value = string | null;
// ============================================================================
// Date Formatter utilities (from dateFormatter.ts)
// ============================================================================
const formatterCache = new Map();
export function getFormatter(
options: Intl.DateTimeFormatOptions,
): (locale: string | undefined, date: Date) => string {
return function formatter(locale: string | undefined, date: Date): string {
const localeWithDefault = locale || getUserLocale();
if (!formatterCache.has(localeWithDefault)) {
formatterCache.set(localeWithDefault, new Map());
}
const formatterCacheLocale = formatterCache.get(localeWithDefault);
if (!formatterCacheLocale.has(options)) {
formatterCacheLocale.set(
options,
new Intl.DateTimeFormat(localeWithDefault || undefined, options).format,
);
}
return formatterCacheLocale.get(options)(date);
};
}
const numberFormatterCache = new Map();
export function getNumberFormatter(
options: Intl.NumberFormatOptions,
): (locale: string | undefined, number: number) => string {
return (locale: string | undefined, number: number): string => {
const localeWithDefault = locale || getUserLocale();
if (!numberFormatterCache.has(localeWithDefault)) {
numberFormatterCache.set(localeWithDefault, new Map());
}
const numberFormatterCacheLocale = numberFormatterCache.get(localeWithDefault);
if (!numberFormatterCacheLocale.has(options)) {
numberFormatterCacheLocale.set(
options,
new Intl.NumberFormat(localeWithDefault || undefined, options).format,
);
}
return numberFormatterCacheLocale.get(options)(number);
};
}
// ============================================================================
// Date utilities (from dateUtils.ts)
// ============================================================================
export function getHours(dateOrTimeString: string | Date | null | undefined): number {
if (!dateOrTimeString) return 0;
if (dateOrTimeString instanceof Date) {
return dateOrTimeString.getHours();
}
// Handle time string format like "14:30:45" or "14:30"
const timeString = String(dateOrTimeString);
const timeParts = timeString.split(':');
if (timeParts.length >= 1) {
const hours = parseInt(timeParts[0], 10);
return isNaN(hours) ? 0 : hours;
}
return 0;
}
export function getMinutes(dateOrTimeString: string | Date | null | undefined): number {
if (!dateOrTimeString) return 0;
if (dateOrTimeString instanceof Date) {
return dateOrTimeString.getMinutes();
}
// Handle time string format like "14:30:45" or "14:30"
const timeString = String(dateOrTimeString);
const timeParts = timeString.split(':');
if (timeParts.length >= 2) {
const minutes = parseInt(timeParts[1], 10);
return isNaN(minutes) ? 0 : minutes;
}
return 0;
}
export function getSeconds(dateOrTimeString: string | Date | null | undefined): number {
if (!dateOrTimeString) return 0;
if (dateOrTimeString instanceof Date) {
return dateOrTimeString.getSeconds();
}
// Handle time string format like "14:30:45"
const timeString = String(dateOrTimeString);
const timeParts = timeString.split(':');
if (timeParts.length >= 3) {
const seconds = parseInt(timeParts[2], 10);
return isNaN(seconds) ? 0 : seconds;
}
return 0;
}
export function getHoursMinutes(dateOrTimeString: string | Date | null | undefined): string {
if (!dateOrTimeString) return '';
if (dateOrTimeString instanceof Date) {
const hours = dateOrTimeString.getHours().toString().padStart(2, '0');
const minutes = dateOrTimeString.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
// Handle time string - return first two components
const timeString = String(dateOrTimeString);
const timeParts = timeString.split(':');
if (timeParts.length >= 2) {
const hours = parseInt(timeParts[0], 10);
const minutes = parseInt(timeParts[1], 10);
if (!isNaN(hours) && !isNaN(minutes)) {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
}
}
return timeString;
}
export function getHoursMinutesSeconds(dateOrTimeString: string | Date | null | undefined): string {
if (!dateOrTimeString) return '';
if (dateOrTimeString instanceof Date) {
const hours = dateOrTimeString.getHours().toString().padStart(2, '0');
const minutes = dateOrTimeString.getMinutes().toString().padStart(2, '0');
const seconds = dateOrTimeString.getSeconds().toString().padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
// Handle time string - ensure it has three components
const timeString = String(dateOrTimeString);
const timeParts = timeString.split(':');
if (timeParts.length >= 3) {
const hours = parseInt(timeParts[0], 10);
const minutes = parseInt(timeParts[1], 10);
const seconds = parseInt(timeParts[2], 10);
if (!isNaN(hours) && !isNaN(minutes) && !isNaN(seconds)) {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}
} else if (timeParts.length === 2) {
// Add seconds if missing
const hours = parseInt(timeParts[0], 10);
const minutes = parseInt(timeParts[1], 10);
if (!isNaN(hours) && !isNaN(minutes)) {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`;
}
}
return timeString;
}
// ============================================================================
// Date conversion utilities (from dates.ts)
// ============================================================================
export function convert12to24(hour12: string | number, amPm: AmPmType): number {
let hour24 = Number(hour12);
if (amPm === 'am' && hour24 === 12) {
hour24 = 0;
} else if (amPm === 'pm' && hour24 < 12) {
hour24 += 12;
}
return hour24;
}
export function convert24to12(hour24: string | number): [number, AmPmType] {
const hour12 = Number(hour24) % 12 || 12;
return [hour12, Number(hour24) < 12 ? 'am' : 'pm'];
}
// ============================================================================
// General utilities (from utils.ts)
// ============================================================================
const nines = ['9', '٩'];
const ninesRegExp = new RegExp(`[${nines.join('')}]`);
const amPmFormatter = getFormatter({ hour: 'numeric' });
export function getAmPmLabels(locale: string | undefined): [string, string] {
const amString = amPmFormatter(locale, new Date(2017, 0, 1, 9));
const pmString = amPmFormatter(locale, new Date(2017, 0, 1, 21));
const [am1, am2] = amString.split(ninesRegExp) as [string, string];
const [pm1, pm2] = pmString.split(ninesRegExp) as [string, string];
if (pm2 !== undefined) {
// If pm2 is undefined, nine was not found in pmString - this locale is not using 12-hour time
if (am1 !== pm1) {
return [am1, pm1].map((el) => el.trim()) as [string, string];
}
if (am2 !== pm2) {
return [am2, pm2].map((el) => el.trim()) as [string, string];
}
}
// Fallback
return ['AM', 'PM'];
}
function isValidNumber(num: unknown): num is number {
return num !== null && num !== false && !Number.isNaN(Number(num));
}
export function safeMin(...args: unknown[]): number {
return Math.min(...args.filter(isValidNumber));
}
export function safeMax(...args: unknown[]): number {
return Math.max(...args.filter(isValidNumber));
}
```
--------------------------------------------------------------------------------
/packages/xmlui-website-blocks/src/Carousel/CarouselNative.tsx:
--------------------------------------------------------------------------------
```typescript
import * as React from "react";
import type { CSSProperties, ForwardedRef } from "react";
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react";
import Autoplay from "embla-carousel-autoplay";
import { composeRefs } from "@radix-ui/react-compose-refs";
import classnames from "classnames";
import styles from "./Carousel.module.scss";
import { RegisterComponentApiFn, Icon } from "xmlui";
const noop = () => {};
export type OrientationOptions = "horizontal" | "vertical";
import { CarouselContext, useCarouselContextValue } from "./CarouselContext";
type CarouselApi = UseEmblaCarouselType[1];
export type CarouselProps = {
style?: CSSProperties;
className?: string;
orientation?: OrientationOptions;
indicators?: boolean;
controls?: boolean;
children: React.ReactNode;
autoplay?: boolean;
loop?: boolean;
startIndex?: number;
prevIcon?: string;
nextIcon?: string;
onDisplayDidChange?: (activeSlide: number) => void;
registerComponentApi?: RegisterComponentApiFn;
transitionDuration?: number;
autoplayInterval?: number;
stopAutoplayOnInteraction?: boolean;
};
export const defaultProps: Pick<
CarouselProps,
| "orientation"
| "indicators"
| "autoplay"
| "controls"
| "loop"
| "startIndex"
| "transitionDuration"
| "autoplayInterval"
| "stopAutoplayOnInteraction"
> = {
orientation: "horizontal",
indicators: true,
autoplay: false,
controls: true,
loop: false,
startIndex: 0,
transitionDuration: 25,
autoplayInterval: 5000,
stopAutoplayOnInteraction: true,
};
export const CarouselComponent = forwardRef(function CarouselComponent(
{
orientation = defaultProps.orientation,
children,
style,
className,
indicators = defaultProps.indicators,
onDisplayDidChange = noop,
autoplay = defaultProps.autoplay,
controls = defaultProps.controls,
loop = defaultProps.loop,
startIndex = defaultProps.startIndex,
prevIcon,
nextIcon,
transitionDuration = defaultProps.transitionDuration,
autoplayInterval = defaultProps.autoplayInterval,
stopAutoplayOnInteraction = defaultProps.stopAutoplayOnInteraction,
registerComponentApi,
...rest
}: CarouselProps,
forwardedRef: ForwardedRef<HTMLDivElement>,
) {
const referenceElement = useRef<HTMLDivElement>(null);
const [activeSlide, setActiveSlide] = useState(0);
const [plugins, setPlugins] = useState<any[]>([]);
const [isPlaying, setIsPlaying] = useState(false);
const { carouselContextValue, carouselItems } = useCarouselContextValue(indicators ?? defaultProps.indicators as boolean);
const ref = forwardedRef ? composeRefs(referenceElement, forwardedRef) : referenceElement;
const [carouselRef, api] = useEmblaCarousel(
{
axis: orientation === "horizontal" ? "x" : "y",
loop,
startIndex,
duration: transitionDuration,
},
plugins,
);
const prevIconName = prevIcon || orientation === "horizontal" ? "arrowleft" : "arrowup";
const nextIconName = nextIcon || orientation === "horizontal" ? "arrowright" : "arrowdown";
useEffect(() => {
if (autoplay) {
setPlugins([
Autoplay({
delay: autoplayInterval,
stopOnInteraction: stopAutoplayOnInteraction,
}),
]);
}
}, [autoplayInterval, autoplay, stopAutoplayOnInteraction]);
const toggleAutoplay = useCallback(() => {
const autoplay = api?.plugins()?.autoplay;
if (!autoplay) return;
const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
playOrStop();
}, [api]);
useEffect(() => {
const autoplay = api?.plugins()?.autoplay;
if (!autoplay) return;
setIsPlaying(autoplay.isPlaying());
api
.on("autoplay:play", () => setIsPlaying(true))
.on("autoplay:stop", () => setIsPlaying(false))
.on("reInit", () => setIsPlaying(autoplay.isPlaying()));
}, [api]);
const scrollTo = useCallback(
(index: number) => {
api?.scrollTo(index);
},
[api],
);
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false);
const onSelect = React.useCallback(
(api: CarouselApi) => {
if (!api) {
return;
}
const activeIndex = api.selectedScrollSnap();
onDisplayDidChange(activeIndex);
setActiveSlide(activeIndex);
setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
},
[onDisplayDidChange],
);
const scrollPrev = useCallback(() => {
if (api) {
api?.scrollPrev();
}
}, [api]);
const scrollNext = useCallback(() => {
api?.scrollNext();
}, [api]);
const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (orientation === "horizontal") {
if (event.key === "ArrowLeft") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowRight") {
event.preventDefault();
scrollNext();
}
} else {
if (event.key === "ArrowUp") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowDown") {
event.preventDefault();
scrollNext();
}
}
},
[orientation, scrollPrev, scrollNext],
);
useEffect(() => {
registerComponentApi?.({
scrollTo,
scrollPrev,
scrollNext,
canScrollPrev: () => canScrollPrev,
canScrollNext: () => canScrollNext,
});
}, [registerComponentApi, scrollTo, scrollPrev, scrollNext, canScrollPrev, canScrollNext]);
React.useEffect(() => {
if (!api) {
return;
}
onSelect(api);
api.on("init", onSelect);
api.on("reInit", onSelect);
api.on("select", onSelect);
return () => {
api?.off("select", onSelect);
};
}, [api, onSelect]);
return (
<CarouselContext.Provider value={carouselContextValue}>
<div
{...rest}
style={style}
ref={ref}
className={classnames(styles.carousel, className)}
role="region"
tabIndex={-1}
onKeyDown={handleKeyDown}
aria-roledescription="carousel"
>
<div ref={carouselRef} className={styles.carouselContentWrapper}>
<div
className={classnames(styles.carouselContent, {
[styles.horizontal]: orientation === "horizontal",
[styles.vertical]: orientation === "vertical",
})}
>
{children}
</div>
</div>
{controls && (
<div className={styles.controls}>
{autoplay && (
<button
className={styles.controlButton}
onClick={toggleAutoplay}
aria-label={isPlaying ? "Pause Autoplay" : "Start Autoplay"}
>
{isPlaying ? <Icon name={"pause"} /> : <Icon name={"play"} />}
</button>
)}
<button
className={styles.controlButton}
disabled={!canScrollPrev}
onClick={scrollPrev}
aria-label="Previous Slide"
>
<Icon name={prevIconName} />
</button>
<button
className={styles.controlButton}
onClick={scrollNext}
disabled={!canScrollNext}
aria-label="Next Slide"
>
<Icon name={nextIconName} />
</button>
</div>
)}
{indicators && (
<div className={styles.indicators} role="tablist" aria-label="Select slide">
{carouselItems.map((_, index) => (
<button
key={index}
type="button"
role="tab"
aria-label={`Go to slide ${index + 1}`}
aria-controls="carousel"
tabIndex={index === activeSlide ? 0 : -1}
onClick={() => scrollTo(index)}
className={classnames(styles.indicator, {
[styles.active]: index === activeSlide,
})}
aria-current={index === activeSlide}
/>
))}
</div>
)}
</div>
</CarouselContext.Provider>
);
});
```
--------------------------------------------------------------------------------
/xmlui/src/syntax/monaco/xmluiscript.monacoLanguage.ts:
--------------------------------------------------------------------------------
```typescript
/* Based on https://github.com/microsoft/monaco-editor/blob/main/src/basic-languages/javascript/javascript.ts */
export const XmluiScripGrammar: { id: string; config: any; language: any } = {
id: "xmluiscript",
config: {
wordPattern:
/(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: "//",
blockComment: ["/*", "*/"],
},
brackets: [
["{", "}"],
["[", "]"],
["(", ")"],
],
onEnterRules: [
{
// e.g. /** | */
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/,
action: {
// numeric repr of indentAction: languages.IndentAction.IndentOutdent, but without the "monaco-core" package
indentAction: 2,
appendText: " * ",
},
},
{
// e.g. /** ...|
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
action: {
// numeric repr of indentAction: languages.IndentAction.None, but without the "monaco-core" package
indentAction: 0,
appendText: " * ",
},
},
{
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: {
// numeric repr of indentAction: languages.IndentAction.None, but without the "monaco-core" package
indentAction: 0,
appendText: "* ",
},
},
{
// e.g. */|
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
action: {
// numeric repr of indentAction: languages.IndentAction.None, but without the "monaco-core" package
indentAction: 0,
removeText: 1,
},
},
],
autoClosingPairs: [
{ open: "{", close: "}" },
{ open: "[", close: "]" },
{ open: "(", close: ")" },
{ open: '"', close: '"', notIn: ["string"] },
{ open: "'", close: "'", notIn: ["string", "comment"] },
{ open: "`", close: "`", notIn: ["string", "comment"] },
{ open: "/**", close: " */", notIn: ["string"] },
],
folding: {
markers: {
start: new RegExp("^\\s*//\\s*#?region\\b"),
end: new RegExp("^\\s*//\\s*#?endregion\\b"),
},
},
},
language: {
// Set defaultToken to invalid to see what you do not tokenize yet
defaultToken: "invalid",
tokenPostfix: ".xs",
typeKeywords: [],
keywords: [
"break",
"case",
"catch",
"class",
"continue",
"const",
"constructor",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"false",
"finally",
"for",
"from",
"function",
"get",
"if",
"import",
"in",
"instanceof",
"let",
"new",
"null",
"return",
"set",
"static",
"super",
"switch",
"symbol",
"this",
"throw",
"true",
"try",
"typeof",
"undefined",
"var",
"void",
"while",
"with",
"yield",
"async",
"await",
"of",
],
operators: [
"<=",
">=",
"==",
"!=",
"===",
"!==",
"=>",
"+",
"-",
"**",
"*",
"/",
"%",
"++",
"--",
"<<",
"</",
">>",
">>>",
"&",
"|",
"^",
"!",
"~",
"&&",
"||",
"??",
"?",
":",
"=",
"+=",
"-=",
"*=",
"**=",
"/=",
"%=",
"<<=",
">>=",
">>>=",
"&=",
"|=",
"^=",
"@",
],
// we include these common regular expressions
symbols: /[=><!~?:&|+\-*\/\^%]+/,
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
digits: /\d+(_+\d+)*/,
octaldigits: /[0-7]+(_+[0-7]+)*/,
binarydigits: /[0-1]+(_+[0-1]+)*/,
hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
regexpctl: /[(){}\[\]\$\^|\-*+?\.]/,
regexpesc: /\\(?:[bBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/,
// The main tokenizer for our languages
tokenizer: {
root: [[/[{}]/, "delimiter.bracket"], { include: "common" }],
common: [
// identifiers and keywords
[
/#?[a-z_$][\w$]*/,
{
cases: {
"@keywords": "keyword",
"@default": "identifier",
},
},
],
[/[A-Z][\w\$]*/, "type.identifier"], // to show class names nicely
// [/[A-Z][\w\$]*/, 'identifier'],
// whitespace
{ include: "@whitespace" },
// regular expression: ensure it is terminated before beginning (otherwise it is an opeator)
[
/\/(?=([^\\\/]|\\.)+\/([dgimsuy]*)(\s*)(\.|;|,|\)|\]|\}|$))/,
{ token: "regexp", bracket: "@open", next: "@regexp" },
],
// delimiters and operators
[/[()\[\]]/, "@brackets"],
[/[<>](?!@symbols)/, "@brackets"],
[/!(?=([^=]|$))/, "delimiter"],
[
/@symbols/,
{
cases: {
"@operators": "delimiter",
"@default": "",
},
},
],
// numbers
[/(@digits)[eE]([\-+]?(@digits))?/, "number.float"],
[/(@digits)\.(@digits)([eE][\-+]?(@digits))?/, "number.float"],
[/0[xX](@hexdigits)n?/, "number.hex"],
[/0[oO]?(@octaldigits)n?/, "number.octal"],
[/0[bB](@binarydigits)n?/, "number.binary"],
[/(@digits)n?/, "number"],
// delimiter: after number because of .\d floats
[/[;,.]/, "delimiter"],
// strings
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
[/'([^'\\]|\\.)*$/, "string.invalid"], // non-teminated string
[/"/, "string", "@string_double"],
[/'/, "string", "@string_single"],
[/`/, "string", "@string_backtick"],
],
whitespace: [
[/[ \t\r\n]+/, ""],
[/\/\*\*(?!\/)/, "comment.doc", "@jsdoc"],
[/\/\*/, "comment", "@comment"],
[/\/\/.*$/, "comment"],
],
comment: [
[/[^\/*]+/, "comment"],
[/\*\//, "comment", "@pop"],
[/[\/*]/, "comment"],
],
jsdoc: [
[/[^\/*]+/, "comment.doc"],
[/\*\//, "comment.doc", "@pop"],
[/[\/*]/, "comment.doc"],
],
// We match regular expression quite precisely
regexp: [
[
/(\{)(\d+(?:,\d*)?)(\})/,
["regexp.escape.control", "regexp.escape.control", "regexp.escape.control"],
],
[
/(\[)(\^?)(?=(?:[^\]\\\/]|\\.)+)/,
["regexp.escape.control", { token: "regexp.escape.control", next: "@regexrange" }],
],
[/(\()(\?:|\?=|\?!)/, ["regexp.escape.control", "regexp.escape.control"]],
[/[()]/, "regexp.escape.control"],
[/@regexpctl/, "regexp.escape.control"],
[/[^\\\/]/, "regexp"],
[/@regexpesc/, "regexp.escape"],
[/\\\./, "regexp.invalid"],
[
/(\/)([dgimsuy]*)/,
[{ token: "regexp", bracket: "@close", next: "@pop" }, "keyword.other"],
],
],
regexrange: [
[/-/, "regexp.escape.control"],
[/\^/, "regexp.invalid"],
[/@regexpesc/, "regexp.escape"],
[/[^\]]/, "regexp"],
[
/\]/,
{
token: "regexp.escape.control",
next: "@pop",
bracket: "@close",
},
],
],
string_double: [
[/[^\\"]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/"/, "string", "@pop"],
],
string_single: [
[/[^\\']+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/'/, "string", "@pop"],
],
string_backtick: [
[/\$\{/, { token: "delimiter.bracket", next: "@bracketCounting" }],
[/[^\\`$]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/`/, "string", "@pop"],
],
bracketCounting: [
[/\{/, "delimiter.bracket", "@bracketCounting"],
[/\}/, "delimiter.bracket", "@pop"],
{ include: "common" },
],
},
},
};
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/behaviors/CoreBehaviors.tsx:
--------------------------------------------------------------------------------
```typescript
import { cloneElement, type ReactElement } from "react";
import {
Animation,
parseAnimation,
parseAnimationOptions,
} from "../../components/Animation/AnimationNative";
import { ItemWithLabel } from "../../components/FormItem/ItemWithLabel";
import { parseTooltipOptions, Tooltip } from "../../components/Tooltip/TooltipNative";
import { useStyles } from "../theming/StyleContext";
import { THEME_VAR_PREFIX, toCssVar } from "../theming/layout-resolver";
import { parseLayoutProperty, toCssPropertyName } from "../theming/parse-layout-props";
import { buttonVariantValues } from "../../components/abstractions";
import type { Behavior } from "./Behavior";
import { badgeVariantValues } from "../../components/Badge/BadgeNative";
/**
* Behavior for applying tooltips to components.
*/
export const tooltipBehavior: Behavior = {
name: "tooltip",
canAttach: (node) => {
const tooltipText = node.props?.tooltip;
const tooltipMarkdown = node.props?.tooltipMarkdown;
return !!tooltipText || !!tooltipMarkdown;
},
attach: (context, node, metadata) => {
const { extractValue } = context;
const tooltipText = extractValue(context.node.props?.tooltip, true);
const tooltipMarkdown = extractValue(context.node.props?.tooltipMarkdown, true);
const tooltipOptions = extractValue(context.node.props?.tooltipOptions, true);
const parsedOptions = parseTooltipOptions(tooltipOptions);
return (
<Tooltip text={tooltipText} markdown={tooltipMarkdown} {...parsedOptions}>
{node}
</Tooltip>
);
},
};
/**
* Behavior for applying animations to components.
*/
export const animationBehavior: Behavior = {
name: "animation",
canAttach: (node) => {
return !!node.props?.animation;
},
attach: (context, node, metadata) => {
const { extractValue } = context;
const animation = extractValue(context.node.props?.animation, true);
const animationOptions = extractValue(context.node.props?.animationOptions, true);
const parsedOptions = parseAnimationOptions(animationOptions);
return (
<Animation animation={parseAnimation(animation)} {...parsedOptions}>
{context.node.type === "ModalDialog"
? cloneElement(node as ReactElement, {
externalAnimation: true,
})
: node}
</Animation>
);
},
};
/**
* Behavior for applying a label to form components using ItemWithLabel.
*/
export const labelBehavior: Behavior = {
name: "label",
canAttach: (node, metadata) => {
/**
* This behavior can be attached if the component has a 'label' prop
* and is not a component that handles its own labeling.
*/
if (metadata?.props?.label) {
return false;
} else if (!node.props?.label) {
return false;
}
return true;
},
attach: (context, node, metadata) => {
const { extractValue, node: componentNode, className } = context;
const label = extractValue.asOptionalString(componentNode.props.label);
const labelPosition = extractValue(componentNode.props.labelPosition);
const labelWidth = extractValue.asOptionalString(componentNode.props.labelWidth);
const labelBreak = extractValue.asOptionalBoolean(componentNode.props.labelBreak);
const required = extractValue.asOptionalBoolean(componentNode.props.required);
const enabled = extractValue.asOptionalBoolean(componentNode.props.enabled, true);
const shrinkToLabel = extractValue.asOptionalBoolean(componentNode.props.shrinkToLabel);
const style = extractValue(componentNode.props.style);
const readOnly = extractValue.asOptionalBoolean(componentNode.props.readOnly);
return (
<ItemWithLabel
labelPosition={labelPosition as any}
label={label}
labelWidth={labelWidth}
labelBreak={labelBreak}
required={required}
enabled={enabled}
style={style}
className={className}
shrinkToLabel={shrinkToLabel}
labelStyle={{ pointerEvents: readOnly ? "none" : undefined }}
isInputTemplateUsed={!!componentNode.props?.inputTemplate}
>
{node}
</ItemWithLabel>
);
},
};
/**
* Behavior for applying custom variant styling to components with non-predefined variant values.
* For Button components, this only applies if the variant is not "solid", "outlined", or "ghost".
* For other components, it applies to any component with a variant prop.
*
* This behavior clones the rendered node and adds the generated CSS class directly,
* without wrapping in an additional component.
*/
export const variantBehavior: Behavior = {
name: "variant",
canAttach: (node) => {
const variant = node.props?.variant;
// Must have a variant prop
if (!variant) {
return false;
}
// Special handling for Button component
if (node.type === "Button") {
// For Button, only attach if variant is NOT one of the predefined values
const variantStr = typeof variant === "string" ? variant : String(variant);
return !buttonVariantValues.includes(variantStr as any);
}
// Special handling for Badge component
if (node.type === "Badge") {
// For Badge, only attach if variant is NOT one of the predefined values
const variantStr = typeof variant === "string" ? variant : String(variant);
return !badgeVariantValues.includes(variantStr as any);
}
return true;
},
attach: (context, node, metadata) => {
const { extractValue, node: componentNode } = context;
const variant = extractValue(componentNode.props?.variant, true);
const componentType = componentNode.type;
if (!variant || typeof variant !== "string") {
return node;
}
// Get theme variables from metadata
const themeVars = metadata?.themeVars;
// Validate that themeVars is a record object
if (!themeVars || typeof themeVars !== 'object' || Array.isArray(themeVars)) {
return node;
}
const themeVarKeys = Object.keys(themeVars);
if (themeVarKeys.length === 0) {
return node;
}
// Generate the variant style specification from metadata themeVars
const subject = `-${componentType}-${variant}`;
const variantSpec: Record<string, any> = {
"&": {},
"&:hover": {},
"&:active": {},
"&:disabled": {},
};
// Process each theme variable from metadata
for (const themeVar of themeVarKeys) {
const parsed = parseLayoutProperty(themeVar, true);
// Skip if parsing failed or returned an error string
if (typeof parsed === "string") {
continue;
}
const { property, states } = parsed;
// Convert camelCase property to CSS property name
const cssProperty = toCssPropertyName(property);
if (!cssProperty) {
continue;
}
// Determine which selector to use based on states
let selector = "&";
let stateSuffix = "";
if (states && states.length > 0) {
const state = states[0]; // Use first state
stateSuffix = `--${states.join("--")}`;
if (state === "hover") {
selector = "&:hover";
} else if (state === "active") {
selector = "&:active";
} else if (state === "disabled") {
selector = "&:disabled";
} else if (state === "focus") {
selector = "&:focus";
}
}
// Generate CSS variable reference for this theme variable with variant suffix
//const cssVarValue = toCssVar(`$${property}${subject}${stateSuffix}`);
const cssVarValue = `var(--${THEME_VAR_PREFIX}-${property}${subject}${stateSuffix}, ` +
`var(--${THEME_VAR_PREFIX}-${property}-${componentType}))`;
// Add to appropriate selector in variant spec
if (!variantSpec[selector]) {
variantSpec[selector] = {};
}
variantSpec[selector][cssProperty] = cssVarValue;
}
// Generate the CSS class using useStyles hook
// eslint-disable-next-line react-hooks/rules-of-hooks
const customVariantClassName = useStyles(variantSpec);
// Clone the node and add the custom variant className
const existingClassName = (node as ReactElement).props.className || "";
const newClassName = `${existingClassName} ${customVariantClassName || ""}`.trim();
return cloneElement(node as ReactElement, {
className: newClassName,
});
},
};
```
--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/lexer-literals.test.ts:
--------------------------------------------------------------------------------
```typescript
import { describe, expect, it } from "vitest";
import { Lexer } from "../../../src/parsers/scripting/Lexer";
import { TokenType } from "../../../src/parsers/scripting/TokenType";
import { InputStream } from "../../../src/parsers/common/InputStream";
describe("Lexer - literal", () => {
const literalCases = [
{ src: "0", exp: TokenType.DecimalLiteral },
{ src: "1", exp: TokenType.DecimalLiteral },
{ src: "2", exp: TokenType.DecimalLiteral },
{ src: "3", exp: TokenType.DecimalLiteral },
{ src: "4", exp: TokenType.DecimalLiteral },
{ src: "5", exp: TokenType.DecimalLiteral },
{ src: "6", exp: TokenType.DecimalLiteral },
{ src: "7", exp: TokenType.DecimalLiteral },
{ src: "8", exp: TokenType.DecimalLiteral },
{ src: "9", exp: TokenType.DecimalLiteral },
{ src: "0123", exp: TokenType.DecimalLiteral },
{ src: "0_123", exp: TokenType.DecimalLiteral },
{ src: "123_456_678_912_345", exp: TokenType.DecimalLiteral },
{ src: "0x0", exp: TokenType.HexadecimalLiteral },
{ src: "0x_0", exp: TokenType.HexadecimalLiteral },
{ src: "0x0_0", exp: TokenType.HexadecimalLiteral },
{ src: "0x1_0", exp: TokenType.HexadecimalLiteral },
{ src: "0x12ac34", exp: TokenType.HexadecimalLiteral },
{ src: "0x12_ac34", exp: TokenType.HexadecimalLiteral },
{ src: "0b0", exp: TokenType.BinaryLiteral },
{ src: "0b_0", exp: TokenType.BinaryLiteral },
{ src: "0b0_0", exp: TokenType.BinaryLiteral },
{ src: "0b1_0", exp: TokenType.BinaryLiteral },
{ src: "0b011100110", exp: TokenType.BinaryLiteral },
{ src: "0b0111_0011_0", exp: TokenType.BinaryLiteral },
{ src: "0.0", exp: TokenType.RealLiteral },
{ src: "1.0", exp: TokenType.RealLiteral },
{ src: "2.1", exp: TokenType.RealLiteral },
{ src: "3.12", exp: TokenType.RealLiteral },
{ src: "4.123", exp: TokenType.RealLiteral },
{ src: "5.1234", exp: TokenType.RealLiteral },
{ src: "6.12345", exp: TokenType.RealLiteral },
{ src: "7.123_456", exp: TokenType.RealLiteral },
{ src: "8.12", exp: TokenType.RealLiteral },
{ src: "9.12", exp: TokenType.RealLiteral },
{ src: "01.0", exp: TokenType.RealLiteral },
{ src: "1_.0", exp: TokenType.RealLiteral },
{ src: "543_210.012_345_6", exp: TokenType.RealLiteral },
{ src: "0e0", exp: TokenType.RealLiteral },
{ src: "1e0", exp: TokenType.RealLiteral },
{ src: "2e0", exp: TokenType.RealLiteral },
{ src: "3e0", exp: TokenType.RealLiteral },
{ src: "4e0", exp: TokenType.RealLiteral },
{ src: "5e0", exp: TokenType.RealLiteral },
{ src: "6e0", exp: TokenType.RealLiteral },
{ src: "7e0", exp: TokenType.RealLiteral },
{ src: "8e0", exp: TokenType.RealLiteral },
{ src: "9e0", exp: TokenType.RealLiteral },
{ src: "123e0", exp: TokenType.RealLiteral },
{ src: "23_4e0", exp: TokenType.RealLiteral },
{ src: "123e13", exp: TokenType.RealLiteral },
{ src: "123e+13", exp: TokenType.RealLiteral },
{ src: "123e-13", exp: TokenType.RealLiteral },
{ src: "123.456e13", exp: TokenType.RealLiteral },
{ src: "123.45_6e+13", exp: TokenType.RealLiteral },
{ src: "123.4_56e-13", exp: TokenType.RealLiteral },
{ src: ".0", exp: TokenType.RealLiteral },
{ src: ".12_34", exp: TokenType.RealLiteral },
{ src: ".456e13", exp: TokenType.RealLiteral },
{ src: ".45_6e+13", exp: TokenType.RealLiteral },
{ src: ".4_56e-13", exp: TokenType.RealLiteral },
{ src: "true", exp: TokenType.True },
{ src: "false", exp: TokenType.False }
];
literalCases.forEach(c => {
it(`Token ${c.src} #1`, () => {
const source = c.src;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(source);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(source.length);
});
it(`Token ${c.src} #2`, () => {
const source = ` \t \r ${c.src}`;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(5);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(5);
expect(next.endColumn).equal(source.length);
});
it(`Token ${c.src} #3`, () => {
const source = ` /* c */ ${c.src}`;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(9);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(9);
expect(next.endColumn).equal(source.length);
});
it(`Token ${c.src} #4`, () => {
const source = `${c.src} \t \r `;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(c.src.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(c.src.length);
});
it(`Token ${c.src} #5`, () => {
const source = `${c.src} // c`;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
const trail1 = wLexer.get(true);
const trail2 = wLexer.get(true);
const trail3 = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(c.src.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(c.src.length);
expect(trail1.type).equal(TokenType.Ws);
expect(trail2.type).equal(TokenType.EolComment);
expect(trail3.type).equal(TokenType.Eof);
});
});
const errorCases = [
"0.",
"0e",
"0E",
"1.",
"1e",
"1E",
"0e+",
"0E+",
"1e-",
"1E+",
"0x",
"0b",
"0x_",
"0b_",
'"',
'"\r"',
'"\n"',
'"\u0085"',
'"\u2028"',
'"\u2029"'
];
errorCases.forEach(c => {
it(`Token error ${c} #1`, () => {
const wLexer = new Lexer(new InputStream(c));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(TokenType.Unknown);
});
});
const stringCases: string[] = [
'""',
'"abc"',
'"abc,def,1234:#"',
'"\\bdef"',
'"\\fdef"',
'"\\ndef"',
'"\\rdef"',
'"\\tdef"',
'"\\vdef"',
'"\\0def"',
'"\\\'def"',
'"\\"def"',
'"\\`def"',
'"\\\\def"',
'"\\qdef"',
'"\\x40def"',
'"abd\\bdef"',
'"abd\\fdef"',
'"abd\\ndef"',
'"abd\\rdef"',
'"abd\\tdef"',
'"abd\\vdef"',
'"abd\\0def"',
'"abd\\\'def"',
'"abd\\"def"',
'"abd\\\\def"',
'"abd\\qdef"',
'"abd\\x40def"',
'"abd\\b"',
'"abd\\f"',
'"abd\\n"',
'"abd\\r"',
'"abd\\t"',
'"abd\\v"',
'"abd\\S"',
'"abd\\0"',
'"abd\\\'"',
'"abd\\""',
'"abd\\\\"',
'"abd\\q"',
'"abd\\x40"',
'"abd\\u1234"',
"'abc'",
"'abc,def,1234:#'",
"'\\bdef'"
];
stringCases.forEach((c, idx) => {
it(`String #${idx + 1}: ${c}`, () => {
// --- Arrange
const wLexer = new Lexer(new InputStream(c));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(TokenType.StringLiteral);
expect(next.text).equal(c);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(c.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(c.length);
});
});
});
```
--------------------------------------------------------------------------------
/xmlui/src/components/DateInput/DateInput.module.scss:
--------------------------------------------------------------------------------
```scss
@use "../../components-core/theming/themes" as t;
// --- This code snippet is required to collect the theme variables used in this module
$themeVars: ();
@function createThemeVar($componentVariable) {
$themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
@return t.getThemeVar($themeVars, $componentVariable);
}
$componentName: "DateInput";
$themeVars: t.composePaddingVars($themeVars, $componentName);
// Compose standard Input theme variables automatically
$themeVars: t.composePaddingVars($themeVars, $componentName);
$themeVars: t.composeBorderVars($themeVars, $componentName);
// --- DateInput specific theme variables
$color-divider-DateInput: createThemeVar("color-divider-#{$componentName}");
$spacing-divider-DateInput: createThemeVar("spacing-divider-#{$componentName}");
$width-input-DateInput: createThemeVar("width-input-#{$componentName}");
$minWidth-input-DateInput: createThemeVar("minWidth-input-#{$componentName}");
$padding-input-DateInput: createThemeVar("padding-input-#{$componentName}");
$textAlign-input-DateInput: createThemeVar("textAlign-input-#{$componentName}");
$fontSize-input-DateInput: createThemeVar("fontSize-input-#{$componentName}");
$borderRadius-input-DateInput: createThemeVar("borderRadius-input-#{$componentName}");
$backgroundColor-input-DateInput-invalid: createThemeVar("backgroundColor-input-#{$componentName}-invalid");
$padding-button-DateInput: createThemeVar("padding-button-#{$componentName}");
$borderRadius-button-DateInput: createThemeVar("borderRadius-button-#{$componentName}");
$hoverColor-button-DateInput: createThemeVar("hoverColor-button-#{$componentName}");
$disabledColor-button-DateInput: createThemeVar("disabledColor-button-#{$componentName}");
$outlineColor-button-DateInput--focused: createThemeVar("outlineColor-button-#{$componentName}--focused");
$outlineWidth-button-DateInput--focused: createThemeVar("outlineWidth-button-#{$componentName}--focused");
$outlineOffset-button-DateInput--focused: createThemeVar("outlineOffset-button-#{$componentName}--focused");
// New theme variables for better theming
$opacity-DateInput--disabled: createThemeVar("opacity-#{$componentName}--disabled");
$backgroundColor-DateInput--hover: createThemeVar("backgroundColor-#{$componentName}--hover");
$margin-input-DateInput: createThemeVar("margin-input-#{$componentName}");
$transition-background-DateInput: createThemeVar("transition-background-#{$componentName}");
// --- CSS properties of a particular DateInput variant
@mixin variant($variantName) {
border-radius: createThemeVar("Input:borderRadius-#{$componentName}-#{$variantName}");
border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}");
border-width: createThemeVar("Input:borderWidth-#{$componentName}-#{$variantName}");
border-style: createThemeVar("Input:borderStyle-#{$componentName}-#{$variantName}");
background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}");
box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}");
color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}");
&:hover {
border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--hover");
background-color: createThemeVar(
"Input:backgroundColor-#{$componentName}-#{$variantName}--hover"
);
box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--hover");
color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}--hover");
}
&:focus-within {
border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--focus");
background-color: createThemeVar(
"Input:backgroundColor-#{$componentName}-#{$variantName}--focus"
);
box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--focus");
color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}--focus");
}
&:has(.dateInputWrapper input:focus-visible) {
outline-width: createThemeVar("Input:outlineWidth-#{$componentName}-#{$variantName}--focus");
outline-color: createThemeVar("Input:outlineColor-#{$componentName}-#{$variantName}--focus");
outline-style: createThemeVar("Input:outlineStyle-#{$componentName}-#{$variantName}--focus");
outline-offset: createThemeVar("Input:outlineOffset-#{$componentName}-#{$variantName}--focus");
}
.adornment {
color: createThemeVar("Input:color-adornment-#{$componentName}-#{$variantName}");
}
}
@layer components {
.dateInputWrapper {
display: flex;
align-items: center;
width: fit-content;
border-style: solid;
border-width: 1px;
transition: $transition-background-DateInput;
overflow: hidden;
gap: createThemeVar("Input:gap-adornment-#{$componentName}");
@include t.paddingVars($themeVars, $componentName);
&.disabled {
opacity: $opacity-DateInput--disabled;
pointer-events: none;
}
&.readOnly {
.dateInputWrapper input {
cursor: default;
}
}
@include variant("default");
&.error {
@include variant("error");
}
&.warning {
@include variant("warning");
}
&.valid {
@include variant("success");
}
&:has(input:is(:disabled)) {
cursor: not-allowed;
background-color: createThemeVar("Input:backgroundColor-#{$componentName}--disabled");
color: createThemeVar("Input:textColor-#{$componentName}--disabled");
border-color: createThemeVar("Input:borderColor-#{$componentName}--disabled");
}
button {
background: transparent;
border: none;
cursor: pointer;
color: inherit;
padding: $padding-button-DateInput;
border-radius: createThemeVar("Input:borderRadius-#{$componentName}-default");
&:hover {
background-color: $backgroundColor-DateInput--hover;
}
&:focus {
outline: 2px solid createThemeVar("Input:outlineColor-#{$componentName}-default--focus");
outline-offset: 2px;
}
&:disabled {
cursor: not-allowed;
opacity: $opacity-DateInput--disabled;
}
}
}
// DateInput internal components styling
.wrapper {
display: flex;
flex-grow: 0; // Don't grow to eliminate unused space
flex-shrink: 0; // Don't shrink to maintain consistency
}
.inputGroup {
display: flex;
align-items: center;
flex-grow: 0; // Don't grow to eliminate unused space
flex-shrink: 0; // Don't shrink to maintain consistency
min-width: 0; // Allow shrinking if needed
gap: 0; // Remove gap since dividers handle spacing
box-sizing: border-box;
}
.divider {
padding: 0 0.25rem; // Increase horizontal spacing around dividers
white-space: pre;
display: inline-block;
font: inherit;
color: $color-divider-DateInput;
}
.input {
min-width: $minWidth-input-DateInput;
height: 100%;
position: relative;
padding: $padding-input-DateInput;
border: 0;
background: none;
color: currentColor;
font: inherit;
font-size: $fontSize-input-DateInput;
box-sizing: content-box;
border-radius: $borderRadius-input-DateInput;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
// Consistent width for all date inputs (following TimeInput strategy)
&.day,
&.month,
&.year {
width: 2.5ch; // Uniform width following TimeInput pattern
min-width: 2.5ch;
max-width: 2.5ch;
text-align: center;
padding: $padding-input-DateInput !important;
box-sizing: border-box !important;
}
&.year {
width: 4.5ch;
min-width: 4.5ch;
max-width: 4.5ch;
}
// Hide spin buttons for number inputs
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
margin: 0 !important;
display: none !important;
}
// Hide spin buttons for Firefox
&[type="number"] {
-moz-appearance: textfield !important;
appearance: textfield !important;
}
&:invalid {
background-color: $backgroundColor-input-DateInput-invalid;
}
&.invalid {
background-color: $backgroundColor-input-DateInput-invalid;
}
}
}
// Export theme variables for consumption by the component definition
:export {
themeVars: t.json-stringify($themeVars);
}
```
--------------------------------------------------------------------------------
/xmlui/src/components/Carousel/CarouselNative.tsx:
--------------------------------------------------------------------------------
```typescript
import * as React from "react";
import type { CSSProperties, ForwardedRef } from "react";
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react";
import Autoplay from "embla-carousel-autoplay";
import { composeRefs } from "@radix-ui/react-compose-refs";
import classnames from "classnames";
import styles from "./Carousel.module.scss";
import type { RegisterComponentApiFn } from "../../abstractions/RendererDefs";
import { noop } from "../../components-core/constants";
import { CarouselContext, useCarouselContextValue } from "./CarouselContext";
import Icon from "../Icon/IconNative";
import type { OrientationOptions } from "../abstractions";
type CarouselApi = UseEmblaCarouselType[1];
export type CarouselProps = {
style?: CSSProperties;
className?: string;
orientation?: OrientationOptions;
indicators?: boolean;
controls?: boolean;
children: React.ReactNode;
autoplay?: boolean;
loop?: boolean;
startIndex?: number;
prevIcon?: string;
nextIcon?: string;
onDisplayDidChange?: (activeSlide: number) => void;
registerComponentApi?: RegisterComponentApiFn;
transitionDuration?: number;
autoplayInterval?: number;
stopAutoplayOnInteraction?: boolean;
};
export const defaultProps: Pick<
CarouselProps,
| "orientation"
| "indicators"
| "autoplay"
| "controls"
| "loop"
| "startIndex"
| "transitionDuration"
| "autoplayInterval"
| "stopAutoplayOnInteraction"
> = {
orientation: "horizontal",
indicators: true,
autoplay: false,
controls: true,
loop: false,
startIndex: 0,
transitionDuration: 25,
autoplayInterval: 5000,
stopAutoplayOnInteraction: true,
};
export const CarouselComponent = forwardRef(function CarouselComponent(
{
orientation = defaultProps.orientation,
children,
style,
className,
indicators = defaultProps.indicators,
onDisplayDidChange = noop,
autoplay = defaultProps.autoplay,
controls = defaultProps.controls,
loop = defaultProps.loop,
startIndex = defaultProps.startIndex,
prevIcon,
nextIcon,
transitionDuration = defaultProps.transitionDuration,
autoplayInterval = defaultProps.autoplayInterval,
stopAutoplayOnInteraction = defaultProps.stopAutoplayOnInteraction,
registerComponentApi,
...rest
}: CarouselProps,
forwardedRef: ForwardedRef<HTMLDivElement>,
) {
const referenceElement = useRef(null);
const [activeSlide, setActiveSlide] = useState(0);
const [plugins, setPlugins] = useState([]);
const [isPlaying, setIsPlaying] = useState(false);
const { carouselContextValue, carouselItems } = useCarouselContextValue(indicators);
const ref = forwardedRef ? composeRefs(referenceElement, forwardedRef) : referenceElement;
const [carouselRef, api] = useEmblaCarousel(
{
axis: orientation === "horizontal" ? "x" : "y",
loop,
startIndex,
duration: transitionDuration,
},
plugins,
);
const prevIconName = prevIcon || (orientation === "horizontal" ? "arrowleft" : "arrowup");
const nextIconName = nextIcon || (orientation === "horizontal" ? "arrowright" : "arrowdown");
useEffect(() => {
if (autoplay) {
setPlugins([
Autoplay({
delay: autoplayInterval,
stopOnInteraction: stopAutoplayOnInteraction,
}),
]);
}
}, [autoplayInterval, autoplay, stopAutoplayOnInteraction]);
const toggleAutoplay = useCallback(() => {
const autoplay = api?.plugins()?.autoplay;
if (!autoplay) return;
const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
playOrStop();
}, [api]);
useEffect(() => {
const autoplay = api?.plugins()?.autoplay;
if (!autoplay) return;
setIsPlaying(autoplay.isPlaying());
api
.on("autoplay:play", () => setIsPlaying(true))
.on("autoplay:stop", () => setIsPlaying(false))
.on("reInit", () => setIsPlaying(autoplay.isPlaying()));
}, [api]);
const scrollTo = useCallback(
(index: number) => {
api?.scrollTo(index);
},
[api],
);
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false);
const onSelect = React.useCallback(
(api: CarouselApi) => {
if (!api) {
return;
}
const activeIndex = api.selectedScrollSnap();
onDisplayDidChange(activeIndex);
setActiveSlide(activeIndex);
setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
},
[onDisplayDidChange],
);
const scrollPrev = useCallback(() => {
if (api) {
api?.scrollPrev();
}
}, [api]);
const scrollNext = useCallback(() => {
api?.scrollNext();
}, [api]);
const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (orientation === "horizontal") {
if (event.key === "ArrowLeft") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowRight") {
event.preventDefault();
scrollNext();
}
} else {
if (event.key === "ArrowUp") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowDown") {
event.preventDefault();
scrollNext();
}
}
},
[orientation, scrollPrev, scrollNext],
);
useEffect(() => {
registerComponentApi?.({
scrollTo,
scrollPrev,
scrollNext,
canScrollPrev: () => canScrollPrev,
canScrollNext: () => canScrollNext,
});
}, [registerComponentApi, scrollTo, scrollPrev, scrollNext, canScrollPrev, canScrollNext]);
React.useEffect(() => {
if (!api) {
return;
}
onSelect(api);
api.on("init", onSelect);
api.on("reInit", onSelect);
api.on("select", onSelect);
return () => {
api?.off("select", onSelect);
};
}, [api, onSelect]);
useEffect(() => {
if (referenceElement?.current) {
referenceElement.current.addEventListener("keydown", handleKeyDown);
}
}, [ref, handleKeyDown]);
return (
<CarouselContext.Provider value={carouselContextValue}>
<div
{...rest}
style={style}
ref={ref}
className={classnames(styles.carousel, className)}
role="region"
tabIndex={-1}
aria-roledescription="carousel"
>
<div ref={carouselRef} className={styles.carouselContentWrapper}>
<div
className={classnames(styles.carouselContent, {
[styles.horizontal]: orientation === "horizontal",
[styles.vertical]: orientation === "vertical",
})}
>
{children}
</div>
</div>
{controls && (
<div className={styles.controls}>
{autoplay && (
<button
className={styles.controlButton}
onClick={toggleAutoplay}
aria-label={isPlaying ? "Pause Autoplay" : "Start Autoplay"}
>
{isPlaying ? <Icon name={"pause"} /> : <Icon name={"play"} />}
</button>
)}
<button
className={styles.controlButton}
disabled={!canScrollPrev}
onClick={scrollPrev}
aria-label="Previous Slide"
>
<Icon name={prevIconName} />
</button>
<button
className={styles.controlButton}
onClick={scrollNext}
disabled={!canScrollNext}
aria-label="Next Slide"
>
<Icon name={nextIconName} />
</button>
</div>
)}
{indicators && (
<div className={styles.indicators} role="tablist" aria-label="Select slide">
{carouselItems.map((_, index) => (
<button
key={index}
type="button"
role="tab"
aria-label={`Go to slide ${index + 1}`}
aria-controls="carousel"
tabIndex={index === activeSlide ? 0 : -1}
onClick={() => scrollTo(index)}
className={classnames(styles.indicator, {
[styles.active]: index === activeSlide,
})}
aria-current={index === activeSlide}
/>
))}
</div>
)}
</div>
</CarouselContext.Provider>
);
});
```
--------------------------------------------------------------------------------
/docs/src/config.ts:
--------------------------------------------------------------------------------
```typescript
import type { StandaloneAppDescription } from "xmlui";
import { createHighlighterCoreSync, type DecorationItem } from "shiki";
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
// @ts-ignore
import js from "@shikijs/langs/javascript";
// @ts-ignore
import scss from "@shikijs/langs/scss";
// @ts-ignore
import css from "@shikijs/langs/css";
// @ts-ignore
import json from "@shikijs/langs/json";
// @ts-ignore
import html from "@shikijs/langs/html";
import { xmluiGrammar, xmluiThemeLight, xmluiThemeDark } from "xmlui/syntax/textmate";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import stripMarkdown from "strip-markdown";
export function markdownToPlainText(markdown: string): string {
const processor = unified()
.use(remarkParse)
.use(stripMarkdown, { keep: ["code"] })
.use(remarkStringify);
const file = processor.processSync(markdown);
let cleanedText = String(file);
// NEW: Remove admonition tags like [!WARNING] or \[!NOTE]
// This looks for an optional backslash, then "[!", any word, and "]"
cleanedText = cleanedText.replace(/\\?\[!\w+\]\s*/g, "");
// 1. Remove the anchor-like tags, e.g., "\[#button]"
cleanedText = cleanedText.replace(/\s*\\\[#.*?\]/g, "");
// 2. Process each line individually to reformat tables
const lines = cleanedText.split("\n");
const reformattedLines = lines.map((line) => {
const trimmedLine = line.trim();
// Check if the line is a table separator like "| --- | --- |"
if (trimmedLine.match(/^\|(?:\s*---\s*\|)+$/)) {
return null; // Mark this line for removal
}
// Check if the line is a table row (starts and ends with '|')
if (trimmedLine.startsWith("|") && trimmedLine.endsWith("|")) {
// Remove leading/trailing pipes, then split into cells
return trimmedLine
.slice(1, -1)
.split("|")
.map((cell) => cell.trim()) // Trim whitespace from each cell
.join(" - "); // Join cells with a more readable separator
}
// filter code block header/footers
if (trimmedLine.startsWith("```") || trimmedLine.startsWith("---")) {
return null;
}
// If it's not a table line, return it as is
return line;
});
// 3. Filter out the removed separator lines and join back into a string
cleanedText = reformattedLines.filter((line) => line !== null).join("\n");
// 4. Consolidate multiple consecutive newlines into a maximum of two
cleanedText = cleanedText.replace(/(\r\n|\n){3,}/g, "\n\n");
// 5. Trim any leading or trailing whitespace from the final result
return cleanedText.trim();
}
// @ts-ignore
const contentRuntime: Record<string, any> = import.meta.glob(`/content/**/*.{md,mdx}`, {
eager: true,
query: "?raw",
});
// @ts-ignore
const metaJsons: Record<string, MetaJson> = import.meta.glob(`/content/**/_meta.json`, {
eager: true,
});
const content: Record<string, any> = {};
const plainTextContent: Record<string, string> = {};
const navPanelContent: any[] = [];
Object.keys(contentRuntime).map((filePath) => {
const urlFragment = filePath.substring("/content/".length).replace(".mdx", "").replace(".md", "");
content[omitIndexFromPath(urlFragment)] = contentRuntime[filePath].default;
plainTextContent[omitIndexFromPath(urlFragment)] = markdownToPlainText(
contentRuntime[filePath].default,
);
navPanelContent.push(urlFragment);
});
const pagesRuntime: Record<string, any> = import.meta.glob(`/public/pages/**/*.md`, {
eager: true,
query: "?raw",
});
const prefetchedContent: Record<string, any> = {};
Object.keys(pagesRuntime).map((filePath) => {
const urlFragment = filePath.substring("/public".length);
prefetchedContent[urlFragment] = pagesRuntime[filePath].default;
});
const shikiHighlighter = createHighlighterCoreSync({
// @ts-ignore
langs: [js, json, html, xmluiGrammar, css, scss],
// @ts-ignore
themes: [xmluiThemeLight, xmluiThemeDark],
engine: createJavaScriptRegexEngine(),
});
function highlight(
code: string,
lang: string,
meta?: Record<string, any>,
themeTone: "dark" | "light" = "light",
) {
if (!code) return "";
if (!themeTone) themeTone = "light";
if (!["dark", "light"].includes(themeTone)) {
themeTone = "light";
}
const highlightedRows: DecorationItem[] =
meta?.highlightRows?.map((row: DecorationItem) => {
return {
start: row.start,
end: row.end,
properties: row.properties,
};
}) ?? [];
const highlightedSubstrings: DecorationItem[] =
[...(meta?.highlightSubstringsEmphasized ?? []), ...(meta?.highlightSubstrings ?? [])]?.map(
(str: DecorationItem) => {
return {
start: str.start,
end: str.end,
properties: str.properties,
};
},
) ?? [];
const opts = {
lang,
theme: `xmlui-${themeTone}`,
decorations: [...highlightedRows, ...highlightedSubstrings],
};
return shikiHighlighter.codeToHtml(code, opts);
}
type TreeNode = {
name: string;
path: string;
title?: string;
type?: string;
children?: TreeNode[];
};
type MetaJson = Record<string, any>;
function omitIndexFromPath(path: string) {
return path.endsWith("index") ? path.substring(0, path.length - "index".length) : path;
}
// generated with chatgpt
function buildTreeFromPathsAndMeta(
paths: string[],
metaByFolder: Record<string, MetaJson>,
): TreeNode[] {
const root: TreeNode[] = [];
paths.forEach((path) => {
const parts = path.split("/");
let currentPath = "/content";
let currentLevel: TreeNode[] | undefined = root;
parts.forEach((part, index) => {
let existingNode = currentLevel?.find((node) => node.name === part);
if (!existingNode) {
// --- Look up title/type in meta
const meta = metaByFolder[currentPath + "/_meta.json"];
let title, type;
if (meta?.default?.[part]) {
const metaEntry = meta.default[part];
if (typeof metaEntry === "string") {
title = metaEntry;
} else if (typeof metaEntry === "object") {
title = metaEntry.title;
type = metaEntry.type;
}
}
existingNode = {
name: part,
title: title || part,
path: omitIndexFromPath(path),
...(type && { type }),
};
if (existingNode) {
currentLevel?.push(existingNode);
}
}
if (index < parts.length - 1) {
if (existingNode && !existingNode?.children) {
existingNode.children = [];
}
currentLevel = existingNode?.children;
}
currentPath += `/${part}`;
});
});
// Function to sort children based on _meta.json order
function sortChildren(level: TreeNode[], path: string) {
const meta = metaByFolder[path + "/_meta.json"];
if (!meta?.default) return;
const order = Object.keys(meta.default);
order.forEach((orderKey) => {
if (!level.find((item) => item.name === orderKey)) {
level.push({
name: orderKey,
...meta.default[orderKey],
});
}
});
level.sort((a, b) => {
const aIndex = order.indexOf(a.name);
const bIndex = order.indexOf(b.name);
if (aIndex === -1 && bIndex === -1) return 0;
if (aIndex === -1) return 1;
if (bIndex === -1) return -1;
return aIndex - bIndex;
});
level.forEach((node) => {
if (node.children) {
sortChildren(node.children, `${path}/${node.name}`);
}
});
}
sortChildren(root, "/content");
return root;
}
const groupedNavPanelContent = buildTreeFromPathsAndMeta(navPanelContent, metaJsons);
const App: StandaloneAppDescription = {
name: "XMLUI Docs",
defaultTheme: "docs-theme",
resources: {
logo: "/resources/logo.svg",
"logo-dark": "/resources/logo-dark.svg",
favicon: "/resources/favicon.ico",
"icon.github": "/resources/github.svg",
"font.Inter":
"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
},
appGlobals: {
useHashBasedRouting: false,
showHeadingAnchors: true,
searchIndexEnabled: true,
navPanelContent: groupedNavPanelContent,
content,
plainTextContent,
codeHighlighter: {
availableLangs: shikiHighlighter.getLoadedLanguages(),
highlight,
},
prefetchedContent,
lintSeverity: "skip", // Turn off xmlui linting
popOutUrl: "https://playground.xmlui.org/#/playground",
},
};
export default App;
```
--------------------------------------------------------------------------------
/xmlui/src/components/Charts/PieChart/PieChartNative.tsx:
--------------------------------------------------------------------------------
```typescript
import {
Pie,
PieChart as RPieChart,
Sector,
ResponsiveContainer,
LabelList,
Tooltip,
Legend as RLegend,
} from "recharts";
import type { PieSectorDataItem } from "recharts/types/polar/Pie";
import styles from "./PieChartNative.module.scss";
import type { CSSProperties, ReactNode } from "react";
import { useMemo, useRef, useEffect, useState } from "react";
import type { LabelPosition } from "recharts/types/component/Label";
import ChartProvider, { useChartContextValue } from "../utils/ChartProvider";
import { TooltipContent } from "../Tooltip/TooltipContent";
import { useTheme } from "../../../components-core/theming/ThemeContext";
import classnames from "classnames";
export type PieChartProps = {
data: any[];
dataKey: string;
nameKey: string;
style?: CSSProperties;
className?: string;
showLabel?: boolean;
showLabelList?: boolean;
labelListPosition?: LabelPosition;
innerRadius?: number;
children?: ReactNode;
showLegend?: boolean;
outerRadius?: string | number;
};
export const defaultProps: Pick<
PieChartProps,
"showLabel" | "showLabelList" | "showLegend" | "labelListPosition" | "innerRadius"
> = {
showLabel: true,
showLabelList: false,
showLegend: false,
labelListPosition: "inside",
innerRadius: 0,
};
// Custom label renderer that draws connectors and places text outside the pie
const renderCustomizedLabel = (props: any) => {
const RADIAN = Math.PI / 180;
const { cx, cy, midAngle, outerRadius, fill, payload, percent, value, name, index } = props;
// Calculate positions for the label connector lines
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? "start" : "end";
return (
<g>
{/* Connector line from pie to label */}
<path d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`} stroke={fill} fill="none" />
{/* Circle at the bend point */}
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
{/* Name label */}
<text
x={ex + (cos >= 0 ? 1 : -1) * 12}
y={ey}
textAnchor={textAnchor}
className={styles.pieLabel}
fill="currentColor"
fontSize={12}
>
{props.name}
</text>
</g>
);
};
// Enhanced active shape renderer
const renderActiveShape = (props: PieSectorDataItem) => {
const { cx, cy, innerRadius, outerRadius, startAngle, endAngle, fill } = props;
return (
<g>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={(outerRadius as number) + 10}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={(outerRadius as number) + 6}
outerRadius={(outerRadius as number) + 10}
fill={fill}
/>
</g>
);
};
export function PieChart({
data,
dataKey,
nameKey,
style,
className,
showLabel = defaultProps.showLabel,
showLabelList = defaultProps.showLabelList,
labelListPosition = defaultProps.labelListPosition,
innerRadius = defaultProps.innerRadius,
children,
outerRadius, // no default; we'll compute when undefined or "auto"
showLegend = defaultProps.showLegend,
}: PieChartProps) {
const { getThemeVar } = useTheme();
const colorValues = useMemo(() => {
return [
getThemeVar("color-primary-500"),
getThemeVar("color-primary-300"),
getThemeVar("color-success-500"),
getThemeVar("color-success-300"),
getThemeVar("color-warn-500"),
getThemeVar("color-warn-300"),
getThemeVar("color-danger-500"),
getThemeVar("color-danger-300"),
getThemeVar("color-info-500"),
getThemeVar("color-info-300"),
getThemeVar("color-secondary-500"),
getThemeVar("color-secondary-300"),
getThemeVar("color-primary-600"),
getThemeVar("color-primary-400"),
getThemeVar("color-success-600"),
getThemeVar("color-success-400"),
getThemeVar("color-warn-600"),
getThemeVar("color-warn-400"),
getThemeVar("color-danger-600"),
getThemeVar("color-danger-400"),
getThemeVar("color-info-600"),
getThemeVar("color-info-400"),
getThemeVar("color-secondary-600"),
getThemeVar("color-secondary-400"),
getThemeVar("color-primary-900"),
getThemeVar("color-primary-700"),
getThemeVar("color-success-900"),
getThemeVar("color-success-700"),
getThemeVar("color-warn-900"),
getThemeVar("color-warn-700"),
getThemeVar("color-danger-900"),
getThemeVar("color-danger-700"),
getThemeVar("color-info-900"),
getThemeVar("color-info-700"),
getThemeVar("color-secondary-900"),
getThemeVar("color-secondary-700"),
];
}, [getThemeVar]);
const chartData = useMemo(() => {
if (!data) return [];
return data.map((item, index) => ({
...item,
fill: item.fill || colorValues[index % colorValues.length],
}));
}, [colorValues, data]);
const chartContextValue = useChartContextValue({ dataKey, nameKey });
// --- measurement for auto radius ---
const wrapperRef = useRef<HTMLDivElement | null>(null);
const [box, setBox] = useState<{ width: number; height: number }>({ width: 0, height: 0 });
useEffect(() => {
if (!wrapperRef.current || typeof window === "undefined" || !(window as any).ResizeObserver)
return;
const ro = new (window as any).ResizeObserver((entries: any[]) => {
const cr = entries[0]?.contentRect;
if (cr) setBox({ width: cr.width, height: cr.height });
});
ro.observe(wrapperRef.current);
return () => ro.disconnect();
}, []);
// Sizing heuristics
const RING_PADDING = 8; // keep ring off the SVG edge
const LABEL_GUTTER_OUTSIDE = 16; // space for connectors/labels
const LABEL_GUTTER_INSIDE = 6; // small breathing room if labels inside/off
const MIN_RING_THICKNESS = 12; // maintain legibility on small canvases
// Are labels drawn outside the ring?
const labelsOutside = !!showLabel || (showLabelList && labelListPosition === "outside");
// Resolve outerRadius: explicit value wins; otherwise compute from measured box
const resolvedOuterRadius = useMemo((): number | string => {
const wantsAuto =
outerRadius === undefined ||
(typeof outerRadius === "string" && outerRadius.toLowerCase() === "auto");
if (!wantsAuto) return outerRadius as number | string;
const base = Math.min(box.width, box.height) / 2;
const gutter = labelsOutside ? LABEL_GUTTER_OUTSIDE : LABEL_GUTTER_INSIDE;
const inner = Number(innerRadius) || 0;
const derived = Math.max(inner + MIN_RING_THICKNESS, base - gutter - RING_PADDING);
// Fallback before first measurement
if (!Number.isFinite(derived) || derived <= 0) {
return labelsOutside ? "72%" : "88%";
}
return derived;
}, [outerRadius, box.width, box.height, labelsOutside, innerRadius]);
return (
<ChartProvider value={chartContextValue}>
{children}
<div ref={wrapperRef} className={classnames(styles.wrapper, className)} style={style}>
<ResponsiveContainer width="100%" height="100%" minWidth={0}>
<RPieChart
margin={{ top: 8, right: 8, bottom: labelsOutside ? 16 : 8, left: 8 }}
>
<Tooltip content={<TooltipContent />} />
<Pie
data={chartData}
dataKey={dataKey}
nameKey={nameKey}
innerRadius={innerRadius}
outerRadius={resolvedOuterRadius}
paddingAngle={1}
activeShape={renderActiveShape}
label={showLabel ? renderCustomizedLabel : false}
labelLine={showLabel}
>
{chartContextValue.labelList
? chartContextValue.labelList
: showLabelList && (
<LabelList
position={labelListPosition}
dataKey={nameKey}
className={styles.labelList}
stroke="none"
fill="currentColor"
fontSize={12}
/>
)}
</Pie>
{chartContextValue.legend ? chartContextValue.legend : showLegend && <RLegend />}
</RPieChart>
</ResponsiveContainer>
</div>
</ChartProvider>
);
}
```
--------------------------------------------------------------------------------
/xmlui/scripts/generate-docs/constants.mjs:
--------------------------------------------------------------------------------
```
/**
* Constants and configuration values for the documentation generation scripts
*
* This file contains all the magic strings, configuration objects, and constants
* that were extracted from multiple files in the documentation generation system:
* - get-docs.mjs
* - DocsGenerator.mjs
* - MetadataProcessor.mjs
* - utils.mjs
* - generate-summary-files.mjs
* - build-pages-map.mjs
* - build-downloads-map.mjs
* - logger.mjs
*
* These constants can be imported and used by other documentation generation scripts
* to ensure consistency and improve maintainability.
*/
import { fromKebabtoReadable } from "./utils.mjs";
export const COMPONENT_STATES = {
INTERNAL: "internal",
EXPERIMENTAL: "experimental",
STABLE: "stable",
DEPRECATED: "deprecated",
};
export const FILE_EXTENSIONS = {
MARKDOWN: [".md", ".mdx"],
METADATA: "_meta.json",
};
export const FOLDER_NAMES = {
COMPONENTS: "components",
EXTENSIONS: "extensions",
COMPONENT_SAMPLES: "component-samples",
CONTENT: "content",
PAGES: "pages",
SRC: "src",
DIST: "dist",
META: "meta",
};
export const CONFIG_FILES = {
COMPONENTS: "components-config.json",
EXTENSIONS: "extensions-config.json",
};
export const SUMMARY_CONFIG = {
COMPONENTS: {
title: "Components Overview",
fileName: "_overview",
},
EXTENSIONS: {
title: "Extension Overview",
fileName: "_overview",
},
};
export const PACKAGE_PATTERNS = {
XMLUI_PREFIX: "xmlui-",
METADATA_SUFFIX: "-metadata.js",
};
export const FILE_NAMES = {
COMPONENTS_METADATA: "componentsMetadata.ts",
};
export const LOG_MESSAGES = {
GENERATING_EXTENSION_DOCS: "Generating extension package docs",
LOADING_CONFIG: "Loading config",
LOADING_EXTENSION_PACKAGES: "Loading extension packages",
CLEANING_FOLDER: (folderName) => `Cleaning ${folderName} by removing previous doc files`,
FILES_DELETED_SUCCESS: "All files have been successfully deleted",
SKIPPING_INTERNAL_PACKAGE: "Skipping internal extension package:",
LOADED_EXTENSION_PACKAGE: "Loaded extension package:",
NO_DIST_FOLDER: (dir) => `No dist folder found for ${dir}`,
NO_METADATA_OBJECT: (packageName) =>
`No meta object found for package: ${packageName}.\n Have you built the package?`,
NO_COMPONENT_METADATA: (packageName) =>
`No component metadata found in meta object for package: ${packageName}. Check the "${FOLDER_NAMES.META}/${FILE_NAMES.COMPONENTS_METADATA}" file.`,
};
export const ERROR_MESSAGES = {
NO_CONFIG_PATH: "No config path provided",
WRITE_META_FILE_ERROR: "Could not write _meta file: ",
UNKNOWN_ERROR: "unknown error",
NO_THEME_INFO: "No theme information found. Please run the metadata build first.",
NO_COMPONENT_METADATA: "No component metadata found. Please run the metadata build first.",
FILE_WRITE_ERROR: "Failed to write file",
DIRECTORY_CREATE_ERROR: "Failed to create directory",
METADATA_LOAD_ERROR: "Failed to load metadata",
CONFIG_VALIDATION_ERROR: "Configuration validation failed",
};
export const METADATA_PROPERTIES = {
IS_HTML_TAG: "isHtmlTag",
};
export const TEMPLATE_STRINGS = {
PACKAGE_HEADER: (packageName) => `# ${fromKebabtoReadable(packageName)} Package`,
};
// From DocsGenerator.mjs
export const OUTPUT_FILES = {
METADATA_JSON: "metadata.json",
LANDING_METADATA_JSON: "landing-metadata.json",
PAGES_MAP: "pages.js",
DOWNLOADS_MAP: "downloads.js",
};
export const URL_REFERENCES = {
DOCS: "https://docs.xmlui.org/",
};
// From MetadataProcessor.mjs
export const METADATA_SECTIONS = {
IMPORTS: "imports",
DESCRIPTION: "description",
CONTEXT_VARS: "contextVars",
PROPS: "props",
API: "apis",
EVENTS: "events",
PARTS: "parts",
STYLES: "styles",
};
export const DIRECTIVE_CONFIG = {
INDICATOR: "%-",
SECTION_MAP: {
imports: "IMPORT",
description: "DESC",
props: "PROP",
events: "EVENT",
parts: "PART",
styles: "STYLE",
apis: "API",
contextVars: "CONTEXT_VAR",
},
};
export const SECTION_DISPLAY_NAMES = {
props: "Properties",
events: "Events",
styles: "Styling",
apis: "Exposed Methods",
contextVars: "Context Values",
parts: "Parts",
};
export const SECTION_REFERENCE_KEYS = {
DESCRIPTION: "description",
DESCRIPTION_REF: "descriptionRef",
};
// From utils.mjs
export const TABLE_CONFIG = {
STYLES: {
LEFT: "left",
CENTER: "center",
RIGHT: "right",
},
MARKDOWN_ALIGNMENTS: {
LEFT: ":---",
CENTER: ":---:",
RIGHT: "---:",
DEFAULT: "---",
},
DEFAULT_ROW_NUM_HEADER: { value: "Num", style: "center" },
};
export const COMMON_TABLE_HEADERS = {
VALUE_DESCRIPTION: ["Value", "Description"],
PARAMETER_DESCRIPTION: ["Name", "Description"],
THEME_VARIABLE_DESCRIPTION: ["Theme Variable", "Description"],
};
// From logger.mjs
export const LOGGER_SEVERITY = {
INFO: "info",
WARNING: "warning",
ERROR: "error",
};
export const LOGGER_LEVEL_VALUES = {
ALL: "all",
NONE: "none",
};
// From generate-summary-files.mjs
export const COMPONENT_STATUS_CONFIG = {
ACCEPTED_STATUSES: ["stable", "experimental", "deprecated", "in progress"],
DEFAULT_STATUS: "stable",
};
export const SUMMARY_FILE_CONFIG = {
COMPONENTS_OVERVIEW_HEADER: "# Components Overview [#components-overview]",
PACKAGE_COMPONENTS_HEADER: "## Package Components",
};
// From build-pages-map.mjs
export const PAGES_MAP_CONFIG = {
PATH_CUTOFF: "pages",
INCLUDED_FILE_EXTENSIONS: [".mdx", ".md"],
};
// From build-downloads-map.mjs
export const DOWNLOADS_MAP_CONFIG = {
BASE_URL_CUTOFF: "files",
INCLUDED_FILE_EXTENSIONS: [".zip"],
};
// Error handling utilities
export const ERROR_HANDLING = {
EXIT_CODES: {
SUCCESS: 0,
GENERAL_ERROR: 1,
INVALID_CONFIG: 2,
FILE_NOT_FOUND: 3,
METADATA_ERROR: 4,
},
DEFAULT_EXIT_CODE: 1,
};
// Component navigation generation constants
export const COMPONENT_NAVIGATION = {
DELIMITERS: {
START: "<!-- GENERATED CONTENT/Component references links -->",
END: "<!-- END GENERATED CONTENT/Component references links -->",
START_REGEX: /<!--\s*GENERATED CONTENT\/Component references links.*?-->/,
END_REGEX: /<!--\s*END GENERATED CONTENT\/Component references links.*?-->/,
},
OVERVIEW_LINK: {
LABEL: "Components Overview",
TO: "/components/_overview",
},
TEMPLATES: {
NAVLINK: (componentName) =>
`<NavLink label="${componentName}" to="/components/${componentName}" />`,
OVERVIEW_NAVLINK: (label, to) => `<NavLink label="${label}" to="${to}" />`,
},
};
export const EXTENSIONS_NAVIGATION = {
DELIMITERS: {
START: "<!-- GENERATED CONTENT/Extension references links -->",
END: "<!-- END GENERATED CONTENT/Extension references links -->",
START_REGEX: /<!--\s*GENERATED CONTENT\/Extension references links.*?-->/,
END_REGEX: /<!--\s*END GENERATED CONTENT\/Extension references links.*?-->/,
},
OVERVIEW_LINK: {
LABEL: "Extension Overview",
TO: (packageName) => `/extensions/${packageName}/_overview`
},
TEMPLATES: {
NAVGROUP: (label, children) => `<NavGroup label="${label}">\n${children}\n</NavGroup>`,
NAVLINK: (packageName, componentName) =>
`<NavLink label="${componentName}" to="/extensions/${packageName}/${componentName}" />`,
OVERVIEW_NAVLINK: (label, to) => `<NavLink label="${label}" to="${to}" />`,
},
};
// File paths used in the documentation generation
export const FILE_PATHS = {
MAIN_XMLUI: "src/Main.xmlui",
};
// Path constants for resolving workspace locations
export const PATH_CONSTANTS = {
XMLUI_SRC_COMPONENTS: "xmlui/src/components",
DOCS_COMPONENT_SAMPLES: "docs/component-samples",
WORKSPACE: "workspace",
PACKAGES: "packages",
};
// Text constants and encoding
export const TEXT_CONSTANTS = {
NO_DESCRIPTION_AVAILABLE: "No description available",
UTF8_ENCODING: "utf8",
NEWLINE_SEPARATOR: "\n",
};
// Error contexts for operation handling
export const ERROR_CONTEXTS = {
EXTENSION_PACKAGES_METADATA: "extension packages metadata generation",
FOLDER_CLEANUP: "folder cleanup",
LOADING_EXTENSION_PACKAGE: "loading extension package",
};
// Error messages for component navigation
export const COMPONENT_NAV_ERRORS = {
COMPONENT_NAV_FAILED: "Failed to generate component navigation links",
MAIN_XMLUI_NOT_FOUND: (path) => `Main.xmlui file not found at expected path: ${path}`,
START_DELIMITER_NOT_FOUND: "Start delimiter not found in Main.xmlui",
END_DELIMITER_NOT_FOUND: "End delimiter not found in Main.xmlui",
INVALID_DELIMITER_ORDER: "Invalid delimiter order in Main.xmlui",
DELIMITERS_NOT_FOUND: "Delimiters not found in Main.xmlui",
OVERVIEW_GENERATION_FAILED: "Failed to generate components overview",
CONTENT_REPLACEMENT_FAILED: "Failed to replace generated content",
};
```