This is page 33 of 141. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ └── config.json
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── layout-changes.md
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ └── lorem-ipsum.png
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ └── welcome-to-the-xmlui-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── HelloMd.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── Animation.tsx
│ │ │ ├── AnimationNative.tsx
│ │ │ ├── FadeAnimation.tsx
│ │ │ ├── FadeInAnimation.tsx
│ │ │ ├── FadeOutAnimation.tsx
│ │ │ ├── index.tsx
│ │ │ ├── ScaleAnimation.tsx
│ │ │ └── SlideInAnimation.tsx
│ │ └── tsconfig.json
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ ├── tsconfig.json
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── HelloWorld.module.scss
│ │ │ ├── HelloWorld.tsx
│ │ │ ├── HelloWorldNative.tsx
│ │ │ └── index.tsx
│ │ └── tsconfig.json
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── IPhoneFrame.module.scss
│ │ │ ├── IPhoneFrame.tsx
│ │ │ ├── MacOSAppFrame.module.scss
│ │ │ ├── MacOSAppFrame.tsx
│ │ │ ├── WindowsAppFrame.module.scss
│ │ │ └── WindowsAppFrame.tsx
│ │ └── tsconfig.json
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── LazyPdfNative.tsx
│ │ │ ├── Pdf.module.scss
│ │ │ └── Pdf.tsx
│ │ └── tsconfig.json
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── hooks
│ │ │ │ ├── usePlayground.ts
│ │ │ │ └── useToast.ts
│ │ │ ├── index.tsx
│ │ │ ├── playground
│ │ │ │ ├── Box.module.scss
│ │ │ │ ├── Box.tsx
│ │ │ │ ├── CodeSelector.tsx
│ │ │ │ ├── ConfirmationDialog.module.scss
│ │ │ │ ├── ConfirmationDialog.tsx
│ │ │ │ ├── Editor.tsx
│ │ │ │ ├── Header.module.scss
│ │ │ │ ├── Header.tsx
│ │ │ │ ├── Playground.tsx
│ │ │ │ ├── PlaygroundContent.module.scss
│ │ │ │ ├── PlaygroundContent.tsx
│ │ │ │ ├── PlaygroundNative.module.scss
│ │ │ │ ├── PlaygroundNative.tsx
│ │ │ │ ├── Preview.module.scss
│ │ │ │ ├── Preview.tsx
│ │ │ │ ├── Select.module.scss
│ │ │ │ ├── StandalonePlayground.tsx
│ │ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ │ ├── ThemeSwitcher.module.scss
│ │ │ │ ├── ThemeSwitcher.tsx
│ │ │ │ ├── ToneSwitcher.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── providers
│ │ │ │ ├── Toast.module.scss
│ │ │ │ └── ToastProvider.tsx
│ │ │ ├── state
│ │ │ │ └── store.ts
│ │ │ ├── themes
│ │ │ │ └── theme.ts
│ │ │ └── utils
│ │ │ └── helpers.ts
│ │ └── tsconfig.json
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Search.module.scss
│ │ │ └── Search.tsx
│ │ └── tsconfig.json
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Spreadsheet.tsx
│ │ │ └── SpreadsheetNative.tsx
│ │ └── tsconfig.json
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ ├── src
│ │ ├── Carousel
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── FancyButton
│ │ │ ├── FancyButton.module.scss
│ │ │ ├── FancyButton.tsx
│ │ │ └── FancyButton.xmlui
│ │ ├── Hello
│ │ │ ├── Hello.tsx
│ │ │ ├── Hello.xmlui
│ │ │ └── Hello.xmlui.xs
│ │ ├── HeroSection
│ │ │ ├── HeroSection.module.scss
│ │ │ ├── HeroSection.tsx
│ │ │ └── HeroSectionNative.tsx
│ │ ├── index.tsx
│ │ ├── ScrollToTop
│ │ │ ├── ScrollToTop.module.scss
│ │ │ ├── ScrollToTop.tsx
│ │ │ └── ScrollToTopNative.tsx
│ │ └── vite-env.d.ts
│ └── tsconfig.json
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── playwright.config.ts
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── get-langserver-metadata.mjs
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ ├── LabelListNative.module.scss
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.mjs
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.bin.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/docs/public/resources/images/logos/xmlui7.svg:
--------------------------------------------------------------------------------
```
<svg width="53" height="15" viewBox="0 0 53 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.78 14.66C0.593333 14.66 0.5 14.5133 0.5 14.22C0.5 14.0867 0.526667 13.9667 0.58 13.86C1.08667 12.7 1.80667 11.4 2.74 9.96L4.54 7.52L4.98 6.92L4.4 5.7C4.12 5.11333 3.92667 4.72667 3.82 4.54C3.60667 4.18 3.38667 3.86667 3.16 3.6C3.02667 3.44 2.86667 3.30667 2.68 3.2C2.50667 3.08 2.35333 2.96667 2.22 2.86C2.08667 2.75333 2.02 2.62 2.02 2.46C2.02 2.31333 2.05333 2.17333 2.12 2.04C2.2 1.90667 2.28667 1.84 2.38 1.84C2.43333 1.84 2.48 1.86 2.52 1.9C2.62667 1.98 2.76667 2.09333 2.94 2.24C3.11333 2.38667 3.31333 2.56667 3.54 2.78H3.58C4.06 3.34 4.52667 4.08667 4.98 5.02C5.04667 5.15333 5.12 5.32 5.2 5.52C5.29333 5.70667 5.40667 5.92667 5.54 6.18C5.58 6.14 5.60667 6.08 5.62 6C5.75333 5.81333 5.92667 5.56667 6.14 5.26C6.35333 4.95333 6.61333 4.59333 6.92 4.18C7.22667 3.75333 7.66667 3.11333 8.24 2.26C8.48 1.87333 8.78 1.41333 9.14 0.88C9.20667 0.773333 9.28 0.719999 9.36 0.719999C9.57333 0.719999 9.68 0.92 9.68 1.32C9.68 1.45333 9.64667 1.58667 9.58 1.72C9.5 1.85333 8.76 2.91333 7.36 4.9L6.08 6.74V6.78C6.02667 6.83333 5.96 6.92 5.88 7.04C5.90667 7.10667 5.93333 7.18 5.96 7.26C5.98667 7.34 6.01333 7.42667 6.04 7.52C6.13333 7.72 6.24667 7.99333 6.38 8.34C6.51333 8.67333 6.66667 9.08667 6.84 9.58C6.92 9.99333 7.08 10.5467 7.32 11.24C7.50667 11.56 7.70667 11.9133 7.92 12.3C8.13333 12.6867 8.36667 13.1133 8.62 13.58C8.64667 13.66 8.66 13.7267 8.66 13.78C8.66 14.14 8.52 14.32 8.24 14.32C8.14667 14.32 8.06 14.2733 7.98 14.18L7.46 13.48C7.39333 13.36 7.16 12.8267 6.76 11.88C6.70667 11.7467 6.65333 11.6067 6.6 11.46C6.56 11.3 6.52 11.1267 6.48 10.94C6.42667 10.7133 6.38 10.5267 6.34 10.38C6.31333 10.22 6.28667 10.1133 6.26 10.06L5.46 8.08C5.44667 8.06667 5.42667 8.03333 5.4 7.98C5.38667 7.91333 5.35333 7.82667 5.3 7.72L4.9 8.24C4.63333 8.58667 4.36 8.96 4.08 9.36C3.8 9.76 3.51333 10.18 3.22 10.62C3.06 10.8333 2.86 11.1267 2.62 11.5C2.38 11.8733 2.10667 12.32 1.8 12.84L1.06 14.42C0.98 14.58 0.886667 14.66 0.78 14.66ZM11.9355 14.64C11.6821 14.64 11.5821 14.42 11.6355 13.98L11.9355 12.28C11.9755 12.1067 12.0221 11.8933 12.0755 11.64C12.1421 11.3733 12.2088 11.0667 12.2755 10.72C12.3021 10.4133 12.3488 10.02 12.4155 9.54C12.4821 9.04667 12.5621 8.45333 12.6555 7.76L12.9755 5.86C13.0421 5.63333 13.1221 5.34 13.2155 4.98C13.3088 4.60667 13.4155 4.17333 13.5355 3.68C13.6155 3.28 13.7621 3.14 13.9755 3.26L13.8755 2.5C13.8088 2.26 13.7555 2.04 13.7155 1.84C13.6755 1.62667 13.6421 1.43333 13.6155 1.26C13.6021 1.22 13.5955 1.14667 13.5955 1.04C13.5955 0.719999 13.7021 0.559999 13.9155 0.559999C14.0221 0.559999 14.1021 0.646666 14.1555 0.819999C14.2088 0.993333 14.2621 1.19333 14.3155 1.42C14.3688 1.63333 14.4221 1.87333 14.4755 2.14C14.5555 2.51333 14.6155 2.96 14.6555 3.48C14.6821 3.78667 14.7021 4.04 14.7155 4.24C14.7421 4.44 14.7621 4.59333 14.7755 4.7C14.8421 5.16667 14.9021 5.56 14.9555 5.88C15.0088 6.18667 15.0621 6.42667 15.1155 6.6C15.2888 7.24 15.5955 7.8 16.0355 8.28L16.3355 8.66C16.9755 7.94 17.4555 7.38 17.7755 6.98C17.9221 6.80667 18.0955 6.58667 18.2955 6.32C18.5088 6.04 18.7621 5.70667 19.0555 5.32C19.2155 5.06667 19.5421 4.43333 20.0355 3.42L20.0555 3.4C20.0821 3.4 20.1421 3.28 20.2355 3.04C20.3421 2.73333 20.4488 2.52667 20.5555 2.42L20.4955 2.4C20.6421 2.29333 20.7621 2.24 20.8555 2.24C21.0155 2.24 21.1955 2.48667 21.3955 2.98C21.4888 3.18 21.5355 3.42 21.5355 3.7C21.5355 3.79333 21.5221 3.90667 21.4955 4.04C21.4821 4.17333 21.4621 4.32 21.4355 4.48C21.4088 4.64 21.3821 4.77333 21.3555 4.88C21.3421 4.98667 21.3355 5.06667 21.3355 5.12V5.14C21.3355 5.32667 21.3221 5.63333 21.2955 6.06C21.2688 6.48667 21.2288 7.03333 21.1755 7.7C21.1488 7.82 21.1155 8.11333 21.0755 8.58C21.0355 9.04667 20.9955 9.68667 20.9555 10.5C20.9421 10.7533 20.9288 10.9867 20.9155 11.2C20.9155 11.4 20.9155 11.5867 20.9155 11.76C20.9155 11.9333 20.9155 12.08 20.9155 12.2C20.9288 12.32 20.9421 12.42 20.9555 12.5L21.0955 13.34C21.1088 13.38 21.1155 13.4333 21.1155 13.5C21.1155 13.7667 20.9688 13.9 20.6755 13.9C20.5688 13.9 20.5021 13.86 20.4755 13.78C20.4488 13.6733 20.4221 13.54 20.3955 13.38C20.3688 13.22 20.3421 13.0267 20.3155 12.8C20.2755 11.9733 20.2555 11.34 20.2555 10.9C20.2555 10.6867 20.2755 10.3067 20.3155 9.76C20.3555 9.21333 20.4288 8.5 20.5355 7.62C20.5488 7.38 20.5621 7.06 20.5755 6.66C20.6021 6.24667 20.6355 5.75333 20.6755 5.18L20.8355 3.52C20.8221 3.49333 20.8088 3.47333 20.7955 3.46C20.7821 3.44667 20.7688 3.43333 20.7555 3.42C20.7288 3.42 20.6688 3.60667 20.5755 3.98H20.5555C20.3821 4.31333 20.2021 4.64667 20.0155 4.98C19.8421 5.3 19.6621 5.62 19.4755 5.94C19.1688 6.46 18.7221 7.05333 18.1355 7.72L17.2155 8.86C16.7888 9.36667 16.4955 9.62 16.3355 9.62C16.1755 9.62 16.0288 9.55333 15.8955 9.42L15.6355 9.16L15.5555 9.06C15.1421 8.55333 14.8155 7.98667 14.5755 7.36C14.5221 7.2 14.4688 7.02667 14.4155 6.84C14.3755 6.64 14.3421 6.43333 14.3155 6.22C14.2621 5.82 14.2088 5.42 14.1555 5.02C14.1021 4.60667 14.0755 4.36667 14.0755 4.3C14.0221 4.47333 13.9621 4.70667 13.8955 5C13.8421 5.29333 13.7688 5.64 13.6755 6.04C13.6355 6.28 13.5755 6.58667 13.4955 6.96C13.4555 7.13333 13.4221 7.3 13.3955 7.46C13.3688 7.60667 13.3421 7.73333 13.3155 7.84H13.2755C13.2488 8.14667 13.2021 8.55333 13.1355 9.06C13.0821 9.56667 13.0088 10.1733 12.9155 10.88C12.8755 11.0533 12.8221 11.2733 12.7555 11.54C12.7021 11.8067 12.6421 12.1267 12.5755 12.5C12.5221 12.8333 12.4221 13.3933 12.2755 14.18H12.2555C12.2155 14.4867 12.1088 14.64 11.9355 14.64ZM25.1309 14.42C24.9443 14.42 24.8576 14.34 24.8709 14.18C24.8176 14.1 24.7376 13.92 24.6309 13.64V13.66C24.6309 13.74 24.6243 13.7333 24.6109 13.64C24.5976 13.5333 24.5843 13.4 24.5709 13.24C24.5576 13.08 24.5509 12.9533 24.5509 12.86L24.7109 11.26C24.7776 10.7 24.8776 10.1333 25.0109 9.56L25.4109 7.98C25.5976 7.27333 25.8776 6.13333 26.2509 4.56C26.2776 4.29333 26.3243 3.94 26.3909 3.5C26.4709 3.06 26.5576 2.53333 26.6509 1.92C26.7043 1.58667 26.8509 1.42 27.0909 1.42C27.2909 1.42 27.3909 1.58667 27.3909 1.92V1.94C27.3909 2.23333 27.3376 2.62 27.2309 3.1C27.1376 3.56667 27.0376 4.13333 26.9309 4.8C26.8776 5.05333 26.8109 5.32667 26.7309 5.62C26.6643 5.9 26.5776 6.20667 26.4709 6.54C26.3643 6.88667 26.2709 7.2 26.1909 7.48C26.1109 7.76 26.0509 7.99333 26.0109 8.18C25.9309 8.5 25.8376 8.92 25.7309 9.44C25.6376 9.96 25.5243 10.58 25.3909 11.3C25.3509 11.6467 25.3176 11.9533 25.2909 12.22C25.2643 12.4733 25.2509 12.6867 25.2509 12.86C25.2509 12.94 25.2509 13.0133 25.2509 13.08C25.2643 13.1333 25.2776 13.18 25.2909 13.22C25.2909 13.1933 25.3576 13.3067 25.4909 13.56C25.6643 13.48 26.0176 13.4 26.5509 13.32L27.2309 13.34C27.4043 13.3533 27.5443 13.3667 27.6509 13.38C27.7709 13.3933 27.8576 13.4 27.9109 13.4H27.9709C28.1309 13.4 28.3309 13.36 28.5709 13.28C28.7043 13.2267 28.8176 13.1933 28.9109 13.18C29.0176 13.1533 29.0976 13.1333 29.1509 13.12C30.0576 13 30.6709 12.8267 30.9909 12.6C31.0976 12.5067 31.1976 12.46 31.2909 12.46C31.4243 12.46 31.4909 12.5733 31.4909 12.8C31.4909 13.2 31.3909 13.48 31.1909 13.64C31.1243 13.6933 31.0309 13.7467 30.9109 13.8C30.8043 13.84 30.6709 13.8733 30.5109 13.9L29.2109 14.18C28.9176 14.2333 28.6576 14.28 28.4309 14.32C28.2043 14.3733 28.0043 14.4 27.8309 14.4C27.7376 14.4 27.6376 14.3933 27.5309 14.38C27.4243 14.38 27.3043 14.3733 27.1709 14.36C26.8776 14.32 26.6576 14.3 26.5109 14.3C26.2176 14.2733 25.7576 14.3133 25.1309 14.42Z" fill="#DA020F"/>
<path d="M42.5108 0.69363H45.3318V9.30839C45.3453 10.7846 44.8498 11.971 43.8455 12.8675C42.8411 13.7641 41.4963 14.2022 39.8111 14.182C38.0922 14.182 36.7609 13.7438 35.8171 12.8675C34.8802 11.9845 34.4117 10.7846 34.4117 9.26795V0.69363H37.2226V8.84328C37.2226 9.88136 37.4282 10.6363 37.8394 11.1082C38.2573 11.58 38.9314 11.816 39.8616 11.816C40.7986 11.816 41.4727 11.5767 41.8839 11.0981C42.3018 10.6195 42.5108 9.86114 42.5108 8.82305V0.69363ZM52.4057 14H49.5847V0.69363H52.4057V14Z" fill="black"/>
</svg>
```
--------------------------------------------------------------------------------
/xmlui/src/components/CodeBlock/CodeBlock.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { expect, test } from "../../testing/fixtures";
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test("component renders with basic props", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`<CodeBlock />`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
});
test("component renders with Text content", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<Text variant="codefence">const hello = "world";</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toContainText("const hello = \"world\";");
});
test("component renders with children content", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<pre><code>function test() { return 42; }</code></pre>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toContainText("function test() { return 42; }");
});
// =============================================================================
// ACCESSIBILITY TESTS
// =============================================================================
test("component has proper semantic structure", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<Text variant="codefence">const x = 1;</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
// CodeBlock should be identifiable as a code container
await expect(driver.component).toHaveClass(/codeBlock/);
});
test("component content is accessible to screen readers", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<Text variant="codefence">function accessible() { return true; }</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
// Code content should be readable
const codeText = await driver.getCodeText();
expect(codeText).toContain("function accessible() { return true; }");
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test("component applies theme variables correctly", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`<CodeBlock />`, {
testThemeVars: {
"backgroundColor-CodeBlock": "rgb(255, 0, 0)",
"borderRadius-CodeBlock": "8px",
},
});
const driver = await createCodeBlockDriver();
await expect(driver.component).toHaveCSS("background-color", "rgb(255, 0, 0)");
await expect(driver.component).toHaveCSS("border-radius", "8px");
});
// =============================================================================
// EDGE CASE TESTS
// =============================================================================
test("component handles null and undefined props gracefully", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`<CodeBlock/>`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
});
test("component handles empty content", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`<CodeBlock></CodeBlock>`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toBeVisible();
});
test("component handles special characters in code", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<Text variant="codefence">const emoji = "🚀"; const unicode = "émojis"; const quotes = '"test"';</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toContainText('const emoji = "🚀"');
await expect(driver.getContent()).toContainText("émojis");
});
test("component handles very long code content", async ({ initTestBed, createCodeBlockDriver }) => {
const longCode = "const veryLongVariableName = ".repeat(100) + '"end";';
await initTestBed(`
<CodeBlock>
<Text variant="codefence">${longCode}</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toContainText("end");
});
test("component handles invalid meta prop gracefully", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock meta="invalid">
<Text variant="codefence">test code</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
});
// =============================================================================
// INTEGRATION TESTS
// =============================================================================
test("component works correctly in different layout contexts", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<VStack>
<Text>Code example:</Text>
<CodeBlock>
<Text variant="codefence">const layout = 'test';</Text>
</CodeBlock>
<Text>End of example</Text>
</VStack>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toContainText("const layout = 'test';");
});
test("component handles multiline code content", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<Text variant="codefence">function example() {
return "multiline test";
}</Text>
</CodeBlock>
`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toBeVisible();
await expect(driver.getContent()).toContainText("function example()");
await expect(driver.getContent()).toContainText("return \"multiline test\"");
});
test("component applies global CSS class correctly", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`<CodeBlock />`);
const driver = await createCodeBlockDriver();
await expect(driver.component).toHaveClass(/global-codeBlock/);
});
test("component maintains consistent styling across themes", async ({ initTestBed, createCodeBlockDriver }) => {
// Test light theme
await initTestBed(`<CodeBlock />`, {
testThemeVars: {
"backgroundColor-CodeBlock": "rgb(248, 249, 250)",
"border-CodeBlock": "1px solid rgb(208, 215, 222)",
},
});
let driver = await createCodeBlockDriver();
await expect(driver.component).toHaveCSS("background-color", "rgb(248, 249, 250)");
// Test dark theme
await initTestBed(`<CodeBlock />`, {
testThemeVars: {
"backgroundColor-CodeBlock": "rgb(22, 27, 34)",
"border-CodeBlock": "1px solid rgb(48, 54, 61)",
},
});
driver = await createCodeBlockDriver();
await expect(driver.component).toHaveCSS("background-color", "rgb(22, 27, 34)");
});
// --- Smoke Tests (maintaining existing)
test.describe("smoke tests", { tag: "@smoke" }, () => {
test("component renders", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`<CodeBlock />`);
await expect((await createCodeBlockDriver()).component).toBeAttached();
});
test("component with Text codefence renders", async ({ initTestBed, createCodeBlockDriver }) => {
await initTestBed(`
<CodeBlock>
<Text variant="codefence">
This is a test
</Text>
</CodeBlock>
`);
await expect((await createCodeBlockDriver()).component).toBeAttached();
});
});
```
--------------------------------------------------------------------------------
/docs/content/components/ExpandableItem.md:
--------------------------------------------------------------------------------
```markdown
# ExpandableItem [#expandableitem]
`ExpandableItem` creates expandable/collapsible section, similar to the HTML details disclosure element. When the user clicks on the `summary` the content expands or collapses.
## Properties [#properties]
### `enabled` (default: true) [#enabled-default-true]
When true, the expandable item can be opened and closed. When false, it cannot be toggled.
### `iconCollapsed` (default: "chevronright") [#iconcollapsed-default-chevronright]
The icon to display when the item is collapsed.
### `iconExpanded` (default: "chevrondown") [#iconexpanded-default-chevrondown]
The icon to display when the item is expanded.
### `iconPosition` (default: "end") [#iconposition-default-end]
Determines the position of the icon (start or end).
Available values:
| Value | Description |
| --- | --- |
| `start` | The icon will appear at the start (left side when the left-to-right direction is set) |
| `end` | The icon will appear at the end (right side when the left-to-right direction is set) **(default)** |
### `initiallyExpanded` (default: false) [#initiallyexpanded-default-false]
Determines if the component is initially expanded when rendered.
### `summary` [#summary]
The summary content that is always visible and acts as the trigger.
### `withSwitch` (default: false) [#withswitch-default-false]
When true, a switch is used instead of an icon to toggle the expanded state.
## Events [#events]
### `expandedChange` [#expandedchange]
This event fires when the expandable item is expanded or collapsed. It provides a boolean value indicating the new state.
## Exposed Methods [#exposed-methods]
### `collapse` [#collapse]
This method collapses the item.
**Signature**: `collapse(): void`
### `expand` [#expand]
This method expands the item.
**Signature**: `expand(): void`
### `isExpanded` [#isexpanded]
This method returns a boolean indicating whether the item is currently expanded.
**Signature**: `isExpanded(): boolean`
### `toggle` [#toggle]
This method toggles the item's expanded state.
**Signature**: `toggle(): void`
## Styling [#styling]
### Theme Variables [#theme-variables]
| Variable | Default Value (Light) | Default Value (Dark) |
| --- | --- | --- |
| [backgroundColor](../styles-and-themes/common-units/#color)-ExpandableItem | transparent | transparent |
| [border](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [borderBottom](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [borderBottomColor](../styles-and-themes/common-units/#color)-ExpandableItem | *none* | *none* |
| [borderBottomStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | *none* | *none* |
| [borderBottomWidth](../styles-and-themes/common-units/#size)-ExpandableItem | 1px | 1px |
| [borderColor](../styles-and-themes/common-units/#color)-ExpandableItem | $borderColor | $borderColor |
| [borderEndEndRadius](../styles-and-themes/common-units/#border-rounding)-ExpandableItem | *none* | *none* |
| [borderEndStartRadius](../styles-and-themes/common-units/#border-rounding)-ExpandableItem | *none* | *none* |
| [borderHorizontal](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [borderHorizontalColor](../styles-and-themes/common-units/#color)-ExpandableItem | *none* | *none* |
| [borderHorizontalStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | *none* | *none* |
| [borderHorizontalWidth](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [borderLeft](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [color](../styles-and-themes/common-units/#color)-ExpandableItem | *none* | *none* |
| [borderLeftStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | *none* | *none* |
| [borderLeftWidth](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-ExpandableItem | 0 | 0 |
| [borderRight](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [color](../styles-and-themes/common-units/#color)-ExpandableItem | *none* | *none* |
| [borderRightStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | *none* | *none* |
| [borderRightWidth](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [borderStartEndRadius](../styles-and-themes/common-units/#border-rounding)-ExpandableItem | *none* | *none* |
| [borderStartStartRadius](../styles-and-themes/common-units/#border-rounding)-ExpandableItem | *none* | *none* |
| [borderStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | solid | solid |
| [borderTop](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [borderTopColor](../styles-and-themes/common-units/#color)-ExpandableItem | *none* | *none* |
| [borderTopStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | *none* | *none* |
| [borderTopWidth](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [borderHorizontal](../styles-and-themes/common-units/#border)-ExpandableItem | *none* | *none* |
| [borderVerticalColor](../styles-and-themes/common-units/#color)-ExpandableItem | *none* | *none* |
| [borderVerticalStyle](../styles-and-themes/common-units/#border-style)-ExpandableItem | *none* | *none* |
| [borderVerticalWidth](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [borderWidth](../styles-and-themes/common-units/#size)-ExpandableItem | 0 | 0 |
| [color](../styles-and-themes/common-units/#color)-ExpandableItem | $textColor-primary | $textColor-primary |
| [color](../styles-and-themes/common-units/#color)-ExpandableItem--disabled | $textColor--disabled | $textColor--disabled |
| [fontFamily](../styles-and-themes/common-units/#fontFamily)-ExpandableItem | $fontFamily | $fontFamily |
| [fontSize](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [fontWeight](../styles-and-themes/common-units/#fontWeight)-ExpandableItem | *none* | *none* |
| [gap](../styles-and-themes/common-units/#size)-ExpandableItem | $space-2 | $space-2 |
| [padding](../styles-and-themes/common-units/#size)-content-ExpandableItem | *none* | *none* |
| [padding](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [paddingBottom](../styles-and-themes/common-units/#size)-content-ExpandableItem | *none* | *none* |
| [paddingBottom](../styles-and-themes/common-units/#size)-ExpandableItem | $space-2 | $space-2 |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-content-ExpandableItem | *none* | *none* |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [paddingHorizontal](../styles-and-themes/common-units/#size)-ExpandableItem-summary | *none* | *none* |
| [paddingLeft](../styles-and-themes/common-units/#size)-content-ExpandableItem | $space-3 | $space-3 |
| [paddingLeft](../styles-and-themes/common-units/#size)-ExpandableItem | $space-0 | $space-0 |
| [paddingRight](../styles-and-themes/common-units/#size)-content-ExpandableItem | $space-3 | $space-3 |
| [paddingRight](../styles-and-themes/common-units/#size)-ExpandableItem | $space-0 | $space-0 |
| [paddingTop](../styles-and-themes/common-units/#size)-content-ExpandableItem | *none* | *none* |
| [paddingTop](../styles-and-themes/common-units/#size)-ExpandableItem | $space-2 | $space-2 |
| [paddingVertical](../styles-and-themes/common-units/#size)-content-ExpandableItem | $space-2 | $space-2 |
| [paddingVertical](../styles-and-themes/common-units/#size)-ExpandableItem | *none* | *none* |
| [paddingVertical](../styles-and-themes/common-units/#size)-ExpandableItem-summary | *none* | *none* |
| [transition](../styles-and-themes/common-units/#transition)-ExpandableItem | 0.2s ease | 0.2s ease |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/loader/Loader.tsx:
--------------------------------------------------------------------------------
```typescript
import { useCallback, useEffect, useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
import { createDraft, finishDraft } from "immer";
import type { QueryFunction } from "@tanstack/query-core/src/types";
import type { RegisterComponentApiFn } from "../../abstractions/RendererDefs";
import type {
LoaderErrorFn,
LoaderInProgressChangedFn,
LoaderLoadedFn,
TransformResultFn,
} from "../abstractions/LoaderRenderer";
import type { ComponentDef } from "../../abstractions/ComponentDefs";
import type { ContainerState } from "../rendering/ContainerWrapper";
import { extractParam } from "../utils/extractParam";
import { useAppContext } from "../AppContext";
import { useIsomorphicLayoutEffect, usePrevious } from "../utils/hooks";
import { useApiInterceptorContext } from "../interception/useApiInterceptorContext";
/**
* The properties of the Loader component
*/
type LoaderProps = {
state: ContainerState;
loader: ComponentDef;
loaderFn: LoaderFunction;
queryId?: readonly any[];
pollIntervalInSeconds?: number;
registerComponentApi?: RegisterComponentApiFn;
onLoaded?: (...args: any[]) => void;
loaderInProgressChanged: LoaderInProgressChangedFn;
loaderIsRefetchingChanged: LoaderInProgressChangedFn;
loaderLoaded: LoaderLoadedFn;
loaderError: LoaderErrorFn;
transformResult?: TransformResultFn;
structuralSharing?: boolean;
};
/**
* This function represents the loader's job.
*/
type LoaderFunction = (abortSignal?: AbortSignal) => Promise<any>;
export function Loader({
state,
loader,
loaderFn,
queryId,
pollIntervalInSeconds,
registerComponentApi,
onLoaded,
loaderLoaded,
loaderInProgressChanged,
loaderIsRefetchingChanged,
loaderError,
transformResult,
structuralSharing = true,
}: LoaderProps) {
const { uid } = loader;
const appContext = useAppContext();
const { initialized } = useApiInterceptorContext();
// --- Rely on react-query to decide when data fetching should use the cache or when is should fetch the data from
// --- its data source.
// --- data: The data obtained by the query
// --- status: Query execution status
// --- error: Error information about the current query error (in "error" state)
// --- refetch: The function that can be used to re-fetch the data (because of data/state changes)
const { data, status, isFetching, isLoading, error, refetch, isRefetching } = useQuery({
queryKey: useMemo(
() => (queryId ? queryId : [uid, extractParam(state, loader.props, appContext)]),
[appContext, loader.props, queryId, state, uid],
),
structuralSharing,
//we pause the loaders if the apiInterceptor is not initialized (true when the app is not using mockApi)
enabled: initialized,
queryFn: useCallback<QueryFunction>(
async ({ signal }) => {
// console.log("[Loader queryFn] Starting to fetch data...");
try {
const newVar: any = await loaderFn(signal);
//console.log("[Loader queryFn] Data received:", newVar);
if (newVar === undefined) {
//console.log("[Loader queryFn] Data is undefined, returning null");
return null;
}
return newVar;
} catch (error) {
//console.error("[Loader queryFn] Error fetching data:", error);
throw error;
}
},
[loaderFn],
),
select: useCallback(
(data: any) => {
// console.log("[Loader select] Data before transform:", data);
// console.log("[Loader select] resultSelector:", loader.props.resultSelector);
// console.log("[Loader select] transformResult function:", !!transformResult);
let result = data;
const resultSelector = loader.props.resultSelector;
if (resultSelector) {
//console.log("[Loader select] Applying resultSelector");
result = extractParam(
{ $response: data },
resultSelector.startsWith("{") ? resultSelector : `{$response.${resultSelector}}`,
);
//console.log("[Loader select] Result after resultSelector:", result);
}
const finalResult = transformResult ? transformResult(result) : result;
//console.log("[Loader select] Final result:", finalResult);
return finalResult;
},
[loader.props.resultSelector, transformResult],
),
retry: false,
});
useEffect(() => {
let intervalId: NodeJS.Timeout;
if (pollIntervalInSeconds) {
intervalId = setInterval(() => {
void refetch();
}, pollIntervalInSeconds * 1000);
}
return () => {
if (intervalId) {
clearInterval(intervalId);
}
};
}, [pollIntervalInSeconds, refetch]);
const prevData = usePrevious(data);
const prevError = usePrevious(error);
useIsomorphicLayoutEffect(() => {
loaderInProgressChanged(isFetching || isLoading);
}, [isLoading, isFetching, loaderInProgressChanged]);
useIsomorphicLayoutEffect(() => {
loaderIsRefetchingChanged(isRefetching);
}, [isRefetching, loaderIsRefetchingChanged]);
useIsomorphicLayoutEffect(() => {
//console.log("isRefetching", isRefetching);
//console.log("[Loader] useLayoutEffect status:", status);
//console.log("[Loader] useLayoutEffect data:", data);
//console.log("[Loader] useLayoutEffect prevData:", prevData);
//console.log("[Loader] useLayoutEffect data !== prevData:", data !== prevData);
if (status === "success" && data !== prevData) {
//console.log("[Loader] Calling loaderLoaded with data:", data);
loaderLoaded(data);
//we do this to push the onLoaded callback to the next event loop.
// It works, because useLayoutEffect will run synchronously after the render, and the onLoaded callback will have
// access to the latest loader value
setTimeout(() => {
// console.log("[Loader] Calling onLoaded with data:", data);
// console.log("[Loader] onLoaded function exists:", !!onLoaded);
onLoaded?.(data, isRefetching);
}, 0);
} else if (status === "error" && error !== prevError) {
// console.log("[Loader] Calling loaderError with error:", error);
loaderError(error);
}
}, [data, error, loaderError, loaderLoaded, onLoaded, prevData, prevError, status, isRefetching]);
useIsomorphicLayoutEffect(() => {
return () => {
loaderLoaded(undefined);
};
}, [loaderLoaded, uid]);
useEffect(() => {
registerComponentApi?.({
refetch: (options) => {
void refetch(options);
},
update: async (updater) => {
const oldData = appContext.queryClient?.getQueryData(queryId!) as any[];
if (!oldData) {
//loader not loaded yet, we skip the update
return;
}
const draft = createDraft(oldData);
const ret = await updater(draft); //if it returns a value, we take it as the new data
const newData = ret || finishDraft(draft);
if (oldData.length !== newData.length) {
throw new Error(
"Use this method for update only. If you want to add or delete, call the addItem/deleteItem method.",
);
}
appContext.queryClient?.setQueryData(queryId!, newData);
},
addItem: (element: any, indexToInsert?: number) => {
const oldData = appContext.queryClient?.getQueryData(queryId!) as any[];
const draft = createDraft(oldData);
if (indexToInsert === undefined) {
draft.push(element);
} else {
draft.splice(indexToInsert, 0, element);
}
const newData = finishDraft(draft);
appContext.queryClient?.setQueryData(queryId!, newData);
},
getItems: () => {
return data;
},
deleteItem: (element: any) => {
throw new Error("not implemented");
},
});
}, [appContext.queryClient, queryId, refetch, registerComponentApi, data]);
return null;
}
```
--------------------------------------------------------------------------------
/docs/content/components/AppState.md:
--------------------------------------------------------------------------------
```markdown
# AppState [#appstate]
`AppState` is an invisible component that provides global state management across your entire application. Unlike component variables that are scoped locally, AppState allows any component to access and update shared state without prop drilling.
**Key advantages over variables:**
- **Global accessibility**: Any component can access the state by referencing the same `bucket`
- **Automatic reactivity**: UI updates automatically when state changes, no manual prop passing required
- **Cross-component coordination**: Perfect for user sessions, UI preferences, loading states, and shared data
## Using AppState [#using-appstate]
Variables in xmlui are a straightforward tool for managing states. However, a variable's scope is the app's main file or the particular component file in which it is declared. To access the variable's value (the stored state), you must pass its value to components wanting to leverage it.
### Storing State in Variables [#storing-state-in-variables]
In the following example, the main file of the app declares a variable, `enhancedMode`, which is toggled with a checkbox:
```xmlui-pg
---app copy display filename="Main.xmlui"
<App var.enhancedMode="{false}">
<VStack gap="$space-4" padding="$space-4">
<Checkbox
label="Enhanced mode"
initialValue="{enhancedMode}"
onDidChange="v => enhancedMode = v" />
<Component1 enhancedMode="{enhancedMode}" />
<Component2 enhancedMode="{enhancedMode}" />
</VStack>
</App>
---desc
Two components, `Component1` and `Component2`, use the value of `enhancedMode`. Because of the aforementioned scoping issue, the app must explicitly pass the variable's value to those components so that they can use it. These components utilize the value to render their UI:
---comp copy display filename="Component1.xmlui"
<Component name="Component1">
<H3 when="{$props.enhancedMode}">I am in enhanced mode!</H3>
<Text when="{!$props.enhancedMode}">Enhanced mode turned off.</Text>
</Component>
---desc
When you define an `AppState`, you can set its `initialValue` property to initialize the state value.
---comp copy display filename="Component2.xmlui"
<Component name="Component2">
<Button enabled="{$props.enhancedMode}">Set enhanced options</Button>
</Component>
---desc
You can try how this app works:
```
### Storing State in AppState [#storing-state-in-appstate]
What if `Component1` and `Component2` had nested components using `enhancedMode`? You must also pass them to the nested components (via properties). What if you have not only one but a dozen of similar properties and a long chain of nested components? The "use a variable" pattern soon becomes a state management nightmare.
If the nested components want to change the state value, you must declare events and event handlers or component APIs to manage the state. It sounds pretty tedious!
This situation is where `AppState` comes into the picture. With an `AppState` instance, you can define a state object that automatically conveys between parent and nested child component chains implicitly.
Let's turn the previous example into one using `AppState`! The following code shows how we change the main app file:
```xmlui-pg
---app copy display filename="Main.xmlui"
<App>
<AppState id="appState" initialValue="{{ enhancedMode: false }}"/>
<VStack gap="$space-4" padding="$space-4">
<Checkbox
label="Enhanced mode"
initialValue="{appState.value.enhancedMode}"
onDidChange="v => appState.update({ enhancedMode: v})" />
<Component1 />
<Component2 />
</VStack>
</App>
---desc
When you define an `AppState`, you can set its `initialValue` property to initialize the state value. You must give an ID to the `AppState` instance to access it and use the `value` property to get the state. You must invoke the `AppState`'s `update` method when you intend to update the state.
The components may use their own `AppState` object to access the state value:
---comp copy display filename="Component1.xmlui"
<Component name="Component1">
<AppState id="state" />
<H3 when="{state.value.enhancedMode}">I am in enhanced mode!</H3>
<Text when="{!state.value.enhancedMode}">Enhanced mode turned off.</Text>
</Component>
---comp copy display filename="Component2.xmlui"
<Component name="Component2">
<AppState id="state" />
<Button enabled="{state.value.enhancedMode}">Set enhanced options</Button>
</Component>
---desc
The modified app works the same way as the previous one (using variables):
```
### State Buckets [#state-buckets]
With the `AppState` component, you can use separate state objects. The `bucket` property of `AppState` is an identifier (using the "default" string by default). While multiple `AppState` objects use the same `bucket` property value, they refer to the same state object.
If you want to run the sample with explicit state buckets (for example, with the `settings` bucket id), you should change the `AppState` declarations accordingly:
```xmlui /bucket="settings"/
<!-- Main.xmlui -->
<AppState id="appState" bucket="settings" initialValue="{{ enhancedMode: false }}"/>
<!-- Component1 -->
<AppState id="state" bucket="settings" />
<!-- Component2 -->
<AppState id="state" bucket="settings" />
```
## Properties [#properties]
### `bucket` (default: "default") [#bucket-default-default]
This property is the identifier of the bucket to which the `AppState` instance is bound. Multiple `AppState` instances with the same bucket will share the same state object: any of them updating the state will cause the other instances to view the new, updated state.
### `initialValue` [#initialvalue]
This property contains the initial state value. Though you can use multiple `AppState`component instances for the same bucket with their `initialValue` set, it may result in faulty app logic. When xmlui instantiates an `AppState` with an explicit initial value, that value is immediately merged with the existing state. The issue may come from the behavior that `initialValue` is set (merged) only when a component mounts. By default, the bucket's initial state is undefined.
## Events [#events]
### `didUpdate` [#didupdate]
This event is fired when the AppState value is updated. The event provides the new state value as its parameter.
## Exposed Methods [#exposed-methods]
### `appendToList` [#appendtolist]
This method appends an item to an array in the application state object bound to the `AppState` instance.
**Signature**: `appendToList(key: string, id: any)`
- `key`: The key of the array in the state object.
- `id`: The item to append to the array.
### `listIncludes` [#listincludes]
This method checks if an array in the application state object contains a specific item.
**Signature**: `listIncludes(key: string, id: any)`
- `key`: The key of the array in the state object.
- `id`: The item to check for in the array.
### `removeFromList` [#removefromlist]
This method removes an item from an array in the application state object bound to the `AppState` instance.
**Signature**: `removeFromList(key: string, id: any)`
- `key`: The key of the array in the state object.
- `id`: The item to remove from the array.
### `update` [#update]
This method updates the application state object bound to the `AppState` instance.
**Signature**: `update(newState: Record<string, any>)`
- `newState`: An object that specifies the new state value.
If the argument is a hash object, it will be merged with the previous state value. Let's assume the previous state value was the following:
```json
{
"enhancedMode": false,
"showHeader": true,
"showFooter": true,
"theme": "light"
}
```
Now, update the state with this call:
```js
appState.update({ enhancedMode: true, theme: "dark" });
```
The new state value will be:
```json
{
"enhancedMode": true,
"showHeader": true,
"showFooter": true,
"theme": "dark"
}
```
## Styling [#styling]
This component does not have any styles.
```
--------------------------------------------------------------------------------
/docs/public/resources/files/for-download/mockApi.js:
--------------------------------------------------------------------------------
```javascript
/* eslint-disable */
/* tslint:disable */
/**
* Mock Service Worker (2.0.1).
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.
*/
const INTEGRITY_CHECKSUM = '0877fcdc026242810f5bfde0d7178db4'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()
self.addEventListener('install', function () {
self.skipWaiting()
})
self.addEventListener('activate', function (event) {
event.waitUntil(self.clients.claim())
})
self.addEventListener('message', async function (event) {
const clientId = event.source.id
if (!clientId || !self.clients) {
return
}
const client = await self.clients.get(clientId)
if (!client) {
return
}
const allClients = await self.clients.matchAll({
type: 'window',
})
switch (event.data) {
case 'KEEPALIVE_REQUEST': {
sendToClient(client, {
type: 'KEEPALIVE_RESPONSE',
})
break
}
case 'INTEGRITY_CHECK_REQUEST': {
sendToClient(client, {
type: 'INTEGRITY_CHECK_RESPONSE',
payload: INTEGRITY_CHECKSUM,
})
break
}
case 'MOCK_ACTIVATE': {
activeClientIds.add(clientId)
sendToClient(client, {
type: 'MOCKING_ENABLED',
payload: true,
})
break
}
case 'MOCK_DEACTIVATE': {
activeClientIds.delete(clientId)
break
}
case 'CLIENT_CLOSED': {
activeClientIds.delete(clientId)
const remainingClients = allClients.filter((client) => {
return client.id !== clientId
})
// Unregister itself when there are no more clients
if (remainingClients.length === 0) {
self.registration.unregister()
}
break
}
}
})
self.addEventListener('fetch', function (event) {
const { request } = event
// Bypass navigation requests.
if (request.mode === 'navigate') {
return
}
// Opening the DevTools triggers the "only-if-cached" request
// that cannot be handled by the worker. Bypass such requests.
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
return
}
// Bypass all requests when there are no active clients.
// Prevents the self-unregistered worked from handling requests
// after it's been deleted (still remains active until the next reload).
if (activeClientIds.size === 0) {
return
}
// Generate unique request ID.
const requestId = crypto.randomUUID()
event.respondWith(handleRequest(event, requestId))
})
async function handleRequest(event, requestId) {
const client = await resolveMainClient(event)
const response = await getResponse(event, client, requestId)
// Send back the response clone for the "response:*" life-cycle events.
// Ensure MSW is active and ready to handle the message, otherwise
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
;(async function () {
const responseClone = response.clone()
// When performing original requests, response body will
// always be a ReadableStream, even for 204 responses.
// But when creating a new Response instance on the client,
// the body for a 204 response must be null.
const responseBody = response.status === 204 ? null : responseClone.body
sendToClient(
client,
{
type: 'RESPONSE',
payload: {
requestId,
isMockedResponse: IS_MOCKED_RESPONSE in response,
type: responseClone.type,
status: responseClone.status,
statusText: responseClone.statusText,
body: responseBody,
headers: Object.fromEntries(responseClone.headers.entries()),
},
},
[responseBody],
)
})()
}
return response
}
// Resolve the main client for the given event.
// Client that issues a request doesn't necessarily equal the client
// that registered the worker. It's with the latter the worker should
// communicate with during the response resolving phase.
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId)
if (client?.frameType === 'top-level') {
return client
}
const allClients = await self.clients.matchAll({
type: 'window',
})
return allClients
.filter((client) => {
// Get only those clients that are currently visible.
return client.visibilityState === 'visible'
})
.find((client) => {
// Find the client ID that's recorded in the
// set of clients that have registered the worker.
return activeClientIds.has(client.id)
})
}
async function getResponse(event, client, requestId) {
const { request } = event
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const requestClone = request.clone()
function passthrough() {
const headers = Object.fromEntries(requestClone.headers.entries())
// Remove internal MSW request header so the passthrough request
// complies with any potential CORS preflight checks on the server.
// Some servers forbid unknown request headers.
delete headers['x-msw-intention']
return fetch(requestClone, { headers })
}
// Bypass mocking when the client is not active.
if (!client) {
return passthrough()
}
// Bypass initial page load requests (i.e. static assets).
// The absence of the immediate/parent client in the map of the active clients
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
// and is not ready to handle requests.
if (!activeClientIds.has(client.id)) {
return passthrough()
}
// Bypass requests with the explicit bypass header.
// Such requests can be issued by "ctx.fetch()".
const mswIntention = request.headers.get('x-msw-intention')
if (['bypass', 'passthrough'].includes(mswIntention)) {
return passthrough()
}
// Notify the client that a request has been intercepted.
const requestBuffer = await request.arrayBuffer()
const clientMessage = await sendToClient(
client,
{
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
mode: request.mode,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: requestBuffer,
keepalive: request.keepalive,
},
},
[requestBuffer],
)
switch (clientMessage.type) {
case 'MOCK_RESPONSE': {
return respondWithMock(clientMessage.data)
}
case 'MOCK_NOT_FOUND': {
return passthrough()
}
}
return passthrough()
}
function sendToClient(client, message, transferrables = []) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel()
channel.port1.onmessage = (event) => {
if (event.data && event.data.error) {
return reject(event.data.error)
}
resolve(event.data)
}
client.postMessage(
message,
[channel.port2].concat(transferrables.filter(Boolean)),
)
})
}
async function respondWithMock(response) {
// Setting response status code to 0 is a no-op.
// However, when responding with a "Response.error()", the produced Response
// instance will have status code set to 0. Since it's not possible to create
// a Response instance with status code 0, handle that use-case separately.
if (response.status === 0) {
return Response.error()
}
const mockedResponse = new Response(response.body, response)
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
value: true,
enumerable: true,
})
return mockedResponse
}
```
--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/lexer-misc.test.ts:
--------------------------------------------------------------------------------
```typescript
import { describe, expect, it } from "vitest";
import { Lexer } from "../../../src/parsers/scripting/Lexer";
import { InputStream } from "../../../src/parsers/common/InputStream";
import { TokenType } from "../../../src/parsers/scripting/TokenType";
describe("Lexer - miscellaneous", () => {
it("Empty", () => {
// --- Arrange
const source = "";
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(TokenType.Eof);
expect(next.text).equal(source);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(source.length);
});
const miscCases = [
{ src: "...", exp: TokenType.Spread },
{ src: ";", exp: TokenType.Semicolon },
{ src: "/", exp: TokenType.Divide },
{ src: "**", exp: TokenType.Exponent },
{ src: "*", exp: TokenType.Multiply },
{ src: "%", exp: TokenType.Remainder },
{ src: "+", exp: TokenType.Plus },
{ src: "-", exp: TokenType.Minus },
{ src: "^", exp: TokenType.BitwiseXor },
{ src: "|", exp: TokenType.BitwiseOr },
{ src: "||", exp: TokenType.LogicalOr },
{ src: "&", exp: TokenType.BitwiseAnd },
{ src: "&&", exp: TokenType.LogicalAnd },
{ src: ",", exp: TokenType.Comma },
{ src: "(", exp: TokenType.LParent },
{ src: ")", exp: TokenType.RParent },
{ src: ":", exp: TokenType.Colon },
{ src: "[", exp: TokenType.LSquare },
{ src: "]", exp: TokenType.RSquare },
{ src: "?", exp: TokenType.QuestionMark },
{ src: "??", exp: TokenType.NullCoalesce },
{ src: "?.", exp: TokenType.OptionalChaining },
{ src: "{", exp: TokenType.LBrace },
{ src: "}", exp: TokenType.RBrace },
{ src: "=", exp: TokenType.Assignment },
{ src: "==", exp: TokenType.Equal },
{ src: "===", exp: TokenType.StrictEqual },
{ src: "!", exp: TokenType.LogicalNot },
{ src: "!=", exp: TokenType.NotEqual },
{ src: "!==", exp: TokenType.StrictNotEqual },
{ src: "<", exp: TokenType.LessThan },
{ src: "<=", exp: TokenType.LessThanOrEqual },
{ src: "<<", exp: TokenType.ShiftLeft },
{ src: ">", exp: TokenType.GreaterThan },
{ src: ">=", exp: TokenType.GreaterThanOrEqual },
{ src: ">>", exp: TokenType.SignedShiftRight },
{ src: ">>>", exp: TokenType.ShiftRight },
{ src: ".", exp: TokenType.Dot },
{ src: "thisId", exp: TokenType.Identifier },
{ src: "_other145$", exp: TokenType.Identifier },
{ src: "$loader", exp: TokenType.Identifier },
{ src: "Infinity", exp: TokenType.Infinity },
{ src: "NaN", exp: TokenType.NaN },
{ src: "true", exp: TokenType.True },
{ src: "false", exp: TokenType.False },
{ src: "$item", exp: TokenType.Identifier },
{ src: "null", exp: TokenType.Null },
{ src: "undefined", exp: TokenType.Undefined },
{ src: "in", exp: TokenType.In },
{ src: "+=", exp: TokenType.AddAssignment },
{ src: "-=", exp: TokenType.SubtractAssignment },
{ src: "**=", exp: TokenType.ExponentAssignment },
{ src: "*=", exp: TokenType.MultiplyAssignment },
{ src: "/=", exp: TokenType.DivideAssignment },
{ src: "%=", exp: TokenType.RemainderAssignment },
{ src: "<<=", exp: TokenType.ShiftLeftAssignment },
{ src: ">>=", exp: TokenType.SignedShiftRightAssignment },
{ src: ">>>=", exp: TokenType.ShiftRightAssignment },
{ src: "&=", exp: TokenType.BitwiseAndAssignment },
{ src: "&&=", exp: TokenType.LogicalAndAssignment },
{ src: "^=", exp: TokenType.BitwiseXorAssignment },
{ src: "|=", exp: TokenType.BitwiseOrAssignment },
{ src: "||=", exp: TokenType.LogicalOrAssignment },
{ src: "??=", exp: TokenType.NullCoalesceAssignment },
{ src: "=>", exp: TokenType.Arrow },
{ src: "++", exp: TokenType.IncOp },
{ src: "--", exp: TokenType.DecOp },
{ src: "let", exp: TokenType.Let },
{ src: "const", exp: TokenType.Const },
{ src: "var", exp: TokenType.Var },
{ src: "if", exp: TokenType.If },
{ src: "else", exp: TokenType.Else },
{ src: "return", exp: TokenType.Return },
{ src: "break", exp: TokenType.Break },
{ src: "continue", exp: TokenType.Continue },
{ src: "do", exp: TokenType.Do },
{ src: "while", exp: TokenType.While },
{ src: "for", exp: TokenType.For },
{ src: "of", exp: TokenType.Of },
{ src: "try", exp: TokenType.Try },
{ src: "catch", exp: TokenType.Catch },
{ src: "finally", exp: TokenType.Finally },
{ src: "throw", exp: TokenType.Throw },
{ src: "switch", exp: TokenType.Switch },
{ src: "case", exp: TokenType.Case },
{ src: "default", exp: TokenType.Default },
{ src: "delete", exp: TokenType.Delete },
{ src: "function", exp: TokenType.Function },
{ src: "as", exp: TokenType.As },
];
miscCases.forEach(c => {
it(`Token ${c.src} #1`, () => {
const source = c.src;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(source);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(source.length);
});
it(`Token ${c.src} #2`, () => {
const source = ` \t \r ${c.src}`;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(5);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(5);
expect(next.endColumn).equal(source.length);
});
it(`Token ${c.src} #3`, () => {
const source = ` /* c */ ${c.src}`;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(9);
expect(next.endPosition).equal(source.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(9);
expect(next.endColumn).equal(source.length);
});
it(`Token ${c.src} #4`, () => {
const source = `${c.src} \t \r `;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(c.src.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(c.src.length);
});
it(`Token ${c.src} #5`, () => {
const source = `${c.src} // c`;
const wLexer = new Lexer(new InputStream(source));
// --- Act
const next = wLexer.get();
const trail1 = wLexer.get(true);
const trail2 = wLexer.get(true);
const trail3 = wLexer.get();
// --- Assert
expect(next.type).equal(c.exp);
expect(next.text).equal(c.src);
expect(next.startPosition).equal(0);
expect(next.endPosition).equal(c.src.length);
expect(next.startLine).equal(1);
expect(next.endLine).equal(1);
expect(next.startColumn).equal(0);
expect(next.endColumn).equal(c.src.length);
expect(trail1.type).equal(TokenType.Ws);
expect(trail2.type).equal(TokenType.EolComment);
expect(trail3.type).equal(TokenType.Eof);
});
});
});
```
--------------------------------------------------------------------------------
/xmlui/tests/components-core/theming/layout-resolver2.test.ts:
--------------------------------------------------------------------------------
```typescript
import { describe, expect, it } from "vitest";
import { resolveLayoutProps, toCssVar } from "../../../src/components-core/theming/layout-resolver";
describe("Layout resolver 2", () => {
const THEME_ID = "$some-theme-id_x";
const THEME_ID_VALUE = toCssVar(THEME_ID);
const THEME_ID2 = "$some-theme-id2";
const THEME_ID2_VALUE = toCssVar(THEME_ID2);
const THEME_ID3 = "$some-theme-id3";
const THEME_ID3_VALUE = toCssVar(THEME_ID3);
const THEME_ID4 = "$some-theme-id4";
const THEME_ID4_VALUE = toCssVar(THEME_ID4);
// --- Borders
it("textDecoration", () => {
const PROP = "textDecoration";
const VALUE = "underline solid red";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("textDecorationLine", () => {
const PROP = "textDecorationLine";
const VALUE = "overline";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("textDecorationColor", () => {
const PROP = "textDecorationColor";
const VALUE = "blue";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("textDecorationStyle", () => {
const PROP = "textDecorationStyle";
const VALUE = "dotted";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("textDecorationThickness", () => {
const PROP = "textDecorationThickness";
const VALUE = "8px";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("textUnderlineOffset", () => {
const PROP = "textUnderlineOffset";
const VALUE = "8px";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("outline", () => {
const PROP = "outline";
const VALUE = "3px solid green";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("outlineWidth", () => {
const PROP = "outlineWidth";
const VALUE = "3px";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("outlineColor", () => {
const PROP = "outlineColor";
const VALUE = "green";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("outlineStyle", () => {
const PROP = "outlineStyle";
const VALUE = "dotted";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("outlineOffset", () => {
const PROP = "outlineOffset";
const VALUE = "5px";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps[PROP]).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderHorizontal: 1px solid red", () => {
const PROP = "borderHorizontal";
const VALUE = "1px solid red";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderLeft).toBe(VALUE);
expect(result.cssProps.borderRight).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderLeft overwrites borderHorizontal", () => {
const PROP = "borderHorizontal";
const VALUE = "1px solid red";
const result = resolveLayoutProps({ [PROP]: VALUE, borderLeft: "2px solid blue" });
expect(result.cssProps.borderLeft).toBe("2px solid blue");
expect(result.cssProps.borderRight).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderRight overwrites borderHorizontal", () => {
const PROP = "borderHorizontal";
const VALUE = "1px solid red";
const result = resolveLayoutProps({ [PROP]: VALUE, borderRight: "2px solid blue" });
expect(result.cssProps.borderLeft).toBe(VALUE);
expect(result.cssProps.borderRight).toBe("2px solid blue");
expect(result.issues.has(PROP)).toBe(false);
});
it("borderHorizontal (themeVar) 1", () => {
const PROP = "borderHorizontal";
const VALUE = THEME_ID;
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderLeft).toBe(THEME_ID_VALUE);
expect(result.cssProps.borderRight).toBe(THEME_ID_VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderHorizontal (themeVar) 2", () => {
const PROP = "borderHorizontal";
const VALUE = `${THEME_ID} ${THEME_ID2}`;
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderLeft).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE}`);
expect(result.cssProps.borderRight).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE}`);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderHorizontal (themeVar) 3", () => {
const PROP = "borderHorizontal";
const VALUE = `${THEME_ID} ${THEME_ID2} ${THEME_ID3}`;
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderLeft).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE} ${THEME_ID3_VALUE}`);
expect(result.cssProps.borderRight).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE} ${THEME_ID3_VALUE}`);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderVertical: 1px solid red", () => {
const PROP = "borderVertical";
const VALUE = "1px solid red";
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderTop).toBe(VALUE);
expect(result.cssProps.borderBottom).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderVertical (themeVar) 1", () => {
const PROP = "borderVertical";
const VALUE = THEME_ID;
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderTop).toBe(THEME_ID_VALUE);
expect(result.cssProps.borderBottom).toBe(THEME_ID_VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderVertical (themeVar) 2", () => {
const PROP = "borderVertical";
const VALUE = `${THEME_ID} ${THEME_ID2}`;
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderTop).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE}`);
expect(result.cssProps.borderBottom).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE}`);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderVertical (themeVar) 3", () => {
const PROP = "borderVertical";
const VALUE = `${THEME_ID} ${THEME_ID2} ${THEME_ID3}`;
const result = resolveLayoutProps({ [PROP]: VALUE });
expect(result.cssProps.borderTop).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE} ${THEME_ID3_VALUE}`);
expect(result.cssProps.borderBottom).toBe(`${THEME_ID_VALUE} ${THEME_ID2_VALUE} ${THEME_ID3_VALUE}`);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderTop overwrites borderVertical", () => {
const PROP = "borderVertical";
const VALUE = "1px solid red";
const result = resolveLayoutProps({ [PROP]: VALUE, borderTop: "2px solid blue" });
expect(result.cssProps.borderTop).toBe("2px solid blue");
expect(result.cssProps.borderBottom).toBe(VALUE);
expect(result.issues.has(PROP)).toBe(false);
});
it("borderBottom overwrites borderVertical", () => {
const PROP = "borderVertical";
const VALUE = "1px solid red";
const result = resolveLayoutProps({ [PROP]: VALUE, borderBottom: "2px solid blue" });
expect(result.cssProps.borderTop).toBe(VALUE);
expect(result.cssProps.borderBottom).toBe("2px solid blue");
expect(result.issues.has(PROP)).toBe(false);
});
});
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/interception/IndexedDb.ts:
--------------------------------------------------------------------------------
```typescript
import type { Table } from "dexie";
import Dexie from "dexie";
import { ReadOnlyCollection } from "../interception/ReadonlyCollection";
import type { IDatabase, TableDescriptor } from "./abstractions";
export class IndexedDb implements IDatabase {
// private repository: Record<string, Array<any>>;
// private maxIdsByCollections: Record<string, number> = {};
private db: Dexie | null = null;
[key: string]: unknown;
constructor(
private tables: Array<TableDescriptor> | undefined,
private initialData: Record<string, any[]> | (() => Promise<Record<string, any[]>>) = {},
private config?: any,
) {}
private getDb() {
if (this.db === null) {
throw new Error("Db is not initialized yet");
}
return this.db;
}
public async initialize() {
const resolvedInitialData =
typeof this.initialData === "function" ? await this.initialData() : this.initialData;
const schema: Record<string, string> = {};
const tableNames = new Set<string>();
if (this.tables) {
this.tables.forEach((tableDescriptor) => {
const schemaArray = [];
if (tableDescriptor.pk.length === 1) {
schemaArray.push(tableDescriptor.pk[0]);
} else {
schemaArray.push(`[${tableDescriptor.pk.join("+")}]`); //indexeddb compound index looks like this: [field+anotherfield]
}
if (tableDescriptor.indexes) {
schemaArray.push(...tableDescriptor.indexes);
}
schema[tableDescriptor.name] = schemaArray.join(", ");
tableNames.add(tableDescriptor.name);
});
} else {
Object.entries(resolvedInitialData).forEach(([key]) => {
schema[key] = "++id";
tableNames.add(key);
});
}
const targetVersion =
this.config?.version !== undefined && typeof this.config?.version === "number"
? this.config?.version
: 1;
const shouldInitializeData = await this.dropDbOnVersionChange(targetVersion);
this.db = this.createDbInstance();
this.db.version(targetVersion).stores(schema);
if (shouldInitializeData) {
await Promise.all(
Object.entries(resolvedInitialData).map(async ([key, value]) => {
try {
await this.getDb().table(key).bulkAdd(value);
} catch (ignored) {
console.error(ignored);
}
}),
);
}
tableNames.forEach((key) => {
this[`$${key}`] = createTableWrapper(this.getDb().table(key));
});
}
private createDbInstance() {
return new Dexie(this.config?.database ?? "defaultIndexDb");
}
private async dropDbOnVersionChange(targetVersion: number) {
const tempDb = this.createDbInstance();
if (!(await Dexie.exists(tempDb.name))) {
return true;
}
await tempDb.open();
if (tempDb.verno !== targetVersion) {
await tempDb.delete();
return true;
} else {
tempDb.close();
return false;
}
}
public getItems = (resourceId: string) => this.getDb().table(resourceId).toArray();
public findItems = async (resourceId: string, predicate: (item: any) => Promise<boolean>) => {
const ret = await this.getItems(resourceId);
const results = await Promise.all(ret.map(predicate));
return ret.filter((_v, index) => results[index]);
};
public getItem = async (resourceId: string, predicate: (item: any) => Promise<boolean>) => {
const ret = await this.getItems(resourceId);
const results = await Promise.all(ret.map(predicate));
return ret.find((_v, index) => results[index]);
};
public getItemById = async (resourceId: string, id: any) => {
return await this.getItem(resourceId, (item) => {
return Promise.resolve(item.id + "" === id + "");
});
};
public deleteItems = async (resourceId: string, predicate: (item: any) => Promise<boolean>) => {
// this.repository[resourceId] = this.repository[resourceId]?.filter((item) => !predicate(item));
};
public insertItem = async (resourceId: string, item: any) => {
const id = await this.getDb().table(resourceId).add(item);
return this.getItemById(resourceId, id);
};
public updateItem = async (resourceId: string, item: any) => {
await this.getDb().table(resourceId).update(item.id, item);
return await this.getItemById(resourceId, item.id);
};
// --- This method signifies that a section of operations is executed in a transaction
async transaction(actions: () => Promise<void>): Promise<void> {
if (!this.db) return;
const tables = this.db.tables;
await this.db.transaction("rw", tables, actions);
}
}
// Wraps an indexDb Table into an object that provides helpful methods
function createTableWrapper(table: Table): any {
// --- Function to retrieve the current table data
const getDataFn = () => table.db.table(table.name);
// --- Helper method to filter the table data
const filteredData = async (predicate?: (item: any) => Promise<boolean>) => {
const dataSnapshot = await table.toArray();
const results = await Promise.all(dataSnapshot.map(predicate ?? (() => Promise.resolve(true))));
return dataSnapshot.filter((_v, index) => results[index]);
};
return {
native: getDataFn,
insert: async (item: any) => {
const id = await table.add(item);
return getDataFn().get(id);
},
update: async (item: any) => {
await table.update(item.id, item);
return getDataFn().get(item.id);
},
save: async (item: any) => {
const key = await table.put(item);
return table.get(key);
},
deleteById: async (id: any) => {
await table.delete(id);
},
byId: async (id: any) => {
if (id === undefined || id === null) {
return null;
}
let safeId = id;
if (table.schema.primKey.src === "++id") {
//it's an auto incremented id, must be a number
safeId = Number(id);
}
return await table.get(safeId);
},
toArray: async () => await table.toArray(),
single: async (predicate: (item: any) => Promise<boolean>) =>
await new ReadOnlyCollection(await table.toArray()).single(predicate),
singleOrDefault: async (predicate: (item: any) => Promise<boolean>, defValue?: any) => {
return await new ReadOnlyCollection(await table.toArray()).singleOrDefault(
predicate,
defValue,
);
},
where: async (predicate: (item: any) => Promise<boolean>) =>
new ReadOnlyCollection(await filteredData(predicate)),
whereAsArray: async (predicate: (item: any) => Promise<boolean>) =>
await filteredData(predicate),
orderBy: async (...mappers: any[]) =>
await new ReadOnlyCollection(await table.toArray()).orderBy(...mappers),
orderByAsArray: async (...mappers: any[]) =>
await new ReadOnlyCollection(await table.toArray()).orderByAsArray(...mappers),
groupBy: async (groupKey: (item: any) => Promise<any>) =>
await new ReadOnlyCollection(await table.toArray()).groupBy(groupKey),
groupByAsArray: async (groupKey: (item: any) => Promise<any>) =>
await new ReadOnlyCollection(await table.toArray()).groupByAsArray(groupKey),
distinct: async (distinctValue?: (item: any) => Promise<any>) =>
await new ReadOnlyCollection(await table.toArray()).distinct(distinctValue),
distinctAsArray: async (distinctValue?: (item: any) => Promise<any>) =>
await new ReadOnlyCollection(await table.toArray()).distinctAsArray(distinctValue),
maxValue: async (fieldName = "id", predicate?: (item: any) => Promise<boolean>) => {
return await new ReadOnlyCollection(await table.toArray()).maxValue(fieldName, predicate);
},
skip: async (count: number) => {
return await new ReadOnlyCollection(await table.toArray()).skip(count);
},
take: async (count: number) => {
return await new ReadOnlyCollection(await table.toArray()).take(count);
},
skipTake: async (skip: number, take: number) => {
return await new ReadOnlyCollection(await table.toArray()).skipTake(skip, take);
},
};
}
```
--------------------------------------------------------------------------------
/xmlui/src/components/IconRegistryContext.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { useCallback, useContext } from "react";
import type { IconRegistry } from "./IconProvider";
import { useIsomorphicLayoutEffect } from "../components-core/utils/hooks";
export const IconRegistryContext = React.createContext<IconRegistry | null>(null);
export function useIconRegistry() {
return useContext(IconRegistryContext)!;
}
//https://github.com/hatashiro/react-attr-converter/tree/master
const svgAttributeMap: Record<string, string> = {
// SVG attributes
accentheight: "accentHeight",
accumulate: "accumulate",
additive: "additive",
alignmentbaseline: "alignmentBaseline",
allowreorder: "allowReorder",
alphabetic: "alphabetic",
amplitude: "amplitude",
arabicform: "arabicForm",
ascent: "ascent",
attributename: "attributeName",
attributetype: "attributeType",
autoreverse: "autoReverse",
azimuth: "azimuth",
basefrequency: "baseFrequency",
baseprofile: "baseProfile",
baselineshift: "baselineShift",
bbox: "bbox",
begin: "begin",
bias: "bias",
by: "by",
calcmode: "calcMode",
capheight: "capHeight",
clip: "clip",
clippath: "clipPath",
clippathunits: "clipPathUnits",
cliprule: "clipRule",
colorinterpolation: "colorInterpolation",
colorinterpolationfilters: "colorInterpolationFilters",
colorprofile: "colorProfile",
colorrendering: "colorRendering",
contentscripttype: "contentScriptType",
contentstyletype: "contentStyleType",
cursor: "cursor",
cx: "cx",
cy: "cy",
d: "d",
decelerate: "decelerate",
descent: "descent",
diffuseconstant: "diffuseConstant",
direction: "direction",
display: "display",
divisor: "divisor",
dominantbaseline: "dominantBaseline",
dur: "dur",
dx: "dx",
dy: "dy",
edgemode: "edgeMode",
elevation: "elevation",
enablebackground: "enableBackground",
end: "end",
exponent: "exponent",
externalresourcesrequired: "externalResourcesRequired",
fill: "fill",
fillopacity: "fillOpacity",
fillrule: "fillRule",
filter: "filter",
filterres: "filterRes",
filterunits: "filterUnits",
floodcolor: "floodColor",
floodopacity: "floodOpacity",
focusable: "focusable",
fontfamily: "fontFamily",
fontsize: "fontSize",
fontsizeadjust: "fontSizeAdjust",
fontstretch: "fontStretch",
fontstyle: "fontStyle",
fontvariant: "fontVariant",
fontweight: "fontWeight",
format: "format",
from: "from",
fx: "fx",
fy: "fy",
g1: "g1",
g2: "g2",
glyphname: "glyphName",
glyphorientationhorizontal: "glyphOrientationHorizontal",
glyphorientationvertical: "glyphOrientationVertical",
glyphref: "glyphRef",
gradienttransform: "gradientTransform",
gradientunits: "gradientUnits",
hanging: "hanging",
horizadvx: "horizAdvX",
horizoriginx: "horizOriginX",
ideographic: "ideographic",
imagerendering: "imageRendering",
in: "in",
in2: "in2",
intercept: "intercept",
k: "k",
k1: "k1",
k2: "k2",
k3: "k3",
k4: "k4",
kernelmatrix: "kernelMatrix",
kernelunitlength: "kernelUnitLength",
kerning: "kerning",
keypoints: "keyPoints",
keysplines: "keySplines",
keytimes: "keyTimes",
lengthadjust: "lengthAdjust",
letterspacing: "letterSpacing",
lightingcolor: "lightingColor",
limitingconeangle: "limitingConeAngle",
local: "local",
markerend: "markerEnd",
markerheight: "markerHeight",
markermid: "markerMid",
markerstart: "markerStart",
markerunits: "markerUnits",
markerwidth: "markerWidth",
mask: "mask",
maskcontentunits: "maskContentUnits",
maskunits: "maskUnits",
mathematical: "mathematical",
mode: "mode",
numoctaves: "numOctaves",
offset: "offset",
opacity: "opacity",
operator: "operator",
order: "order",
orient: "orient",
orientation: "orientation",
origin: "origin",
overflow: "overflow",
overlineposition: "overlinePosition",
overlinethickness: "overlineThickness",
paintorder: "paintOrder",
panose1: "panose1",
pathlength: "pathLength",
patterncontentunits: "patternContentUnits",
patterntransform: "patternTransform",
patternunits: "patternUnits",
pointerevents: "pointerEvents",
points: "points",
pointsatx: "pointsAtX",
pointsaty: "pointsAtY",
pointsatz: "pointsAtZ",
preservealpha: "preserveAlpha",
preserveaspectratio: "preserveAspectRatio",
primitiveunits: "primitiveUnits",
r: "r",
radius: "radius",
refx: "refX",
refy: "refY",
renderingintent: "renderingIntent",
repeatcount: "repeatCount",
repeatdur: "repeatDur",
requiredextensions: "requiredExtensions",
requiredfeatures: "requiredFeatures",
restart: "restart",
result: "result",
rotate: "rotate",
rx: "rx",
ry: "ry",
scale: "scale",
seed: "seed",
shaperendering: "shapeRendering",
slope: "slope",
spacing: "spacing",
specularconstant: "specularConstant",
specularexponent: "specularExponent",
speed: "speed",
spreadmethod: "spreadMethod",
startoffset: "startOffset",
stddeviation: "stdDeviation",
stemh: "stemh",
stemv: "stemv",
stitchtiles: "stitchTiles",
stopcolor: "stopColor",
stopopacity: "stopOpacity",
strikethroughposition: "strikethroughPosition",
strikethroughthickness: "strikethroughThickness",
string: "string",
stroke: "stroke",
strokedasharray: "strokeDasharray",
strokedashoffset: "strokeDashoffset",
strokelinecap: "strokeLinecap",
strokelinejoin: "strokeLinejoin",
strokemiterlimit: "strokeMiterlimit",
strokeopacity: "strokeOpacity",
strokewidth: "strokeWidth",
surfacescale: "surfaceScale",
systemlanguage: "systemLanguage",
tablevalues: "tableValues",
targetx: "targetX",
targety: "targetY",
textanchor: "textAnchor",
textdecoration: "textDecoration",
textlength: "textLength",
textrendering: "textRendering",
to: "to",
transform: "transform",
u1: "u1",
u2: "u2",
underlineposition: "underlinePosition",
underlinethickness: "underlineThickness",
unicode: "unicode",
unicodebidi: "unicodeBidi",
unicoderange: "unicodeRange",
unitsperem: "unitsPerEm",
valphabetic: "vAlphabetic",
vhanging: "vHanging",
videographic: "vIdeographic",
vmathematical: "vMathematical",
values: "values",
vectoreffect: "vectorEffect",
version: "version",
vertadvy: "vertAdvY",
vertoriginx: "vertOriginX",
vertoriginy: "vertOriginY",
viewbox: "viewBox",
viewtarget: "viewTarget",
visibility: "visibility",
widths: "widths",
wordspacing: "wordSpacing",
writingmode: "writingMode",
x: "x",
x1: "x1",
x2: "x2",
xchannelselector: "xChannelSelector",
xheight: "xHeight",
xlinkactuate: "xlinkActuate",
xlinkarcrole: "xlinkArcrole",
xlinkhref: "xlinkHref",
xlinkrole: "xlinkRole",
xlinkshow: "xlinkShow",
xlinktitle: "xlinkTitle",
xlinktype: "xlinkType",
xmlns: "xmlns",
xmlnsxlink: "xmlnsXlink",
xmlbase: "xmlBase",
xmllang: "xmlLang",
xmlspace: "xmlSpace",
y: "y",
y1: "y1",
y2: "y2",
ychannelselector: "yChannelSelector",
z: "z",
zoomandpan: "zoomAndPan",
};
const extraCharRegex = /[-:]/g;
export function useCustomSvgIconRenderer(resourceUrl?: string) {
const { ensureCustomSvgIcon, customSvgs } = useIconRegistry();
useIsomorphicLayoutEffect(() => {
if (!resourceUrl) {
return;
}
void ensureCustomSvgIcon(resourceUrl);
}, [ensureCustomSvgIcon, resourceUrl]);
const customSvg = resourceUrl ? customSvgs[resourceUrl] : null;
const iconRenderer = useCallback(
({ style, className }: any) => {
if (!customSvg) {
return null;
}
const { attributes, name } = customSvg;
const safeAttributes: any = {};
Object.entries(attributes).forEach(([key, value]) => {
let safeKey = key;
if (/^(data-|aria-)/.test(key)) {
safeKey = key;
} else {
safeKey = key.replace(extraCharRegex, "").toLowerCase();
}
safeAttributes[svgAttributeMap[safeKey] || key] = value;
});
return (
<svg {...safeAttributes} style={style} className={className}>
<use href={`#${name}`} />
</svg>
);
},
[customSvg],
);
return !resourceUrl ? null : iconRenderer;
}
```