This is page 46 of 186. Use http://codebase.md/xmlui-org/xmlui/xmlui/mockApiDef.js?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── cyan-tools-design.md
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog-optimized.yml
│ ├── deploy-blog-swa.yml
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs-swa.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── an-advanced-codefence.gif
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── codefence-runner.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ ├── lorem-ipsum.png
│ │ │ │ ├── playground-checkbox-source.png
│ │ │ │ ├── playground.png
│ │ │ │ ├── use-xmlui-mcp-to-find-a-howto.png
│ │ │ │ └── xmlui-demo-gallery.png
│ │ │ ├── introducing-xmlui.md
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ ├── xmlui-playground.md
│ │ │ └── xmlui-powered-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ ├── staticwebapp.config.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── FancyButton.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── control-cache-invalidation.md
│ │ │ │ ├── debounce-user-input-for-api-calls.md
│ │ │ │ ├── debounce-with-changelistener.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── tsconfig.json
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── Animation.tsx
│ │ ├── AnimationNative.tsx
│ │ ├── FadeAnimation.tsx
│ │ ├── FadeInAnimation.tsx
│ │ ├── FadeOutAnimation.tsx
│ │ ├── index.tsx
│ │ ├── ScaleAnimation.tsx
│ │ └── SlideInAnimation.tsx
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── HelloWorld.module.scss
│ │ ├── HelloWorld.tsx
│ │ ├── HelloWorldNative.tsx
│ │ └── index.tsx
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── IPhoneFrame.module.scss
│ │ ├── IPhoneFrame.tsx
│ │ ├── MacOSAppFrame.module.scss
│ │ ├── MacOSAppFrame.tsx
│ │ ├── WindowsAppFrame.module.scss
│ │ └── WindowsAppFrame.tsx
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── LazyPdfNative.tsx
│ │ ├── Pdf.module.scss
│ │ └── Pdf.tsx
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── hooks
│ │ │ ├── usePlayground.ts
│ │ │ └── useToast.ts
│ │ ├── index.tsx
│ │ ├── playground
│ │ │ ├── Box.module.scss
│ │ │ ├── Box.tsx
│ │ │ ├── CodeSelector.module.scss
│ │ │ ├── CodeSelector.tsx
│ │ │ ├── ConfirmationDialog.module.scss
│ │ │ ├── ConfirmationDialog.tsx
│ │ │ ├── Editor.tsx
│ │ │ ├── Header.module.scss
│ │ │ ├── Header.tsx
│ │ │ ├── Playground.tsx
│ │ │ ├── PlaygroundContent.module.scss
│ │ │ ├── PlaygroundContent.tsx
│ │ │ ├── PlaygroundNative.module.scss
│ │ │ ├── PlaygroundNative.tsx
│ │ │ ├── Preview.tsx
│ │ │ ├── StandalonePlayground.tsx
│ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ ├── ThemeSwitcher.module.scss
│ │ │ ├── ThemeSwitcher.tsx
│ │ │ └── utils.ts
│ │ ├── providers
│ │ │ ├── Toast.module.scss
│ │ │ └── ToastProvider.tsx
│ │ ├── state
│ │ │ └── store.ts
│ │ ├── themes
│ │ │ └── theme.ts
│ │ └── utils
│ │ └── helpers.ts
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Search.module.scss
│ │ └── Search.tsx
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Spreadsheet.tsx
│ │ └── SpreadsheetNative.tsx
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ └── src
│ ├── Carousel
│ │ ├── Carousel.module.scss
│ │ ├── Carousel.tsx
│ │ ├── CarouselContext.tsx
│ │ └── CarouselNative.tsx
│ ├── FancyButton
│ │ ├── FancyButton.module.scss
│ │ ├── FancyButton.tsx
│ │ └── FancyButton.xmlui
│ ├── Hello
│ │ ├── Hello.tsx
│ │ ├── Hello.xmlui
│ │ └── Hello.xmlui.xs
│ ├── HeroSection
│ │ ├── HeroSection.module.scss
│ │ ├── HeroSection.spec.ts
│ │ ├── HeroSection.tsx
│ │ └── HeroSectionNative.tsx
│ ├── index.tsx
│ ├── ScrollToTop
│ │ ├── ScrollToTop.module.scss
│ │ ├── ScrollToTop.tsx
│ │ └── ScrollToTopNative.tsx
│ └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.cjs
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── component-metadata.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── theme-variables-refactoring.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── extract-component-metadata.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── generate-metadata-markdown.js
│ ├── get-langserver-metadata.js
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.module.scss
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.json
├── tsdown.config.ts
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/components/TimeInput/utils.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Merged utility functions for TimeInput component
2 | import getUserLocale from 'get-user-locale';
3 |
4 | // ============================================================================
5 | // Types (from types.ts)
6 | // ============================================================================
7 |
8 | export type Range<T> = [T, T];
9 |
10 | export type AmPmType = 'am' | 'pm';
11 |
12 | export type ClassName = string | null | undefined | (string | null | undefined)[];
13 |
14 | export type Detail = 'hour' | 'minute' | 'second';
15 |
16 | export type LooseValuePiece = string | Date | null;
17 |
18 | export type LooseValue = LooseValuePiece | Range<LooseValuePiece>;
19 |
20 | export type Value = string | null;
21 |
22 | // ============================================================================
23 | // Date Formatter utilities (from dateFormatter.ts)
24 | // ============================================================================
25 |
26 | const formatterCache = new Map();
27 |
28 | export function getFormatter(
29 | options: Intl.DateTimeFormatOptions,
30 | ): (locale: string | undefined, date: Date) => string {
31 | return function formatter(locale: string | undefined, date: Date): string {
32 | const localeWithDefault = locale || getUserLocale();
33 |
34 | if (!formatterCache.has(localeWithDefault)) {
35 | formatterCache.set(localeWithDefault, new Map());
36 | }
37 |
38 | const formatterCacheLocale = formatterCache.get(localeWithDefault);
39 |
40 | if (!formatterCacheLocale.has(options)) {
41 | formatterCacheLocale.set(
42 | options,
43 | new Intl.DateTimeFormat(localeWithDefault || undefined, options).format,
44 | );
45 | }
46 |
47 | return formatterCacheLocale.get(options)(date);
48 | };
49 | }
50 |
51 | const numberFormatterCache = new Map();
52 |
53 | export function getNumberFormatter(
54 | options: Intl.NumberFormatOptions,
55 | ): (locale: string | undefined, number: number) => string {
56 | return (locale: string | undefined, number: number): string => {
57 | const localeWithDefault = locale || getUserLocale();
58 |
59 | if (!numberFormatterCache.has(localeWithDefault)) {
60 | numberFormatterCache.set(localeWithDefault, new Map());
61 | }
62 |
63 | const numberFormatterCacheLocale = numberFormatterCache.get(localeWithDefault);
64 |
65 | if (!numberFormatterCacheLocale.has(options)) {
66 | numberFormatterCacheLocale.set(
67 | options,
68 | new Intl.NumberFormat(localeWithDefault || undefined, options).format,
69 | );
70 | }
71 |
72 | return numberFormatterCacheLocale.get(options)(number);
73 | };
74 | }
75 |
76 | // ============================================================================
77 | // Date utilities (from dateUtils.ts)
78 | // ============================================================================
79 |
80 | export function getHours(dateOrTimeString: string | Date | null | undefined): number {
81 | if (!dateOrTimeString) return 0;
82 |
83 | if (dateOrTimeString instanceof Date) {
84 | return dateOrTimeString.getHours();
85 | }
86 |
87 | // Handle time string format like "14:30:45" or "14:30"
88 | const timeString = String(dateOrTimeString);
89 | const timeParts = timeString.split(':');
90 |
91 | if (timeParts.length >= 1) {
92 | const hours = parseInt(timeParts[0], 10);
93 | return isNaN(hours) ? 0 : hours;
94 | }
95 |
96 | return 0;
97 | }
98 |
99 | export function getMinutes(dateOrTimeString: string | Date | null | undefined): number {
100 | if (!dateOrTimeString) return 0;
101 |
102 | if (dateOrTimeString instanceof Date) {
103 | return dateOrTimeString.getMinutes();
104 | }
105 |
106 | // Handle time string format like "14:30:45" or "14:30"
107 | const timeString = String(dateOrTimeString);
108 | const timeParts = timeString.split(':');
109 |
110 | if (timeParts.length >= 2) {
111 | const minutes = parseInt(timeParts[1], 10);
112 | return isNaN(minutes) ? 0 : minutes;
113 | }
114 |
115 | return 0;
116 | }
117 |
118 | export function getSeconds(dateOrTimeString: string | Date | null | undefined): number {
119 | if (!dateOrTimeString) return 0;
120 |
121 | if (dateOrTimeString instanceof Date) {
122 | return dateOrTimeString.getSeconds();
123 | }
124 |
125 | // Handle time string format like "14:30:45"
126 | const timeString = String(dateOrTimeString);
127 | const timeParts = timeString.split(':');
128 |
129 | if (timeParts.length >= 3) {
130 | const seconds = parseInt(timeParts[2], 10);
131 | return isNaN(seconds) ? 0 : seconds;
132 | }
133 |
134 | return 0;
135 | }
136 |
137 | export function getHoursMinutes(dateOrTimeString: string | Date | null | undefined): string {
138 | if (!dateOrTimeString) return '';
139 |
140 | if (dateOrTimeString instanceof Date) {
141 | const hours = dateOrTimeString.getHours().toString().padStart(2, '0');
142 | const minutes = dateOrTimeString.getMinutes().toString().padStart(2, '0');
143 | return `${hours}:${minutes}`;
144 | }
145 |
146 | // Handle time string - return first two components
147 | const timeString = String(dateOrTimeString);
148 | const timeParts = timeString.split(':');
149 |
150 | if (timeParts.length >= 2) {
151 | const hours = parseInt(timeParts[0], 10);
152 | const minutes = parseInt(timeParts[1], 10);
153 |
154 | if (!isNaN(hours) && !isNaN(minutes)) {
155 | return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
156 | }
157 | }
158 |
159 | return timeString;
160 | }
161 |
162 | export function getHoursMinutesSeconds(dateOrTimeString: string | Date | null | undefined): string {
163 | if (!dateOrTimeString) return '';
164 |
165 | if (dateOrTimeString instanceof Date) {
166 | const hours = dateOrTimeString.getHours().toString().padStart(2, '0');
167 | const minutes = dateOrTimeString.getMinutes().toString().padStart(2, '0');
168 | const seconds = dateOrTimeString.getSeconds().toString().padStart(2, '0');
169 | return `${hours}:${minutes}:${seconds}`;
170 | }
171 |
172 | // Handle time string - ensure it has three components
173 | const timeString = String(dateOrTimeString);
174 | const timeParts = timeString.split(':');
175 |
176 | if (timeParts.length >= 3) {
177 | const hours = parseInt(timeParts[0], 10);
178 | const minutes = parseInt(timeParts[1], 10);
179 | const seconds = parseInt(timeParts[2], 10);
180 |
181 | if (!isNaN(hours) && !isNaN(minutes) && !isNaN(seconds)) {
182 | return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
183 | }
184 | } else if (timeParts.length === 2) {
185 | // Add seconds if missing
186 | const hours = parseInt(timeParts[0], 10);
187 | const minutes = parseInt(timeParts[1], 10);
188 |
189 | if (!isNaN(hours) && !isNaN(minutes)) {
190 | return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`;
191 | }
192 | }
193 |
194 | return timeString;
195 | }
196 |
197 | // ============================================================================
198 | // Date conversion utilities (from dates.ts)
199 | // ============================================================================
200 |
201 | export function convert12to24(hour12: string | number, amPm: AmPmType): number {
202 | let hour24 = Number(hour12);
203 |
204 | if (amPm === 'am' && hour24 === 12) {
205 | hour24 = 0;
206 | } else if (amPm === 'pm' && hour24 < 12) {
207 | hour24 += 12;
208 | }
209 |
210 | return hour24;
211 | }
212 |
213 | export function convert24to12(hour24: string | number): [number, AmPmType] {
214 | const hour12 = Number(hour24) % 12 || 12;
215 |
216 | return [hour12, Number(hour24) < 12 ? 'am' : 'pm'];
217 | }
218 |
219 | // ============================================================================
220 | // General utilities (from utils.ts)
221 | // ============================================================================
222 |
223 | const nines = ['9', '٩'];
224 | const ninesRegExp = new RegExp(`[${nines.join('')}]`);
225 | const amPmFormatter = getFormatter({ hour: 'numeric' });
226 |
227 | export function getAmPmLabels(locale: string | undefined): [string, string] {
228 | const amString = amPmFormatter(locale, new Date(2017, 0, 1, 9));
229 | const pmString = amPmFormatter(locale, new Date(2017, 0, 1, 21));
230 |
231 | const [am1, am2] = amString.split(ninesRegExp) as [string, string];
232 | const [pm1, pm2] = pmString.split(ninesRegExp) as [string, string];
233 |
234 | if (pm2 !== undefined) {
235 | // If pm2 is undefined, nine was not found in pmString - this locale is not using 12-hour time
236 | if (am1 !== pm1) {
237 | return [am1, pm1].map((el) => el.trim()) as [string, string];
238 | }
239 |
240 | if (am2 !== pm2) {
241 | return [am2, pm2].map((el) => el.trim()) as [string, string];
242 | }
243 | }
244 |
245 | // Fallback
246 | return ['AM', 'PM'];
247 | }
248 |
249 | function isValidNumber(num: unknown): num is number {
250 | return num !== null && num !== false && !Number.isNaN(Number(num));
251 | }
252 |
253 | export function safeMin(...args: unknown[]): number {
254 | return Math.min(...args.filter(isValidNumber));
255 | }
256 |
257 | export function safeMax(...args: unknown[]): number {
258 | return Math.max(...args.filter(isValidNumber));
259 | }
260 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Heading/Heading.module.scss:
--------------------------------------------------------------------------------
```scss
1 | @use "../../components-core/theming/themes" as t;
2 |
3 | // --- This code snippet is required to collect the theme variables used in this module
4 | $themeVars: ();
5 | @function createThemeVar($componentVariable) {
6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
7 | @return t.getThemeVar($themeVars, $componentVariable);
8 | }
9 |
10 | $themeVars: t.composeBorderVars($themeVars, "H1");
11 | $themeVars: t.composePaddingVars($themeVars, "H1");
12 | $themeVars: t.composeTextVars($themeVars, "H1");
13 | $themeVars: t.composeBorderVars($themeVars, "H2");
14 | $themeVars: t.composePaddingVars($themeVars, "H2");
15 | $themeVars: t.composeTextVars($themeVars, "H2");
16 | $themeVars: t.composeBorderVars($themeVars, "H3");
17 | $themeVars: t.composePaddingVars($themeVars, "H3");
18 | $themeVars: t.composeTextVars($themeVars, "H3");
19 | $themeVars: t.composeBorderVars($themeVars, "H4");
20 | $themeVars: t.composePaddingVars($themeVars, "H4");
21 | $themeVars: t.composeTextVars($themeVars, "H4");
22 | $themeVars: t.composeBorderVars($themeVars, "H5");
23 | $themeVars: t.composePaddingVars($themeVars, "H5");
24 | $themeVars: t.composeTextVars($themeVars, "H5");
25 | $themeVars: t.composeBorderVars($themeVars, "H6");
26 | $themeVars: t.composePaddingVars($themeVars, "H6");
27 | $themeVars: t.composeTextVars($themeVars, "H6");
28 | $color-anchor-Heading: createThemeVar("color-anchor-Heading");
29 | $gap-anchor-Heading: createThemeVar("gap-anchor-Heading");
30 | $textDecorationLine-anchor-Heading: createThemeVar("textDecorationLine-anchor-Heading");
31 |
32 | // Variables for H1
33 | $textColor-H1: createThemeVar("Heading:textColor-H1");
34 | $letterSpacing-H1: createThemeVar("Heading:letterSpacing-H1");
35 | $fontFamily-H1: createThemeVar("Heading:fontFamily-H1");
36 | $fontWeight-H1: createThemeVar("Heading:fontWeight-H1");
37 | $marginTop-H1: createThemeVar("marginTop-H1");
38 | $marginBottom-H1: createThemeVar("marginBottom-H1");
39 | $textDecorationLine-H1: createThemeVar("Heading:textDecorationLine-H1");
40 | $textDecorationColor-H1: createThemeVar("Heading:textDecorationColor-H1");
41 | $textDecorationStyle-H1: createThemeVar("Heading:textDecorationStyle-H1");
42 | $textDecorationThickness-H1: createThemeVar("Heading:textDecorationThickness-H1");
43 | $textUnderlineOffset-H1: createThemeVar("Heading:textUnderlineOffset-H1");
44 |
45 | // Variables for H2
46 | $textColor-H2: createThemeVar("Heading:textColor-H2");
47 | $letterSpacing-H2: createThemeVar("Heading:letterSpacing-H2");
48 | $fontFamily-H2: createThemeVar("Heading:fontFamily-H2");
49 | $fontWeight-H2: createThemeVar("Heading:fontWeight-H2");
50 | $marginTop-H2: createThemeVar("marginTop-H2");
51 | $marginBottom-H2: createThemeVar("marginBottom-H2");
52 | $textDecorationLine-H2: createThemeVar("Heading:textDecorationLine-H2");
53 | $textDecorationColor-H2: createThemeVar("Heading:textDecorationColor-H2");
54 | $textDecorationStyle-H2: createThemeVar("Heading:textDecorationStyle-H2");
55 | $textDecorationThickness-H2: createThemeVar("Heading:textDecorationThickness-H2");
56 | $textUnderlineOffset-H2: createThemeVar("Heading:textUnderlineOffset-H2");
57 |
58 | // Variables for H3
59 | $textColor-H3: createThemeVar("Heading:textColor-H3");
60 | $letterSpacing-H3: createThemeVar("Heading:letterSpacing-H3");
61 | $fontFamily-H3: createThemeVar("Heading:fontFamily-H3");
62 | $fontWeight-H3: createThemeVar("Heading:fontWeight-H3");
63 | $marginTop-H3: createThemeVar("marginTop-H3");
64 | $marginBottom-H3: createThemeVar("marginBottom-H3");
65 | $textDecorationLine-H3: createThemeVar("Heading:textDecorationLine-H3");
66 | $textDecorationColor-H3: createThemeVar("Heading:textDecorationColor-H3");
67 | $textDecorationStyle-H3: createThemeVar("Heading:textDecorationStyle-H3");
68 | $textDecorationThickness-H3: createThemeVar("Heading:textDecorationThickness-H3");
69 | $textUnderlineOffset-H3: createThemeVar("Heading:textUnderlineOffset-H3");
70 |
71 | // Variables for H4
72 | $textColor-H4: createThemeVar("Heading:textColor-H4");
73 | $letterSpacing-H4: createThemeVar("Heading:letterSpacing-H4");
74 | $fontFamily-H4: createThemeVar("Heading:fontFamily-H4");
75 | $fontWeight-H4: createThemeVar("Heading:fontWeight-H4");
76 | $marginTop-H4: createThemeVar("marginTop-H4");
77 | $marginBottom-H4: createThemeVar("marginBottom-H4");
78 | $textDecorationLine-H4: createThemeVar("Heading:textDecorationLine-H4");
79 | $textDecorationColor-H4: createThemeVar("Heading:textDecorationColor-H4");
80 | $textDecorationStyle-H4: createThemeVar("Heading:textDecorationStyle-H4");
81 | $textDecorationThickness-H4: createThemeVar("Heading:textDecorationThickness-H4");
82 | $textUnderlineOffset-H4: createThemeVar("Heading:textUnderlineOffset-H4");
83 |
84 | // Variables for H5
85 | $textColor-H5: createThemeVar("Heading:textColor-H5");
86 | $letterSpacing-H5: createThemeVar("Heading:letterSpacing-H5");
87 | $fontFamily-H5: createThemeVar("Heading:fontFamily-H5");
88 | $fontWeight-H5: createThemeVar("Heading:fontWeight-H5");
89 | $marginTop-H5: createThemeVar("marginTop-H5");
90 | $marginBottom-H5: createThemeVar("marginBottom-H5");
91 | $textDecorationLine-H5: createThemeVar("Heading:textDecorationLine-H5");
92 | $textDecorationColor-H5: createThemeVar("Heading:textDecorationColor-H5");
93 | $textDecorationStyle-H5: createThemeVar("Heading:textDecorationStyle-H5");
94 | $textDecorationThickness-H5: createThemeVar("Heading:textDecorationThickness-H5");
95 | $textUnderlineOffset-H5: createThemeVar("Heading:textUnderlineOffset-H5");
96 |
97 | // Variables for H6
98 | $textColor-H6: createThemeVar("Heading:textColor-H6");
99 | $letterSpacing-H6: createThemeVar("Heading:letterSpacing-H6");
100 | $fontFamily-H6: createThemeVar("Heading:fontFamily-H6");
101 | $fontWeight-H6: createThemeVar("Heading:fontWeight-H6");
102 | $marginTop-H6: createThemeVar("marginTop-H6");
103 | $marginBottom-H6: createThemeVar("marginBottom-H6");
104 | $textDecorationLine-H6: createThemeVar("Heading:textDecorationLine-H6");
105 | $textDecorationColor-H6: createThemeVar("Heading:textDecorationColor-H6");
106 | $textDecorationStyle-H6: createThemeVar("Heading:textDecorationStyle-H6");
107 | $textDecorationThickness-H6: createThemeVar("Heading:textDecorationThickness-H6");
108 | $textUnderlineOffset-H6: createThemeVar("Heading:textUnderlineOffset-H6");
109 |
110 | @mixin heading($level) {
111 | @include t.paddingVars($themeVars, $level);
112 | @include t.borderVars($themeVars, $level);
113 | @include t.textVars($themeVars, $level);
114 | color: createThemeVar("Heading:textColor-#{$level}");
115 | letter-spacing: createThemeVar("Heading:letterSpacing-#{$level}");
116 | font-family: createThemeVar("Heading:fontFamily-#{$level}");
117 | font-weight: createThemeVar("Heading:fontWeight-#{$level}");
118 | margin-top: createThemeVar("marginTop-#{$level}"); // Intentionally omitting "Heading" inheritance
119 | margin-bottom: createThemeVar(
120 | "marginBottom-#{$level}"
121 | ); // Intentionally omitting "Heading" inheritance
122 | text-decoration-line: createThemeVar("Heading:textDecorationLine-#{$level}");
123 | text-decoration-color: createThemeVar("Heading:textDecorationColor-#{$level}");
124 | text-decoration-style: createThemeVar("Heading:textDecorationStyle-#{$level}");
125 | text-decoration-thickness: createThemeVar("Heading:textDecorationThickness-#{$level}");
126 | text-underline-offset: createThemeVar("Heading:textUnderlineOffset-#{$level}");
127 | }
128 |
129 | @layer components{
130 | .heading {
131 | &.h1 {
132 | @include heading("H1");
133 | }
134 | &.h2 {
135 | @include heading("H2");
136 | }
137 | &.h3 {
138 | @include heading("H3");
139 | }
140 | &.h4 {
141 | @include heading("H4");
142 | }
143 | &.h5 {
144 | @include heading("H5");
145 | }
146 | &.h6 {
147 | @include heading("H6");
148 | }
149 |
150 | --my-scroll-margin-top: var(--header-height);
151 | scroll-margin-top: var(--my-scroll-margin-top);
152 |
153 | a {
154 | opacity: 0; // Hide the <a> element by default
155 | margin-left: $gap-anchor-Heading;
156 | color: $color-anchor-Heading;
157 | transition: opacity 0.2s ease-in-out; // Smooth transition for visibility
158 | }
159 |
160 | &:hover {
161 | a {
162 | opacity: 1;
163 | text-decoration: $textDecorationLine-anchor-Heading;
164 | }
165 | }
166 | }
167 |
168 | .anchorRef {
169 | width: 0;
170 | height: 0;
171 | --my-scroll-margin-top: var(--header-height);
172 | scroll-margin-top: var(--my-scroll-margin-top);
173 | }
174 |
175 | /*
176 | This is a Chromium based solution that is supported by most modern browsers.
177 | See this source for details: https://css-tricks.com/line-clampin/
178 | */
179 | .truncateOverflow {
180 | overflow: hidden;
181 | text-overflow: ellipsis;
182 | white-space: nowrap;
183 | }
184 |
185 | .preserveLinebreaks {
186 | white-space: pre-wrap;
187 | }
188 |
189 | .noEllipsis {
190 | text-overflow: clip;
191 | }
192 | }
193 |
194 |
195 | // --- We export the theme variables to add them to the component renderer
196 | :export {
197 | themeVars: t.json-stringify($themeVars);
198 | }
199 |
```
--------------------------------------------------------------------------------
/packages/xmlui-website-blocks/src/Carousel/CarouselNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import * as React from "react";
2 | import type { CSSProperties, ForwardedRef } from "react";
3 | import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
4 | import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react";
5 | import Autoplay from "embla-carousel-autoplay";
6 | import { composeRefs } from "@radix-ui/react-compose-refs";
7 | import classnames from "classnames";
8 |
9 | import styles from "./Carousel.module.scss";
10 |
11 | import { RegisterComponentApiFn, Icon } from "xmlui";
12 |
13 | const noop = () => {};
14 | export type OrientationOptions = "horizontal" | "vertical";
15 | import { CarouselContext, useCarouselContextValue } from "./CarouselContext";
16 |
17 | type CarouselApi = UseEmblaCarouselType[1];
18 |
19 | export type CarouselProps = {
20 | style?: CSSProperties;
21 | className?: string;
22 | orientation?: OrientationOptions;
23 | indicators?: boolean;
24 | controls?: boolean;
25 | children: React.ReactNode;
26 | autoplay?: boolean;
27 | loop?: boolean;
28 | startIndex?: number;
29 | prevIcon?: string;
30 | nextIcon?: string;
31 | onDisplayDidChange?: (activeSlide: number) => void;
32 | registerComponentApi?: RegisterComponentApiFn;
33 | transitionDuration?: number;
34 | autoplayInterval?: number;
35 | stopAutoplayOnInteraction?: boolean;
36 | };
37 |
38 | export const defaultProps: Pick<
39 | CarouselProps,
40 | | "orientation"
41 | | "indicators"
42 | | "autoplay"
43 | | "controls"
44 | | "loop"
45 | | "startIndex"
46 | | "transitionDuration"
47 | | "autoplayInterval"
48 | | "stopAutoplayOnInteraction"
49 | > = {
50 | orientation: "horizontal",
51 | indicators: true,
52 | autoplay: false,
53 | controls: true,
54 | loop: false,
55 | startIndex: 0,
56 | transitionDuration: 25,
57 | autoplayInterval: 5000,
58 | stopAutoplayOnInteraction: true,
59 | };
60 |
61 | export const CarouselComponent = forwardRef(function CarouselComponent(
62 | {
63 | orientation = defaultProps.orientation,
64 | children,
65 | style,
66 | className,
67 | indicators = defaultProps.indicators,
68 | onDisplayDidChange = noop,
69 | autoplay = defaultProps.autoplay,
70 | controls = defaultProps.controls,
71 | loop = defaultProps.loop,
72 | startIndex = defaultProps.startIndex,
73 | prevIcon,
74 | nextIcon,
75 | transitionDuration = defaultProps.transitionDuration,
76 | autoplayInterval = defaultProps.autoplayInterval,
77 | stopAutoplayOnInteraction = defaultProps.stopAutoplayOnInteraction,
78 | registerComponentApi,
79 | ...rest
80 | }: CarouselProps,
81 | forwardedRef: ForwardedRef<HTMLDivElement>,
82 | ) {
83 | const referenceElement = useRef<HTMLDivElement>(null);
84 | const [activeSlide, setActiveSlide] = useState(0);
85 | const [plugins, setPlugins] = useState<any[]>([]);
86 | const [isPlaying, setIsPlaying] = useState(false);
87 | const { carouselContextValue, carouselItems } = useCarouselContextValue(indicators ?? defaultProps.indicators as boolean);
88 | const ref = forwardedRef ? composeRefs(referenceElement, forwardedRef) : referenceElement;
89 |
90 | const [carouselRef, api] = useEmblaCarousel(
91 | {
92 | axis: orientation === "horizontal" ? "x" : "y",
93 | loop,
94 | startIndex,
95 | duration: transitionDuration,
96 | },
97 | plugins,
98 | );
99 |
100 | const prevIconName = prevIcon || orientation === "horizontal" ? "arrowleft" : "arrowup";
101 | const nextIconName = nextIcon || orientation === "horizontal" ? "arrowright" : "arrowdown";
102 |
103 | useEffect(() => {
104 | if (autoplay) {
105 | setPlugins([
106 | Autoplay({
107 | delay: autoplayInterval,
108 | stopOnInteraction: stopAutoplayOnInteraction,
109 | }),
110 | ]);
111 | }
112 | }, [autoplayInterval, autoplay, stopAutoplayOnInteraction]);
113 |
114 | const toggleAutoplay = useCallback(() => {
115 | const autoplay = api?.plugins()?.autoplay;
116 | if (!autoplay) return;
117 |
118 | const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
119 | playOrStop();
120 | }, [api]);
121 |
122 | useEffect(() => {
123 | const autoplay = api?.plugins()?.autoplay;
124 | if (!autoplay) return;
125 |
126 | setIsPlaying(autoplay.isPlaying());
127 | api
128 | .on("autoplay:play", () => setIsPlaying(true))
129 | .on("autoplay:stop", () => setIsPlaying(false))
130 | .on("reInit", () => setIsPlaying(autoplay.isPlaying()));
131 | }, [api]);
132 |
133 | const scrollTo = useCallback(
134 | (index: number) => {
135 | api?.scrollTo(index);
136 | },
137 | [api],
138 | );
139 |
140 | const [canScrollPrev, setCanScrollPrev] = React.useState(false);
141 | const [canScrollNext, setCanScrollNext] = React.useState(false);
142 |
143 | const onSelect = React.useCallback(
144 | (api: CarouselApi) => {
145 | if (!api) {
146 | return;
147 | }
148 |
149 | const activeIndex = api.selectedScrollSnap();
150 | onDisplayDidChange(activeIndex);
151 | setActiveSlide(activeIndex);
152 |
153 | setCanScrollPrev(api.canScrollPrev());
154 | setCanScrollNext(api.canScrollNext());
155 | },
156 | [onDisplayDidChange],
157 | );
158 |
159 | const scrollPrev = useCallback(() => {
160 | if (api) {
161 | api?.scrollPrev();
162 | }
163 | }, [api]);
164 |
165 | const scrollNext = useCallback(() => {
166 | api?.scrollNext();
167 | }, [api]);
168 |
169 | const handleKeyDown = useCallback(
170 | (event: React.KeyboardEvent<HTMLDivElement>) => {
171 | if (orientation === "horizontal") {
172 | if (event.key === "ArrowLeft") {
173 | event.preventDefault();
174 | scrollPrev();
175 | } else if (event.key === "ArrowRight") {
176 | event.preventDefault();
177 | scrollNext();
178 | }
179 | } else {
180 | if (event.key === "ArrowUp") {
181 | event.preventDefault();
182 | scrollPrev();
183 | } else if (event.key === "ArrowDown") {
184 | event.preventDefault();
185 | scrollNext();
186 | }
187 | }
188 | },
189 | [orientation, scrollPrev, scrollNext],
190 | );
191 |
192 | useEffect(() => {
193 | registerComponentApi?.({
194 | scrollTo,
195 | scrollPrev,
196 | scrollNext,
197 | canScrollPrev: () => canScrollPrev,
198 | canScrollNext: () => canScrollNext,
199 | });
200 | }, [registerComponentApi, scrollTo, scrollPrev, scrollNext, canScrollPrev, canScrollNext]);
201 |
202 | React.useEffect(() => {
203 | if (!api) {
204 | return;
205 | }
206 | onSelect(api);
207 | api.on("init", onSelect);
208 | api.on("reInit", onSelect);
209 | api.on("select", onSelect);
210 | return () => {
211 | api?.off("select", onSelect);
212 | };
213 | }, [api, onSelect]);
214 |
215 |
216 | return (
217 | <CarouselContext.Provider value={carouselContextValue}>
218 | <div
219 | {...rest}
220 | style={style}
221 | ref={ref}
222 | className={classnames(styles.carousel, className)}
223 | role="region"
224 | tabIndex={-1}
225 | onKeyDown={handleKeyDown}
226 | aria-roledescription="carousel"
227 | >
228 | <div ref={carouselRef} className={styles.carouselContentWrapper}>
229 | <div
230 | className={classnames(styles.carouselContent, {
231 | [styles.horizontal]: orientation === "horizontal",
232 | [styles.vertical]: orientation === "vertical",
233 | })}
234 | >
235 | {children}
236 | </div>
237 | </div>
238 | {controls && (
239 | <div className={styles.controls}>
240 | {autoplay && (
241 | <button
242 | className={styles.controlButton}
243 | onClick={toggleAutoplay}
244 | aria-label={isPlaying ? "Pause Autoplay" : "Start Autoplay"}
245 | >
246 | {isPlaying ? <Icon name={"pause"} /> : <Icon name={"play"} />}
247 | </button>
248 | )}
249 | <button
250 | className={styles.controlButton}
251 | disabled={!canScrollPrev}
252 | onClick={scrollPrev}
253 | aria-label="Previous Slide"
254 | >
255 | <Icon name={prevIconName} />
256 | </button>
257 | <button
258 | className={styles.controlButton}
259 | onClick={scrollNext}
260 | disabled={!canScrollNext}
261 | aria-label="Next Slide"
262 | >
263 | <Icon name={nextIconName} />
264 | </button>
265 | </div>
266 | )}
267 | {indicators && (
268 | <div className={styles.indicators} role="tablist" aria-label="Select slide">
269 | {carouselItems.map((_, index) => (
270 | <button
271 | key={index}
272 | type="button"
273 | role="tab"
274 | aria-label={`Go to slide ${index + 1}`}
275 | aria-controls="carousel"
276 | tabIndex={index === activeSlide ? 0 : -1}
277 | onClick={() => scrollTo(index)}
278 | className={classnames(styles.indicator, {
279 | [styles.active]: index === activeSlide,
280 | })}
281 | aria-current={index === activeSlide}
282 | />
283 | ))}
284 | </div>
285 | )}
286 | </div>
287 | </CarouselContext.Provider>
288 | );
289 | });
290 |
```
--------------------------------------------------------------------------------
/xmlui/src/syntax/monaco/xmluiscript.monacoLanguage.ts:
--------------------------------------------------------------------------------
```typescript
1 | /* Based on https://github.com/microsoft/monaco-editor/blob/main/src/basic-languages/javascript/javascript.ts */
2 |
3 | export const XmluiScripGrammar: { id: string; config: any; language: any } = {
4 | id: "xmluiscript",
5 | config: {
6 | wordPattern:
7 | /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
8 |
9 | comments: {
10 | lineComment: "//",
11 | blockComment: ["/*", "*/"],
12 | },
13 |
14 | brackets: [
15 | ["{", "}"],
16 | ["[", "]"],
17 | ["(", ")"],
18 | ],
19 |
20 | onEnterRules: [
21 | {
22 | // e.g. /** | */
23 | beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
24 | afterText: /^\s*\*\/$/,
25 | action: {
26 | // numeric repr of indentAction: languages.IndentAction.IndentOutdent, but without the "monaco-core" package
27 | indentAction: 2,
28 | appendText: " * ",
29 | },
30 | },
31 | {
32 | // e.g. /** ...|
33 | beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
34 | action: {
35 | // numeric repr of indentAction: languages.IndentAction.None, but without the "monaco-core" package
36 | indentAction: 0,
37 | appendText: " * ",
38 | },
39 | },
40 | {
41 | // e.g. * ...|
42 | beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
43 | action: {
44 | // numeric repr of indentAction: languages.IndentAction.None, but without the "monaco-core" package
45 | indentAction: 0,
46 | appendText: "* ",
47 | },
48 | },
49 | {
50 | // e.g. */|
51 | beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
52 | action: {
53 | // numeric repr of indentAction: languages.IndentAction.None, but without the "monaco-core" package
54 | indentAction: 0,
55 | removeText: 1,
56 | },
57 | },
58 | ],
59 |
60 | autoClosingPairs: [
61 | { open: "{", close: "}" },
62 | { open: "[", close: "]" },
63 | { open: "(", close: ")" },
64 | { open: '"', close: '"', notIn: ["string"] },
65 | { open: "'", close: "'", notIn: ["string", "comment"] },
66 | { open: "`", close: "`", notIn: ["string", "comment"] },
67 | { open: "/**", close: " */", notIn: ["string"] },
68 | ],
69 |
70 | folding: {
71 | markers: {
72 | start: new RegExp("^\\s*//\\s*#?region\\b"),
73 | end: new RegExp("^\\s*//\\s*#?endregion\\b"),
74 | },
75 | },
76 | },
77 |
78 | language: {
79 | // Set defaultToken to invalid to see what you do not tokenize yet
80 | defaultToken: "invalid",
81 | tokenPostfix: ".xs",
82 |
83 | typeKeywords: [],
84 |
85 | keywords: [
86 | "break",
87 | "case",
88 | "catch",
89 | "class",
90 | "continue",
91 | "const",
92 | "constructor",
93 | "debugger",
94 | "default",
95 | "delete",
96 | "do",
97 | "else",
98 | "export",
99 | "extends",
100 | "false",
101 | "finally",
102 | "for",
103 | "from",
104 | "function",
105 | "get",
106 | "if",
107 | "import",
108 | "in",
109 | "instanceof",
110 | "let",
111 | "new",
112 | "null",
113 | "return",
114 | "set",
115 | "static",
116 | "super",
117 | "switch",
118 | "symbol",
119 | "this",
120 | "throw",
121 | "true",
122 | "try",
123 | "typeof",
124 | "undefined",
125 | "var",
126 | "void",
127 | "while",
128 | "with",
129 | "yield",
130 | "async",
131 | "await",
132 | "of",
133 | ],
134 |
135 | operators: [
136 | "<=",
137 | ">=",
138 | "==",
139 | "!=",
140 | "===",
141 | "!==",
142 | "=>",
143 | "+",
144 | "-",
145 | "**",
146 | "*",
147 | "/",
148 | "%",
149 | "++",
150 | "--",
151 | "<<",
152 | "</",
153 | ">>",
154 | ">>>",
155 | "&",
156 | "|",
157 | "^",
158 | "!",
159 | "~",
160 | "&&",
161 | "||",
162 | "??",
163 | "?",
164 | ":",
165 | "=",
166 | "+=",
167 | "-=",
168 | "*=",
169 | "**=",
170 | "/=",
171 | "%=",
172 | "<<=",
173 | ">>=",
174 | ">>>=",
175 | "&=",
176 | "|=",
177 | "^=",
178 | "@",
179 | ],
180 |
181 | // we include these common regular expressions
182 | symbols: /[=><!~?:&|+\-*\/\^%]+/,
183 | escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
184 | digits: /\d+(_+\d+)*/,
185 | octaldigits: /[0-7]+(_+[0-7]+)*/,
186 | binarydigits: /[0-1]+(_+[0-1]+)*/,
187 | hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
188 |
189 | regexpctl: /[(){}\[\]\$\^|\-*+?\.]/,
190 | regexpesc: /\\(?:[bBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/,
191 |
192 | // The main tokenizer for our languages
193 | tokenizer: {
194 | root: [[/[{}]/, "delimiter.bracket"], { include: "common" }],
195 |
196 | common: [
197 | // identifiers and keywords
198 | [
199 | /#?[a-z_$][\w$]*/,
200 | {
201 | cases: {
202 | "@keywords": "keyword",
203 | "@default": "identifier",
204 | },
205 | },
206 | ],
207 | [/[A-Z][\w\$]*/, "type.identifier"], // to show class names nicely
208 | // [/[A-Z][\w\$]*/, 'identifier'],
209 |
210 | // whitespace
211 | { include: "@whitespace" },
212 |
213 | // regular expression: ensure it is terminated before beginning (otherwise it is an opeator)
214 | [
215 | /\/(?=([^\\\/]|\\.)+\/([dgimsuy]*)(\s*)(\.|;|,|\)|\]|\}|$))/,
216 | { token: "regexp", bracket: "@open", next: "@regexp" },
217 | ],
218 |
219 | // delimiters and operators
220 | [/[()\[\]]/, "@brackets"],
221 | [/[<>](?!@symbols)/, "@brackets"],
222 | [/!(?=([^=]|$))/, "delimiter"],
223 | [
224 | /@symbols/,
225 | {
226 | cases: {
227 | "@operators": "delimiter",
228 | "@default": "",
229 | },
230 | },
231 | ],
232 |
233 | // numbers
234 | [/(@digits)[eE]([\-+]?(@digits))?/, "number.float"],
235 | [/(@digits)\.(@digits)([eE][\-+]?(@digits))?/, "number.float"],
236 | [/0[xX](@hexdigits)n?/, "number.hex"],
237 | [/0[oO]?(@octaldigits)n?/, "number.octal"],
238 | [/0[bB](@binarydigits)n?/, "number.binary"],
239 | [/(@digits)n?/, "number"],
240 |
241 | // delimiter: after number because of .\d floats
242 | [/[;,.]/, "delimiter"],
243 |
244 | // strings
245 | [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
246 | [/'([^'\\]|\\.)*$/, "string.invalid"], // non-teminated string
247 | [/"/, "string", "@string_double"],
248 | [/'/, "string", "@string_single"],
249 | [/`/, "string", "@string_backtick"],
250 | ],
251 |
252 | whitespace: [
253 | [/[ \t\r\n]+/, ""],
254 | [/\/\*\*(?!\/)/, "comment.doc", "@jsdoc"],
255 | [/\/\*/, "comment", "@comment"],
256 | [/\/\/.*$/, "comment"],
257 | ],
258 |
259 | comment: [
260 | [/[^\/*]+/, "comment"],
261 | [/\*\//, "comment", "@pop"],
262 | [/[\/*]/, "comment"],
263 | ],
264 |
265 | jsdoc: [
266 | [/[^\/*]+/, "comment.doc"],
267 | [/\*\//, "comment.doc", "@pop"],
268 | [/[\/*]/, "comment.doc"],
269 | ],
270 |
271 | // We match regular expression quite precisely
272 | regexp: [
273 | [
274 | /(\{)(\d+(?:,\d*)?)(\})/,
275 | ["regexp.escape.control", "regexp.escape.control", "regexp.escape.control"],
276 | ],
277 | [
278 | /(\[)(\^?)(?=(?:[^\]\\\/]|\\.)+)/,
279 | ["regexp.escape.control", { token: "regexp.escape.control", next: "@regexrange" }],
280 | ],
281 | [/(\()(\?:|\?=|\?!)/, ["regexp.escape.control", "regexp.escape.control"]],
282 | [/[()]/, "regexp.escape.control"],
283 | [/@regexpctl/, "regexp.escape.control"],
284 | [/[^\\\/]/, "regexp"],
285 | [/@regexpesc/, "regexp.escape"],
286 | [/\\\./, "regexp.invalid"],
287 | [
288 | /(\/)([dgimsuy]*)/,
289 | [{ token: "regexp", bracket: "@close", next: "@pop" }, "keyword.other"],
290 | ],
291 | ],
292 |
293 | regexrange: [
294 | [/-/, "regexp.escape.control"],
295 | [/\^/, "regexp.invalid"],
296 | [/@regexpesc/, "regexp.escape"],
297 | [/[^\]]/, "regexp"],
298 | [
299 | /\]/,
300 | {
301 | token: "regexp.escape.control",
302 | next: "@pop",
303 | bracket: "@close",
304 | },
305 | ],
306 | ],
307 |
308 | string_double: [
309 | [/[^\\"]+/, "string"],
310 | [/@escapes/, "string.escape"],
311 | [/\\./, "string.escape.invalid"],
312 | [/"/, "string", "@pop"],
313 | ],
314 |
315 | string_single: [
316 | [/[^\\']+/, "string"],
317 | [/@escapes/, "string.escape"],
318 | [/\\./, "string.escape.invalid"],
319 | [/'/, "string", "@pop"],
320 | ],
321 |
322 | string_backtick: [
323 | [/\$\{/, { token: "delimiter.bracket", next: "@bracketCounting" }],
324 | [/[^\\`$]+/, "string"],
325 | [/@escapes/, "string.escape"],
326 | [/\\./, "string.escape.invalid"],
327 | [/`/, "string", "@pop"],
328 | ],
329 |
330 | bracketCounting: [
331 | [/\{/, "delimiter.bracket", "@bracketCounting"],
332 | [/\}/, "delimiter.bracket", "@pop"],
333 | { include: "common" },
334 | ],
335 | },
336 | },
337 | };
338 |
```
--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/lexer-literals.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, expect, it } from "vitest";
2 | import { Lexer } from "../../../src/parsers/scripting/Lexer";
3 | import { TokenType } from "../../../src/parsers/scripting/TokenType";
4 | import { InputStream } from "../../../src/parsers/common/InputStream";
5 |
6 | describe("Lexer - literal", () => {
7 | const literalCases = [
8 | { src: "0", exp: TokenType.DecimalLiteral },
9 | { src: "1", exp: TokenType.DecimalLiteral },
10 | { src: "2", exp: TokenType.DecimalLiteral },
11 | { src: "3", exp: TokenType.DecimalLiteral },
12 | { src: "4", exp: TokenType.DecimalLiteral },
13 | { src: "5", exp: TokenType.DecimalLiteral },
14 | { src: "6", exp: TokenType.DecimalLiteral },
15 | { src: "7", exp: TokenType.DecimalLiteral },
16 | { src: "8", exp: TokenType.DecimalLiteral },
17 | { src: "9", exp: TokenType.DecimalLiteral },
18 | { src: "0123", exp: TokenType.DecimalLiteral },
19 | { src: "0_123", exp: TokenType.DecimalLiteral },
20 | { src: "123_456_678_912_345", exp: TokenType.DecimalLiteral },
21 |
22 | { src: "0x0", exp: TokenType.HexadecimalLiteral },
23 | { src: "0x_0", exp: TokenType.HexadecimalLiteral },
24 | { src: "0x0_0", exp: TokenType.HexadecimalLiteral },
25 | { src: "0x1_0", exp: TokenType.HexadecimalLiteral },
26 | { src: "0x12ac34", exp: TokenType.HexadecimalLiteral },
27 | { src: "0x12_ac34", exp: TokenType.HexadecimalLiteral },
28 |
29 | { src: "0b0", exp: TokenType.BinaryLiteral },
30 | { src: "0b_0", exp: TokenType.BinaryLiteral },
31 | { src: "0b0_0", exp: TokenType.BinaryLiteral },
32 | { src: "0b1_0", exp: TokenType.BinaryLiteral },
33 | { src: "0b011100110", exp: TokenType.BinaryLiteral },
34 | { src: "0b0111_0011_0", exp: TokenType.BinaryLiteral },
35 |
36 | { src: "0.0", exp: TokenType.RealLiteral },
37 | { src: "1.0", exp: TokenType.RealLiteral },
38 | { src: "2.1", exp: TokenType.RealLiteral },
39 | { src: "3.12", exp: TokenType.RealLiteral },
40 | { src: "4.123", exp: TokenType.RealLiteral },
41 | { src: "5.1234", exp: TokenType.RealLiteral },
42 | { src: "6.12345", exp: TokenType.RealLiteral },
43 | { src: "7.123_456", exp: TokenType.RealLiteral },
44 | { src: "8.12", exp: TokenType.RealLiteral },
45 | { src: "9.12", exp: TokenType.RealLiteral },
46 | { src: "01.0", exp: TokenType.RealLiteral },
47 | { src: "1_.0", exp: TokenType.RealLiteral },
48 | { src: "543_210.012_345_6", exp: TokenType.RealLiteral },
49 |
50 | { src: "0e0", exp: TokenType.RealLiteral },
51 | { src: "1e0", exp: TokenType.RealLiteral },
52 | { src: "2e0", exp: TokenType.RealLiteral },
53 | { src: "3e0", exp: TokenType.RealLiteral },
54 | { src: "4e0", exp: TokenType.RealLiteral },
55 | { src: "5e0", exp: TokenType.RealLiteral },
56 | { src: "6e0", exp: TokenType.RealLiteral },
57 | { src: "7e0", exp: TokenType.RealLiteral },
58 | { src: "8e0", exp: TokenType.RealLiteral },
59 | { src: "9e0", exp: TokenType.RealLiteral },
60 | { src: "123e0", exp: TokenType.RealLiteral },
61 | { src: "23_4e0", exp: TokenType.RealLiteral },
62 | { src: "123e13", exp: TokenType.RealLiteral },
63 | { src: "123e+13", exp: TokenType.RealLiteral },
64 | { src: "123e-13", exp: TokenType.RealLiteral },
65 | { src: "123.456e13", exp: TokenType.RealLiteral },
66 | { src: "123.45_6e+13", exp: TokenType.RealLiteral },
67 | { src: "123.4_56e-13", exp: TokenType.RealLiteral },
68 |
69 | { src: ".0", exp: TokenType.RealLiteral },
70 | { src: ".12_34", exp: TokenType.RealLiteral },
71 | { src: ".456e13", exp: TokenType.RealLiteral },
72 | { src: ".45_6e+13", exp: TokenType.RealLiteral },
73 | { src: ".4_56e-13", exp: TokenType.RealLiteral },
74 |
75 | { src: "true", exp: TokenType.True },
76 | { src: "false", exp: TokenType.False }
77 | ];
78 | literalCases.forEach(c => {
79 | it(`Token ${c.src} #1`, () => {
80 | const source = c.src;
81 | const wLexer = new Lexer(new InputStream(source));
82 |
83 | // --- Act
84 | const next = wLexer.get();
85 |
86 | // --- Assert
87 | expect(next.type).equal(c.exp);
88 | expect(next.text).equal(source);
89 | expect(next.startPosition).equal(0);
90 | expect(next.endPosition).equal(source.length);
91 | expect(next.startLine).equal(1);
92 | expect(next.endLine).equal(1);
93 | expect(next.startColumn).equal(0);
94 | expect(next.endColumn).equal(source.length);
95 | });
96 |
97 | it(`Token ${c.src} #2`, () => {
98 | const source = ` \t \r ${c.src}`;
99 | const wLexer = new Lexer(new InputStream(source));
100 |
101 | // --- Act
102 | const next = wLexer.get();
103 |
104 | // --- Assert
105 | expect(next.type).equal(c.exp);
106 | expect(next.text).equal(c.src);
107 | expect(next.startPosition).equal(5);
108 | expect(next.endPosition).equal(source.length);
109 | expect(next.startLine).equal(1);
110 | expect(next.endLine).equal(1);
111 | expect(next.startColumn).equal(5);
112 | expect(next.endColumn).equal(source.length);
113 | });
114 |
115 | it(`Token ${c.src} #3`, () => {
116 | const source = ` /* c */ ${c.src}`;
117 | const wLexer = new Lexer(new InputStream(source));
118 |
119 | // --- Act
120 | const next = wLexer.get();
121 |
122 | // --- Assert
123 | expect(next.type).equal(c.exp);
124 | expect(next.text).equal(c.src);
125 | expect(next.startPosition).equal(9);
126 | expect(next.endPosition).equal(source.length);
127 | expect(next.startLine).equal(1);
128 | expect(next.endLine).equal(1);
129 | expect(next.startColumn).equal(9);
130 | expect(next.endColumn).equal(source.length);
131 | });
132 |
133 | it(`Token ${c.src} #4`, () => {
134 | const source = `${c.src} \t \r `;
135 | const wLexer = new Lexer(new InputStream(source));
136 |
137 | // --- Act
138 | const next = wLexer.get();
139 |
140 | // --- Assert
141 | expect(next.type).equal(c.exp);
142 | expect(next.text).equal(c.src);
143 | expect(next.startPosition).equal(0);
144 | expect(next.endPosition).equal(c.src.length);
145 | expect(next.startLine).equal(1);
146 | expect(next.endLine).equal(1);
147 | expect(next.startColumn).equal(0);
148 | expect(next.endColumn).equal(c.src.length);
149 | });
150 |
151 | it(`Token ${c.src} #5`, () => {
152 | const source = `${c.src} // c`;
153 | const wLexer = new Lexer(new InputStream(source));
154 |
155 | // --- Act
156 | const next = wLexer.get();
157 | const trail1 = wLexer.get(true);
158 | const trail2 = wLexer.get(true);
159 | const trail3 = wLexer.get();
160 |
161 | // --- Assert
162 | expect(next.type).equal(c.exp);
163 | expect(next.text).equal(c.src);
164 | expect(next.startPosition).equal(0);
165 | expect(next.endPosition).equal(c.src.length);
166 | expect(next.startLine).equal(1);
167 | expect(next.endLine).equal(1);
168 | expect(next.startColumn).equal(0);
169 | expect(next.endColumn).equal(c.src.length);
170 | expect(trail1.type).equal(TokenType.Ws);
171 | expect(trail2.type).equal(TokenType.EolComment);
172 | expect(trail3.type).equal(TokenType.Eof);
173 | });
174 | });
175 |
176 | const errorCases = [
177 | "0.",
178 | "0e",
179 | "0E",
180 | "1.",
181 | "1e",
182 | "1E",
183 | "0e+",
184 | "0E+",
185 | "1e-",
186 | "1E+",
187 |
188 | "0x",
189 | "0b",
190 | "0x_",
191 | "0b_",
192 |
193 | '"',
194 | '"\r"',
195 | '"\n"',
196 | '"\u0085"',
197 | '"\u2028"',
198 | '"\u2029"'
199 | ];
200 | errorCases.forEach(c => {
201 | it(`Token error ${c} #1`, () => {
202 | const wLexer = new Lexer(new InputStream(c));
203 |
204 | // --- Act
205 | const next = wLexer.get();
206 |
207 | // --- Assert
208 | expect(next.type).equal(TokenType.Unknown);
209 | });
210 | });
211 |
212 | const stringCases: string[] = [
213 | '""',
214 | '"abc"',
215 | '"abc,def,1234:#"',
216 | '"\\bdef"',
217 | '"\\fdef"',
218 | '"\\ndef"',
219 | '"\\rdef"',
220 | '"\\tdef"',
221 | '"\\vdef"',
222 | '"\\0def"',
223 | '"\\\'def"',
224 | '"\\"def"',
225 | '"\\`def"',
226 | '"\\\\def"',
227 | '"\\qdef"',
228 | '"\\x40def"',
229 | '"abd\\bdef"',
230 | '"abd\\fdef"',
231 | '"abd\\ndef"',
232 | '"abd\\rdef"',
233 | '"abd\\tdef"',
234 | '"abd\\vdef"',
235 | '"abd\\0def"',
236 | '"abd\\\'def"',
237 | '"abd\\"def"',
238 | '"abd\\\\def"',
239 | '"abd\\qdef"',
240 | '"abd\\x40def"',
241 | '"abd\\b"',
242 | '"abd\\f"',
243 | '"abd\\n"',
244 | '"abd\\r"',
245 | '"abd\\t"',
246 | '"abd\\v"',
247 | '"abd\\S"',
248 | '"abd\\0"',
249 | '"abd\\\'"',
250 | '"abd\\""',
251 | '"abd\\\\"',
252 | '"abd\\q"',
253 | '"abd\\x40"',
254 | '"abd\\u1234"',
255 |
256 | "'abc'",
257 | "'abc,def,1234:#'",
258 | "'\\bdef'"
259 | ];
260 | stringCases.forEach((c, idx) => {
261 | it(`String #${idx + 1}: ${c}`, () => {
262 | // --- Arrange
263 | const wLexer = new Lexer(new InputStream(c));
264 |
265 | // --- Act
266 | const next = wLexer.get();
267 |
268 | // --- Assert
269 | expect(next.type).equal(TokenType.StringLiteral);
270 | expect(next.text).equal(c);
271 | expect(next.startPosition).equal(0);
272 | expect(next.endPosition).equal(c.length);
273 | expect(next.startLine).equal(1);
274 | expect(next.endLine).equal(1);
275 | expect(next.startColumn).equal(0);
276 | expect(next.endColumn).equal(c.length);
277 | });
278 | });
279 | });
280 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Carousel/CarouselNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import * as React from "react";
2 | import type { CSSProperties, ForwardedRef } from "react";
3 | import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
4 | import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react";
5 | import Autoplay from "embla-carousel-autoplay";
6 | import { composeRefs } from "@radix-ui/react-compose-refs";
7 | import classnames from "classnames";
8 |
9 | import styles from "./Carousel.module.scss";
10 |
11 | import type { RegisterComponentApiFn } from "../../abstractions/RendererDefs";
12 | import { noop } from "../../components-core/constants";
13 | import { CarouselContext, useCarouselContextValue } from "./CarouselContext";
14 | import Icon from "../Icon/IconNative";
15 | import type { OrientationOptions } from "../abstractions";
16 |
17 | type CarouselApi = UseEmblaCarouselType[1];
18 |
19 | export type CarouselProps = {
20 | style?: CSSProperties;
21 | className?: string;
22 | orientation?: OrientationOptions;
23 | indicators?: boolean;
24 | controls?: boolean;
25 | children: React.ReactNode;
26 | autoplay?: boolean;
27 | loop?: boolean;
28 | startIndex?: number;
29 | prevIcon?: string;
30 | nextIcon?: string;
31 | onDisplayDidChange?: (activeSlide: number) => void;
32 | registerComponentApi?: RegisterComponentApiFn;
33 | transitionDuration?: number;
34 | autoplayInterval?: number;
35 | stopAutoplayOnInteraction?: boolean;
36 | };
37 |
38 | export const defaultProps: Pick<
39 | CarouselProps,
40 | | "orientation"
41 | | "indicators"
42 | | "autoplay"
43 | | "controls"
44 | | "loop"
45 | | "startIndex"
46 | | "transitionDuration"
47 | | "autoplayInterval"
48 | | "stopAutoplayOnInteraction"
49 | > = {
50 | orientation: "horizontal",
51 | indicators: true,
52 | autoplay: false,
53 | controls: true,
54 | loop: false,
55 | startIndex: 0,
56 | transitionDuration: 25,
57 | autoplayInterval: 5000,
58 | stopAutoplayOnInteraction: true,
59 | };
60 |
61 | export const CarouselComponent = forwardRef(function CarouselComponent(
62 | {
63 | orientation = defaultProps.orientation,
64 | children,
65 | style,
66 | className,
67 | indicators = defaultProps.indicators,
68 | onDisplayDidChange = noop,
69 | autoplay = defaultProps.autoplay,
70 | controls = defaultProps.controls,
71 | loop = defaultProps.loop,
72 | startIndex = defaultProps.startIndex,
73 | prevIcon,
74 | nextIcon,
75 | transitionDuration = defaultProps.transitionDuration,
76 | autoplayInterval = defaultProps.autoplayInterval,
77 | stopAutoplayOnInteraction = defaultProps.stopAutoplayOnInteraction,
78 | registerComponentApi,
79 | ...rest
80 | }: CarouselProps,
81 | forwardedRef: ForwardedRef<HTMLDivElement>,
82 | ) {
83 | const referenceElement = useRef(null);
84 | const [activeSlide, setActiveSlide] = useState(0);
85 | const [plugins, setPlugins] = useState([]);
86 | const [isPlaying, setIsPlaying] = useState(false);
87 | const { carouselContextValue, carouselItems } = useCarouselContextValue(indicators);
88 | const ref = forwardedRef ? composeRefs(referenceElement, forwardedRef) : referenceElement;
89 |
90 | const [carouselRef, api] = useEmblaCarousel(
91 | {
92 | axis: orientation === "horizontal" ? "x" : "y",
93 | loop,
94 | startIndex,
95 | duration: transitionDuration,
96 | },
97 | plugins,
98 | );
99 |
100 | const prevIconName = prevIcon || (orientation === "horizontal" ? "arrowleft" : "arrowup");
101 | const nextIconName = nextIcon || (orientation === "horizontal" ? "arrowright" : "arrowdown");
102 |
103 | useEffect(() => {
104 | if (autoplay) {
105 | setPlugins([
106 | Autoplay({
107 | delay: autoplayInterval,
108 | stopOnInteraction: stopAutoplayOnInteraction,
109 | }),
110 | ]);
111 | }
112 | }, [autoplayInterval, autoplay, stopAutoplayOnInteraction]);
113 |
114 | const toggleAutoplay = useCallback(() => {
115 | const autoplay = api?.plugins()?.autoplay;
116 | if (!autoplay) return;
117 |
118 | const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
119 | playOrStop();
120 | }, [api]);
121 |
122 | useEffect(() => {
123 | const autoplay = api?.plugins()?.autoplay;
124 | if (!autoplay) return;
125 |
126 | setIsPlaying(autoplay.isPlaying());
127 | api
128 | .on("autoplay:play", () => setIsPlaying(true))
129 | .on("autoplay:stop", () => setIsPlaying(false))
130 | .on("reInit", () => setIsPlaying(autoplay.isPlaying()));
131 | }, [api]);
132 |
133 | const scrollTo = useCallback(
134 | (index: number) => {
135 | api?.scrollTo(index);
136 | },
137 | [api],
138 | );
139 |
140 | const [canScrollPrev, setCanScrollPrev] = React.useState(false);
141 | const [canScrollNext, setCanScrollNext] = React.useState(false);
142 |
143 | const onSelect = React.useCallback(
144 | (api: CarouselApi) => {
145 | if (!api) {
146 | return;
147 | }
148 |
149 | const activeIndex = api.selectedScrollSnap();
150 | onDisplayDidChange(activeIndex);
151 | setActiveSlide(activeIndex);
152 |
153 | setCanScrollPrev(api.canScrollPrev());
154 | setCanScrollNext(api.canScrollNext());
155 | },
156 | [onDisplayDidChange],
157 | );
158 |
159 | const scrollPrev = useCallback(() => {
160 | if (api) {
161 | api?.scrollPrev();
162 | }
163 | }, [api]);
164 |
165 | const scrollNext = useCallback(() => {
166 | api?.scrollNext();
167 | }, [api]);
168 |
169 | const handleKeyDown = useCallback(
170 | (event: React.KeyboardEvent<HTMLDivElement>) => {
171 | if (orientation === "horizontal") {
172 | if (event.key === "ArrowLeft") {
173 | event.preventDefault();
174 | scrollPrev();
175 | } else if (event.key === "ArrowRight") {
176 | event.preventDefault();
177 | scrollNext();
178 | }
179 | } else {
180 | if (event.key === "ArrowUp") {
181 | event.preventDefault();
182 | scrollPrev();
183 | } else if (event.key === "ArrowDown") {
184 | event.preventDefault();
185 | scrollNext();
186 | }
187 | }
188 | },
189 | [orientation, scrollPrev, scrollNext],
190 | );
191 |
192 | useEffect(() => {
193 | registerComponentApi?.({
194 | scrollTo,
195 | scrollPrev,
196 | scrollNext,
197 | canScrollPrev: () => canScrollPrev,
198 | canScrollNext: () => canScrollNext,
199 | });
200 | }, [registerComponentApi, scrollTo, scrollPrev, scrollNext, canScrollPrev, canScrollNext]);
201 |
202 | React.useEffect(() => {
203 | if (!api) {
204 | return;
205 | }
206 | onSelect(api);
207 | api.on("init", onSelect);
208 | api.on("reInit", onSelect);
209 | api.on("select", onSelect);
210 | return () => {
211 | api?.off("select", onSelect);
212 | };
213 | }, [api, onSelect]);
214 |
215 | useEffect(() => {
216 | if (referenceElement?.current) {
217 | referenceElement.current.addEventListener("keydown", handleKeyDown);
218 | }
219 | }, [ref, handleKeyDown]);
220 |
221 | return (
222 | <CarouselContext.Provider value={carouselContextValue}>
223 | <div
224 | {...rest}
225 | style={style}
226 | ref={ref}
227 | className={classnames(styles.carousel, className)}
228 | role="region"
229 | tabIndex={-1}
230 | aria-roledescription="carousel"
231 | >
232 | <div ref={carouselRef} className={styles.carouselContentWrapper}>
233 | <div
234 | className={classnames(styles.carouselContent, {
235 | [styles.horizontal]: orientation === "horizontal",
236 | [styles.vertical]: orientation === "vertical",
237 | })}
238 | >
239 | {children}
240 | </div>
241 | </div>
242 | {controls && (
243 | <div className={styles.controls}>
244 | {autoplay && (
245 | <button
246 | className={styles.controlButton}
247 | onClick={toggleAutoplay}
248 | aria-label={isPlaying ? "Pause Autoplay" : "Start Autoplay"}
249 | >
250 | {isPlaying ? <Icon name={"pause"} /> : <Icon name={"play"} />}
251 | </button>
252 | )}
253 | <button
254 | className={styles.controlButton}
255 | disabled={!canScrollPrev}
256 | onClick={scrollPrev}
257 | aria-label="Previous Slide"
258 | >
259 | <Icon name={prevIconName} />
260 | </button>
261 | <button
262 | className={styles.controlButton}
263 | onClick={scrollNext}
264 | disabled={!canScrollNext}
265 | aria-label="Next Slide"
266 | >
267 | <Icon name={nextIconName} />
268 | </button>
269 | </div>
270 | )}
271 | {indicators && (
272 | <div className={styles.indicators} role="tablist" aria-label="Select slide">
273 | {carouselItems.map((_, index) => (
274 | <button
275 | key={index}
276 | type="button"
277 | role="tab"
278 | aria-label={`Go to slide ${index + 1}`}
279 | aria-controls="carousel"
280 | tabIndex={index === activeSlide ? 0 : -1}
281 | onClick={() => scrollTo(index)}
282 | className={classnames(styles.indicator, {
283 | [styles.active]: index === activeSlide,
284 | })}
285 | aria-current={index === activeSlide}
286 | />
287 | ))}
288 | </div>
289 | )}
290 | </div>
291 | </CarouselContext.Provider>
292 | );
293 | });
294 |
```
--------------------------------------------------------------------------------
/xmlui/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "xmlui",
3 | "version": "0.11.5",
4 | "sideEffects": false,
5 | "type": "module",
6 | "scripts": {
7 | "start-test-bed": "cd src/testing/infrastructure && xmlui start",
8 | "build:xmlui-test-bed": "cd src/testing/infrastructure && xmlui build --build-mode=INLINE_ALL --withHostingMetaFiles --withMock",
9 | "build:bin": "tsdown",
10 | "build:xmlui": "vite build --mode lib && tsdown --no-config --no-clean --out-dir='dist/lib' --shims --dts bin/vite-xmlui-plugin.ts",
11 | "build:xmlui-standalone": "vite build --mode standalone",
12 | "build:xmlui-metadata": "vite build --mode metadata",
13 | "test:unit": "vitest run",
14 | "test:e2e": "playwright test",
15 | "test:e2e-summary": "node scripts/e2e-test-summary.js",
16 | "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
17 | "prepublishOnly": "clean-package",
18 | "postpublish": "clean-package restore",
19 | "generate-docs-summaries": "node scripts/generate-docs/generate-summary-files.mjs",
20 | "generate-docs": "node scripts/generate-docs/get-docs.mjs",
21 | "generate-all-docs": "npm run build:xmlui-metadata && npm run generate-docs && npm run generate-docs-summaries",
22 | "export-themes": "npm run build:xmlui-metadata && node scripts/generate-docs/create-theme-files.mjs",
23 | "generate-docs-with-refresh": "npm run build:xmlui-metadata && npm run generate-docs && npm run generate-docs-summaries",
24 | "gen:langserver-metadata": "node scripts/get-langserver-metadata.js > src/language-server/xmlui-metadata-generated.js"
25 | },
26 | "dependencies": {
27 | "@eslint-community/regexpp": "4.10.0",
28 | "@formkit/auto-animate": "0.7.0",
29 | "@modyfi/vite-plugin-yaml": "1.1.0",
30 | "@popperjs/core": "2.11.6",
31 | "@radix-ui/react-accordion": "^1.2.0",
32 | "@radix-ui/react-alert-dialog": "^1.1.2",
33 | "@radix-ui/react-dialog": "1.0.5",
34 | "@radix-ui/react-dropdown-menu": "2.1.16",
35 | "@radix-ui/react-hover-card": "1.0.7",
36 | "@radix-ui/react-popover": "^1.1.2",
37 | "@radix-ui/react-radio-group": "1.1.3",
38 | "@radix-ui/react-select": "^2.1.2",
39 | "@radix-ui/react-slider": "^1.2.3",
40 | "@radix-ui/react-tabs": "1.1.0",
41 | "@radix-ui/react-tooltip": "^1.2.4",
42 | "@radix-ui/react-visually-hidden": "1.0.3",
43 | "@react-spring/web": "^10.0.2",
44 | "@remix-run/react": "2.12.1",
45 | "@tanstack/react-query": "^4.36.1",
46 | "@tanstack/react-table": "8.17.3",
47 | "@tanstack/react-virtual": "3.10.8",
48 | "@vitejs/plugin-react": "5.1.0",
49 | "adm-zip": "0.5.10",
50 | "axios": "1.12.0",
51 | "chroma-js": "^3.1.2",
52 | "classnames": "2.5.1",
53 | "color": "4.2.3",
54 | "date-fns": "2.30.0",
55 | "dexie": "3.2.4",
56 | "dotenv": "16.3.1",
57 | "embla-carousel-autoplay": "^8.3.0",
58 | "embla-carousel-react": "^8.3.0",
59 | "emoji-picker-react": "4.4.10",
60 | "framer-motion": "^12.18.1",
61 | "get-user-locale": "^3.0.0",
62 | "glob": "7.2.0",
63 | "immer": "9.0.16",
64 | "js-yaml": "^4.1.0",
65 | "lodash-es": "4.17.21",
66 | "memoize-one": "6.0.0",
67 | "msw": "2.8.4",
68 | "papaparse": "^5.5.2",
69 | "react": "18.2.0",
70 | "react-day-picker": "9.7.0",
71 | "react-dom": "18.2.0",
72 | "react-dropzone": "14.3.8",
73 | "react-helmet-async": "1.3.0",
74 | "react-hot-toast": "2.4.1",
75 | "react-icons": "4.12.0",
76 | "react-markdown": "^9.0.1",
77 | "react-popper": "2.3.0",
78 | "react-router-dom": "6.26.2",
79 | "react-sticky-el": "^2.1.1",
80 | "react-textarea-autosize": "8.5.3",
81 | "recharts": "^2.15.1",
82 | "rehype-raw": "^7.0.0",
83 | "remark-gfm": "^4.0.1",
84 | "sass": "1.93.3",
85 | "scroll-into-view-if-needed": "^3.1.0",
86 | "tsx": "4.20.6",
87 | "unist-util-visit": "^5.0.0",
88 | "use-context-selector": "1.4.1",
89 | "virtua": "^0.40.0",
90 | "vite": "7.1.12",
91 | "vite-plugin-dts": "4.5.0",
92 | "vite-plugin-lib-inject-css": "1.3.0",
93 | "vite-plugin-svgr": "4.2.0",
94 | "vscode-languageserver": "^9.0.1",
95 | "vscode-languageserver-textdocument": "^1.0.11",
96 | "yargs": "17.7.2"
97 | },
98 | "devDependencies": {
99 | "@rollup/pluginutils": "5.1.0",
100 | "@types/adm-zip": "0.5.4",
101 | "@types/chroma-js": "^3.1.2",
102 | "@types/color": "3.0.6",
103 | "@types/glob": "7.2.0",
104 | "@types/js-yaml": "^4.0.9",
105 | "@types/lodash-es": "4.17.6",
106 | "@types/node": "22.18.13",
107 | "@types/react": "18.2.23",
108 | "@types/react-dom": "18.2.8",
109 | "@types/yargs": "17.0.31",
110 | "@typescript-eslint/eslint-plugin": "8.15.0",
111 | "@typescript-eslint/parser": "8.15.0",
112 | "clean-package": "2.2.0",
113 | "eslint": "^8.57.0",
114 | "eslint-import-resolver-typescript": "3.6.1",
115 | "eslint-plugin-import": "2.28.1",
116 | "eslint-plugin-react": "^7.37.4",
117 | "eslint-plugin-react-hooks": "^5.2.0",
118 | "prettier": "^3.3.3",
119 | "rollup-plugin-copy": "^3.5.0",
120 | "serve": "14.2.0",
121 | "terser": "^5.44.0",
122 | "tsdown": "^0.15.12",
123 | "typescript": "5.7.3",
124 | "vitest": "^3.0.3"
125 | },
126 | "optionalDependencies": {
127 | "@esbuild/linux-x64": "0.25.2",
128 | "@rollup/rollup-linux-x64-gnu": "4.20.0",
129 | "@rollup/rollup-win32-x64-msvc": "4.20.0"
130 | },
131 | "files": [
132 | "dist",
133 | "src/styles",
134 | "src/syntax"
135 | ],
136 | "bin": {
137 | "xmlui": "./bin/bootstrap.js"
138 | },
139 | "clean-package": {
140 | "replace": {
141 | "bin": {
142 | "xmlui": "dist/bin/index.js"
143 | },
144 | "main": "./dist/standalone/xmlui-standalone.umd.js",
145 | "module": "./dist/lib/xmlui.js",
146 | "types": "./dist/lib/xmlui.d.ts",
147 | "exports": {
148 | ".": {
149 | "import": "./dist/lib/xmlui.js",
150 | "require": "./dist/standalone/xmlui-standalone.umd.js"
151 | },
152 | "./language-server": {
153 | "import": "./dist/lib/language-server.js",
154 | "require": "./dist/lib/language-server.js"
155 | },
156 | "./language-server-web-worker": {
157 | "import": "./dist/lib/language-server-web-worker.js",
158 | "require": "./dist/lib/language-server-web-worker.js"
159 | },
160 | "./parser": {
161 | "import": "./dist/lib/xmlui-parser.js",
162 | "require": "./dist/lib/xmlui-parser.js"
163 | },
164 | "./*.css": {
165 | "import": "./dist/lib/*.css",
166 | "require": "./dist/lib/*.css"
167 | },
168 | "./index.scss": {
169 | "import": "./dist/lib/scss/index.scss",
170 | "require": "./dist/lib/scss/index.scss"
171 | },
172 | "./themes.scss": {
173 | "import": "./dist/lib/scss/components-core/theming/_themes.scss",
174 | "require": "./dist/lib/scss/components-core/theming/_themes.scss"
175 | },
176 | "./vite-xmlui-plugin": {
177 | "import": "./dist/lib/vite-xmlui-plugin.js",
178 | "require": "./dist/lib/vite-xmlui-plugin.js"
179 | },
180 | "./syntax/monaco": {
181 | "import": "./dist/lib/syntax-monaco.js",
182 | "require": "./dist/lib/syntax-monaco.js"
183 | },
184 | "./syntax/textmate": {
185 | "import": "./dist/lib/syntax-textmate.js",
186 | "require": "./dist/lib/syntax-textmate.js"
187 | },
188 | "./testing": {
189 | "import": "./dist/lib/testing.js",
190 | "require": "./dist/lib/testing.js"
191 | }
192 | }
193 | }
194 | },
195 | "main": "./src/index.ts",
196 | "exports": {
197 | ".": {
198 | "import": "./src/index.ts",
199 | "require": "./src/index.ts"
200 | },
201 | "./parser": {
202 | "import": "./src/parsers/xmlui-parser/index.ts",
203 | "require": "./src/parsers/xmlui-parser/index.ts"
204 | },
205 | "./index.scss": {
206 | "import": "./src/index.scss",
207 | "require": "./src/index.scss"
208 | },
209 | "./themes.scss": {
210 | "import": "./src/components-core/theming/_themes.scss"
211 | },
212 | "./vite-xmlui-plugin": {
213 | "import": "./bin/vite-xmlui-plugin.ts",
214 | "require": "./bin/vite-xmlui-plugin.ts"
215 | },
216 | "./language-server": {
217 | "import": "./src/language-server/server.ts",
218 | "require": "./src/language-server/server.ts"
219 | },
220 | "./language-server-web-worker": {
221 | "import": "./src/language-server/server-web-worker.ts",
222 | "require": "./src/language-server/server-web-worker.ts"
223 | },
224 | "./syntax/monaco": {
225 | "import": "./src/syntax/monaco/index.ts",
226 | "require": "./src/syntax/monaco/index.ts"
227 | },
228 | "./syntax/textmate": {
229 | "import": "./src/syntax/textMate/index.ts",
230 | "require": "./src/syntax/textMate/index.ts"
231 | },
232 | "./testing": {
233 | "import": "./src/testing/index.ts",
234 | "require": "./src/testing/index.ts"
235 | }
236 | },
237 | "browserslist": {
238 | "production": [
239 | ">0.2%",
240 | "not dead",
241 | "not op_mini all"
242 | ],
243 | "development": [
244 | "last 1 chrome version",
245 | "last 1 firefox version",
246 | "last 1 safari version"
247 | ]
248 | },
249 | "repository": {
250 | "url": "https://github.com/xmlui-org/xmlui.git"
251 | },
252 | "msw": {
253 | "workerDirectory": "src/testing/infrastructure/public"
254 | }
255 | }
256 |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/behaviors/CoreBehaviors.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { cloneElement, type ReactElement } from "react";
2 | import {
3 | Animation,
4 | parseAnimation,
5 | parseAnimationOptions,
6 | } from "../../components/Animation/AnimationNative";
7 | import { ItemWithLabel } from "../../components/FormItem/ItemWithLabel";
8 | import { parseTooltipOptions, Tooltip } from "../../components/Tooltip/TooltipNative";
9 | import { useStyles } from "../theming/StyleContext";
10 | import { THEME_VAR_PREFIX, toCssVar } from "../theming/layout-resolver";
11 | import { parseLayoutProperty, toCssPropertyName } from "../theming/parse-layout-props";
12 | import { buttonVariantValues } from "../../components/abstractions";
13 | import type { Behavior } from "./Behavior";
14 | import { badgeVariantValues } from "../../components/Badge/BadgeNative";
15 |
16 | /**
17 | * Behavior for applying tooltips to components.
18 | */
19 | export const tooltipBehavior: Behavior = {
20 | name: "tooltip",
21 | canAttach: (node) => {
22 | const tooltipText = node.props?.tooltip;
23 | const tooltipMarkdown = node.props?.tooltipMarkdown;
24 | return !!tooltipText || !!tooltipMarkdown;
25 | },
26 | attach: (context, node, metadata) => {
27 | const { extractValue } = context;
28 | const tooltipText = extractValue(context.node.props?.tooltip, true);
29 | const tooltipMarkdown = extractValue(context.node.props?.tooltipMarkdown, true);
30 | const tooltipOptions = extractValue(context.node.props?.tooltipOptions, true);
31 | const parsedOptions = parseTooltipOptions(tooltipOptions);
32 |
33 | return (
34 | <Tooltip text={tooltipText} markdown={tooltipMarkdown} {...parsedOptions}>
35 | {node}
36 | </Tooltip>
37 | );
38 | },
39 | };
40 |
41 | /**
42 | * Behavior for applying animations to components.
43 | */
44 | export const animationBehavior: Behavior = {
45 | name: "animation",
46 | canAttach: (node) => {
47 | return !!node.props?.animation;
48 | },
49 | attach: (context, node, metadata) => {
50 | const { extractValue } = context;
51 | const animation = extractValue(context.node.props?.animation, true);
52 | const animationOptions = extractValue(context.node.props?.animationOptions, true);
53 | const parsedOptions = parseAnimationOptions(animationOptions);
54 |
55 | return (
56 | <Animation animation={parseAnimation(animation)} {...parsedOptions}>
57 | {context.node.type === "ModalDialog"
58 | ? cloneElement(node as ReactElement, {
59 | externalAnimation: true,
60 | })
61 | : node}
62 | </Animation>
63 | );
64 | },
65 | };
66 |
67 | /**
68 | * Behavior for applying a label to form components using ItemWithLabel.
69 | */
70 | export const labelBehavior: Behavior = {
71 | name: "label",
72 | canAttach: (node, metadata) => {
73 | /**
74 | * This behavior can be attached if the component has a 'label' prop
75 | * and is not a component that handles its own labeling.
76 | */
77 | if (metadata?.props?.label) {
78 | return false;
79 | } else if (!node.props?.label) {
80 | return false;
81 | }
82 | return true;
83 | },
84 | attach: (context, node, metadata) => {
85 | const { extractValue, node: componentNode, className } = context;
86 |
87 | const label = extractValue.asOptionalString(componentNode.props.label);
88 | const labelPosition = extractValue(componentNode.props.labelPosition);
89 | const labelWidth = extractValue.asOptionalString(componentNode.props.labelWidth);
90 | const labelBreak = extractValue.asOptionalBoolean(componentNode.props.labelBreak);
91 | const required = extractValue.asOptionalBoolean(componentNode.props.required);
92 | const enabled = extractValue.asOptionalBoolean(componentNode.props.enabled, true);
93 | const shrinkToLabel = extractValue.asOptionalBoolean(componentNode.props.shrinkToLabel);
94 | const style = extractValue(componentNode.props.style);
95 | const readOnly = extractValue.asOptionalBoolean(componentNode.props.readOnly);
96 |
97 | return (
98 | <ItemWithLabel
99 | labelPosition={labelPosition as any}
100 | label={label}
101 | labelWidth={labelWidth}
102 | labelBreak={labelBreak}
103 | required={required}
104 | enabled={enabled}
105 | style={style}
106 | className={className}
107 | shrinkToLabel={shrinkToLabel}
108 | labelStyle={{ pointerEvents: readOnly ? "none" : undefined }}
109 | isInputTemplateUsed={!!componentNode.props?.inputTemplate}
110 | >
111 | {node}
112 | </ItemWithLabel>
113 | );
114 | },
115 | };
116 |
117 | /**
118 | * Behavior for applying custom variant styling to components with non-predefined variant values.
119 | * For Button components, this only applies if the variant is not "solid", "outlined", or "ghost".
120 | * For other components, it applies to any component with a variant prop.
121 | *
122 | * This behavior clones the rendered node and adds the generated CSS class directly,
123 | * without wrapping in an additional component.
124 | */
125 | export const variantBehavior: Behavior = {
126 | name: "variant",
127 | canAttach: (node) => {
128 | const variant = node.props?.variant;
129 |
130 | // Must have a variant prop
131 | if (!variant) {
132 | return false;
133 | }
134 |
135 | // Special handling for Button component
136 | if (node.type === "Button") {
137 | // *** Temporarily disable for Button until we resolve conflicts with existing variants ***
138 | return false;
139 | // // For Button, only attach if variant is NOT one of the predefined values
140 | // const variantStr = typeof variant === "string" ? variant : String(variant);
141 | // return !buttonVariantValues.includes(variantStr as any);
142 | }
143 |
144 | // Special handling for Badge component
145 | if (node.type === "Badge") {
146 | // For Badge, only attach if variant is NOT one of the predefined values
147 | const variantStr = typeof variant === "string" ? variant : String(variant);
148 | return !badgeVariantValues.includes(variantStr as any);
149 | }
150 |
151 | return true;
152 | },
153 | attach: (context, node, metadata) => {
154 | const { extractValue, node: componentNode } = context;
155 | const variant = extractValue(componentNode.props?.variant, true);
156 | const componentType = componentNode.type;
157 |
158 | if (!variant || typeof variant !== "string") {
159 | return node;
160 | }
161 |
162 | // Get theme variables from metadata
163 | const themeVars = metadata?.themeVars;
164 |
165 | // Validate that themeVars is a record object
166 | if (!themeVars || typeof themeVars !== 'object' || Array.isArray(themeVars)) {
167 | return node;
168 | }
169 |
170 | const themeVarKeys = Object.keys(themeVars);
171 |
172 | if (themeVarKeys.length === 0) {
173 | return node;
174 | }
175 |
176 | // Generate the variant style specification from metadata themeVars
177 | const subject = `-${componentType}-${variant}`;
178 |
179 | const variantSpec: Record<string, any> = {
180 | "&": {},
181 | "&:hover": {},
182 | "&:active": {},
183 | "&:disabled": {},
184 | };
185 |
186 | // Process each theme variable from metadata
187 | for (const themeVar of themeVarKeys) {
188 | const parsed = parseLayoutProperty(themeVar, true);
189 |
190 | // Skip if parsing failed or returned an error string
191 | if (typeof parsed === "string") {
192 | continue;
193 | }
194 |
195 | const { property, states } = parsed;
196 |
197 | // Convert camelCase property to CSS property name
198 | const cssProperty = toCssPropertyName(property);
199 | if (!cssProperty) {
200 | continue;
201 | }
202 |
203 | // Determine which selector to use based on states
204 | let selector = "&";
205 | let stateSuffix = "";
206 |
207 | if (states && states.length > 0) {
208 | const state = states[0]; // Use first state
209 | stateSuffix = `--${states.join("--")}`;
210 |
211 | if (state === "hover") {
212 | selector = "&:hover";
213 | } else if (state === "active") {
214 | selector = "&:active";
215 | } else if (state === "disabled") {
216 | selector = "&:disabled";
217 | } else if (state === "focus") {
218 | selector = "&:focus";
219 | }
220 | }
221 |
222 | // Generate CSS variable reference for this theme variable with variant suffix
223 | //const cssVarValue = toCssVar(`$${property}${subject}${stateSuffix}`);
224 | const cssVarValue = `var(--${THEME_VAR_PREFIX}-${property}${subject}${stateSuffix}, ` +
225 | `var(--${THEME_VAR_PREFIX}-${property}-${componentType}))`;
226 |
227 | // Add to appropriate selector in variant spec
228 | if (!variantSpec[selector]) {
229 | variantSpec[selector] = {};
230 | }
231 | variantSpec[selector][cssProperty] = cssVarValue;
232 | }
233 |
234 | // Generate the CSS class using useStyles hook
235 | // eslint-disable-next-line react-hooks/rules-of-hooks
236 | const customVariantClassName = useStyles(variantSpec);
237 |
238 | // Clone the node and add the custom variant className
239 | const existingClassName = (node as ReactElement).props.className || "";
240 | const newClassName = `${existingClassName} ${customVariantClassName || ""}`.trim();
241 |
242 | return cloneElement(node as ReactElement, {
243 | className: newClassName,
244 | });
245 | },
246 | };
247 |
```