This is page 7 of 179. Use http://codebase.md/xmlui-org/xmlui?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ ├── cold-items-taste.md │ ├── config.json │ ├── empty-spiders-dress.md │ ├── shy-windows-allow.md │ ├── sour-coins-read.md │ ├── tame-zebras-invite.md │ ├── three-ideas-invent.md │ ├── twenty-jeans-watch.md │ ├── warm-spies-melt.md │ └── whole-ways-cry.md ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── 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 │ │ │ └── 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 │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.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 │ │ │ │ ├── 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 │ ├── actions.md │ ├── AppRoot.md │ ├── component-apis.md │ ├── component-rendering.md │ ├── component-review-checklist.md │ ├── containers.md │ ├── data-sources.md │ ├── e2e-summary.md │ ├── expression-evaluation.md │ ├── glossary.md │ ├── helper-components.md │ ├── index.md │ ├── loaders.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 │ ├── rendering-fundamentals.md │ ├── reusable-components.md │ ├── standalone-apps.md │ ├── state-management.md │ └── xmlui-extensibility.xlsx ├── 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 │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.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 │ │ │ ├── BehaviorContext.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 │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /xmlui/src/components/Logo/Logo.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { createMetadata } from "../metadata-helpers"; 3 | import { Logo, defaultProps } from "./LogoNative"; 4 | 5 | const COMP = "Logo"; 6 | 7 | export const LogoMd = createMetadata({ 8 | status: "stable", 9 | description: 10 | "`Logo` displays your application's brand symbol by automatically loading logo " + 11 | "images defined in the app manifest. While logos are typically configured " + 12 | "using App-level properties (`logo`, `logo-dark`), this component provides " + 13 | "direct control when you need custom logo placement or templating.", 14 | props: { 15 | alt: { 16 | description: "Alternative text for the logo image for accessibility.", 17 | type: "string", 18 | defaultValue: defaultProps.alt, 19 | }, 20 | inline: { 21 | description: `When set to true, the image will be displayed as an inline element instead of a block element.`, 22 | type: "boolean", 23 | defaultValue: defaultProps.inline, 24 | }, 25 | }, 26 | }); 27 | 28 | export const logoComponentRenderer = createComponentRenderer( 29 | COMP, 30 | LogoMd, 31 | ({ node, className, extractValue }) => { 32 | return ( 33 | <Logo 34 | className={className} 35 | inline={extractValue.asOptionalBoolean(node.props.inline)} 36 | alt={extractValue(node.props.alt)} 37 | /> 38 | ); 39 | }, 40 | ); 41 | ``` -------------------------------------------------------------------------------- /docs/public/pages/hosted-deployment.md: -------------------------------------------------------------------------------- ```markdown 1 | # Hosted deployment 2 | 3 | You can copy the structure shown in [App Structure](/app-structure) to any webserver configured to serve `index.html` and its subfolders. 4 | 5 | Here are some common alternatives. 6 | 7 | ## Use an AWS Bucket 8 | 9 | Your app can run from an AWS bucket. Here's the minimal app served that way: [http://xmlui-minimal.s3-website-us-east-1.amazonaws.com/](http://xmlui-minimal.s3-website-us-east-1.amazonaws.com/). 10 | 11 | > [!INFO] **AWS recipe** 12 | > - Upload xmlui-minimal folder to an AWS bucket 13 | > - In Permissions, turn off the 'Block public access' setting 14 | > - In Permissions, allow 'PublicRead' 15 | > ```json 16 | > { 17 | > "Version": "2012-10-17", 18 | > "Statement": [ 19 | > { 20 | > "Sid": "PublicRead", 21 | > "Effect": "Allow", 22 | > "Principal": "*", 23 | > "Action": "s3:GetObject", 24 | > "Resource": "arn:aws:s3:::xmlui-minimal/*" 25 | > } 26 | > ] 27 | > } 28 | > ``` 29 | > - In Properties, turn on 'Static website hosting' and set 'index.html' as the default 30 | 31 | ## Use Netlify 32 | 33 | You can just drag-and-drop the folder to Netlify: [https://xmlui-minimal.netlify.app/](https://xmlui-minimal.netlify.app/). 34 | 35 | > [!INFO] **Netlify recipe** 36 | > - Go to <a href="https://app.netlify.com/drop">https://app.netlify.com/drop</a> 37 | > - Drag the xmlui-minimal folder to the drop target 38 | > - Rename the appIn Permissions, allow 'PublicRead' 39 | ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/PlaygroundContent.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./PlaygroundContent.module.scss"; 2 | import { usePlayground } from "../hooks/usePlayground"; 3 | import classnames from "classnames"; 4 | import { Preview } from "./Preview"; 5 | import { Splitter } from "xmlui"; 6 | import { Editor } from "./Editor"; 7 | 8 | type PlaygroundContentProps = { 9 | height?: number | string; 10 | initialPrimarySize?: string; 11 | standalone?: boolean; 12 | }; 13 | 14 | export const PlaygroundContent = ({ 15 | height, 16 | initialPrimarySize, 17 | standalone, 18 | }: PlaygroundContentProps) => { 19 | const { options, status, editorStatus } = usePlayground(); 20 | 21 | return ( 22 | <div 23 | className={classnames(styles.playgroundContent, { 24 | [styles.standaloneMode]: standalone, 25 | })} 26 | style={{ height }} 27 | > 28 | {status === "loaded" && 29 | (standalone ? ( 30 | options.previewMode ? ( 31 | <Preview /> 32 | ) : ( 33 | <Splitter 34 | orientation={options.orientation} 35 | swapped={options.swapped} 36 | initialPrimarySize={initialPrimarySize} 37 | > 38 | <Editor /> 39 | <div style={{ height: "100%", width: "100%", overflow: "hidden" }}> 40 | {editorStatus === "loaded" && <Preview />} 41 | </div> 42 | </Splitter> 43 | ) 44 | ) : ( 45 | <Preview /> 46 | ))} 47 | </div> 48 | ); 49 | }; 50 | ``` -------------------------------------------------------------------------------- /tools/create-app/CHANGELOG.md: -------------------------------------------------------------------------------- ```markdown 1 | # create-xmlui-app 2 | 3 | ## 0.10.19 4 | 5 | ## 0.10.18 6 | 7 | ## 0.10.17 8 | 9 | ## 0.10.16 10 | 11 | ## 0.10.15 12 | 13 | ## 0.10.14 14 | 15 | ## 0.10.13 16 | 17 | ## 0.10.12 18 | 19 | ## 0.10.11 20 | 21 | ## 0.10.10 22 | 23 | ## 0.10.9 24 | 25 | ## 0.10.8 26 | 27 | ## 0.10.7 28 | 29 | ## 0.10.6 30 | 31 | ## 0.10.5 32 | 33 | ## 0.10.4 34 | 35 | ## 0.10.3 36 | 37 | ## 0.10.2 38 | 39 | ## 0.10.1 40 | 41 | ## 0.10.0 42 | 43 | ## 0.9.101 44 | 45 | ## 0.9.100 46 | 47 | ## 0.9.99 48 | 49 | ## 0.9.98 50 | 51 | ## 0.9.97 52 | 53 | ## 0.9.96 54 | 55 | ## 0.9.95 56 | 57 | ## 0.9.94 58 | 59 | ## 0.9.93 60 | 61 | ## 0.9.92 62 | 63 | ## 0.9.91 64 | 65 | ## 0.9.90 66 | 67 | ## 0.9.89 68 | 69 | ## 0.9.88 70 | 71 | ## 0.9.87 72 | 73 | ## 0.9.86 74 | 75 | ## 0.9.85 76 | 77 | ## 0.9.84 78 | 79 | ## 0.9.83 80 | 81 | ## 0.9.82 82 | 83 | ## 0.9.81 84 | 85 | ## 0.9.80 86 | 87 | ## 0.9.79 88 | 89 | ## 0.9.78 90 | 91 | ## 0.9.77 92 | 93 | ## 0.9.76 94 | 95 | ## 0.9.75 96 | 97 | ## 0.9.74 98 | 99 | ## 0.9.73 100 | 101 | ## 0.9.72 102 | 103 | ## 0.9.71 104 | 105 | ## 0.9.70 106 | 107 | ## 0.9.69 108 | 109 | ## 0.9.68 110 | 111 | ## 0.9.67 112 | 113 | ## 0.9.66 114 | 115 | ## 0.9.65 116 | 117 | ## 0.9.64 118 | 119 | ## 0.9.63 120 | 121 | ## 0.9.62 122 | 123 | ## 0.9.61 124 | 125 | ## 0.9.60 126 | 127 | ## 0.9.59 128 | 129 | ## 0.9.58 130 | 131 | ## 0.9.57 132 | 133 | ## 0.9.56 134 | 135 | ## 0.9.55 136 | 137 | ## 0.9.54 138 | 139 | ## 0.9.53 140 | 141 | ## 0.9.52 142 | 143 | ## 0.9.51 144 | 145 | ## 0.9.50 146 | 147 | ## 0.9.49 148 | 149 | ## 0.9.48 150 | 151 | ## 0.9.47 152 | 153 | ## 0.9.46 154 | 155 | ## 0.9.45 156 | 157 | ## 0.9.44 158 | 159 | ## 0.9.43 160 | 161 | ## 0.9.42 162 | 163 | ## 0.9.41 164 | 165 | ## 0.9.40 166 | 167 | ## 0.9.39 168 | 169 | ## 0.9.38 170 | 171 | ### Patch Changes 172 | 173 | - d314bad: msw update 174 | 175 | ## 0.9.37 176 | 177 | ## 0.9.36 178 | 179 | ## 0.9.35 180 | 181 | ## 0.9.34 182 | 183 | ## 0.9.33 184 | 185 | ## 0.9.32 186 | 187 | ## 0.9.31 188 | 189 | ## 0.9.30 190 | 191 | ### Patch Changes 192 | 193 | - b0ae113: testing 194 | 195 | ## 0.9.29 196 | 197 | ### Patch Changes 198 | 199 | - f15c018: another testing 200 | - f15c018: testing 201 | 202 | ## 0.9.28 203 | 204 | ### Patch Changes 205 | 206 | - 421968b: testing 207 | 208 | ## 0.9.27 209 | 210 | ### Patch Changes 211 | 212 | - 99bba69: testing 213 | 214 | ## 0.9.26 215 | ``` -------------------------------------------------------------------------------- /docs/public/pages/xmlui-animations/FadeAnimation.md: -------------------------------------------------------------------------------- ```markdown 1 | # FadeAnimation [#fadeanimation] 2 | 3 | >[!WARNING] 4 | > This component is in an **experimental** state; you can use it in your app. However, we may modify it, and it may even have breaking changes in the future.The `FadeAnimation` component represents an animation that fades the content with adjustable opacity values. 5 | 6 | ## Properties 7 | 8 | ### `animateWhenInView` 9 | 10 | Indicates whether the animation should start when the component is in view 11 | 12 | ### `delay (default: 0)` 13 | 14 | The delay before the animation starts in milliseconds 15 | 16 | ### `duration` 17 | 18 | The duration of the animation in milliseconds 19 | 20 | ### `from (default: 0)` 21 | 22 | Sets the initial opacity of the content.If the `to` property is not set, the initial opacity set here will be used as the final opacity. 23 | 24 | ### `loop (default: false)` 25 | 26 | Indicates whether the animation should loop 27 | 28 | ### `reverse (default: false)` 29 | 30 | Indicates whether the animation should run in reverse 31 | 32 | ### `to (default: 1)` 33 | 34 | Sets the final opacity of the content.If the `from` property is not set, the initial opacity set here will be used as the final opacity. 35 | 36 | ## Events 37 | 38 | ### `started` 39 | 40 | Event fired when the animation starts 41 | 42 | ### `stopped` 43 | 44 | Event fired when the animation stops 45 | 46 | ## Exposed Methods 47 | 48 | ### `start` 49 | 50 | Starts the animation 51 | 52 | ### `stop` 53 | 54 | Stops the animation 55 | 56 | ## Styling 57 | 58 | This component does not have any styles. 59 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ModalDialog/Dialog.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type React from "react"; 2 | 3 | import styles from "./Dialog.module.scss"; 4 | 5 | import { Text } from "../Text/TextNative"; 6 | import { ModalDialog } from "./ModalDialogNative"; 7 | 8 | export type Props = { 9 | title?: string; 10 | description?: string; 11 | isOpen?: boolean; 12 | onClose: () => (Promise<boolean | undefined | void> | boolean | undefined | void); 13 | buttons: React.ReactNode; 14 | // Accept any React component - provides a way to add custom content to the dialog, 15 | // like complex layouts, images, etc. 16 | children?: React.ReactNode; 17 | portalTo?: HTMLElement; 18 | }; 19 | 20 | /** 21 | * Dialog component that is customizable with action buttons. The children prop accepts React elements. 22 | * 23 | * Note that clicking outside of the dialog or pressing Escape will call the onClose function. 24 | * Thus, if you want to stop that from firing, do so in the onClose function from outside. 25 | */ 26 | export const Dialog = ({ 27 | title, 28 | description, 29 | children, 30 | isOpen, 31 | onClose, 32 | buttons, 33 | }: Props) => { 34 | return ( 35 | <ModalDialog onClose={onClose} isInitiallyOpen={isOpen} title={title}> 36 | <div className={styles.dialogContent}> 37 | <div id="dialogDesc"> 38 | <Text>{description}</Text> 39 | </div> 40 | {children} 41 | </div> 42 | {!!buttons && <footer className={styles.dialogActions}>{buttons}</footer>} 43 | </ModalDialog> 44 | ); 45 | }; 46 | ``` -------------------------------------------------------------------------------- /docs/content/components/SpaceFiller.md: -------------------------------------------------------------------------------- ```markdown 1 | # SpaceFiller [#spacefiller] 2 | 3 | `SpaceFiller` works well in layout containers to fill remaining (unused) space. Its behavior depends on the layout container in which it is used. 4 | 5 | ## Using `SpaceFiller` [#using-spacefiller] 6 | 7 | In a `Stack`, `SpaceFiller` pushes the children following it to the other end of the container: 8 | 9 | ```xmlui-pg copy display {3} name="SpaceFiller in an HStack" 10 | <App> 11 | <HStack> 12 | <Stack width="36px" height="36px" backgroundColor="red" /> 13 | <SpaceFiller /> 14 | <Stack width="36px" height="36px" backgroundColor="blue" /> 15 | </HStack> 16 | </App> 17 | ``` 18 | 19 | In a `FlowLayout`, `SpaceFiller` acts as a line break for a row. The children following the `SpaceFiller` enters a new line. 20 | 21 | ```xmlui-pg copy display {3} name="Example: in a FlowLayout" 22 | <App> 23 | <FlowLayout> 24 | <Stack width="20%" height="36px" backgroundColor="red" /> 25 | <SpaceFiller /> 26 | <Stack width="20%" height="36px" backgroundColor="green" /> 27 | <Stack width="20%" height="36px" backgroundColor="blue" /> 28 | </FlowLayout> 29 | </App> 30 | ``` 31 | 32 | ## Properties [#properties] 33 | 34 | This component does not have any properties. 35 | 36 | ## Events [#events] 37 | 38 | This component does not have any events. 39 | 40 | ## Exposed Methods [#exposed-methods] 41 | 42 | This component does not expose any methods. 43 | 44 | ## Styling [#styling] 45 | 46 | `SpaceFiller` ignores all layout properties; it cannot be styled. 47 | ``` -------------------------------------------------------------------------------- /xmlui/scripts/get-langserver-metadata.mjs: -------------------------------------------------------------------------------- ``` 1 | import { collectedComponentMetadata } from "../dist/metadata/xmlui-metadata.mjs"; 2 | generateLangServerMetadata(collectedComponentMetadata); 3 | 4 | /** 5 | * @typedef {import('../src/language-server/services/common').ComponentMetadataCollection} ComponentMetadataCollection 6 | * @typedef {import('../src/abstractions/ComponentDefs').ComponentMetadata} ComponentMetadata 7 | */ 8 | 9 | /** 10 | * 11 | * @param {Record<string, ComponentMetadata} metaByComp 12 | */ 13 | function generateLangServerMetadata(metaByComp) { 14 | /** @type {ComponentMetadataCollection} */ 15 | const simplifiedMetadata = {}; 16 | for (const [compName, componentData] of Object.entries(metaByComp)) { 17 | simplifiedMetadata[compName] = { 18 | description: componentData.description, 19 | status: componentData.status, 20 | props: componentData.props, 21 | events: componentData.events, 22 | apis: componentData.apis, 23 | contextVars: componentData.contextVars, 24 | allowArbitraryProps: componentData.allowArbitraryProps, 25 | shortDescription: componentData.shortDescription, 26 | }; 27 | } 28 | 29 | const fileContent = `// This file is auto-generated. Do not edit manually. 30 | export default ${JSON.stringify(metaByComp, null, 2)}; 31 | `; 32 | 33 | process.stdout.write(fileContent, (err) => { 34 | if (err) { 35 | console.error(`Could not write generated metadata to stdout:\n ${err.message}`); 36 | } 37 | }); 38 | } 39 | ``` -------------------------------------------------------------------------------- /tools/create-app/helpers/git.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { execSync } from "child_process"; 2 | import path from "path"; 3 | import fs from "fs"; 4 | 5 | function isInGitRepository(): boolean { 6 | try { 7 | execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" }); 8 | return true; 9 | } catch (_) {} 10 | return false; 11 | } 12 | 13 | function isInMercurialRepository(): boolean { 14 | try { 15 | execSync("hg --cwd . root", { stdio: "ignore" }); 16 | return true; 17 | } catch (_) {} 18 | return false; 19 | } 20 | 21 | function isDefaultBranchSet(): boolean { 22 | try { 23 | execSync("git config init.defaultBranch", { stdio: "ignore" }); 24 | return true; 25 | } catch (_) {} 26 | return false; 27 | } 28 | 29 | export function tryGitInit(root: string): boolean { 30 | let didInit = false; 31 | try { 32 | execSync("git --version", { stdio: "ignore" }); 33 | if (isInGitRepository() || isInMercurialRepository()) { 34 | return false; 35 | } 36 | 37 | execSync("git init", { stdio: "ignore" }); 38 | didInit = true; 39 | 40 | if (!isDefaultBranchSet()) { 41 | execSync("git checkout -b main", { stdio: "ignore" }); 42 | } 43 | 44 | execSync("git add -A", { stdio: "ignore" }); 45 | execSync('git commit -m "Initial commit from Create Next App"', { 46 | stdio: "ignore", 47 | }); 48 | return true; 49 | } catch (e) { 50 | if (didInit) { 51 | try { 52 | fs.rmSync(path.join(root, ".git"), { recursive: true, force: true }); 53 | } catch (_) {} 54 | } 55 | return false; 56 | } 57 | } 58 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/rendering/containers.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * This enumeration defines the available actions on a view container. We use these actions in the redux-style state 3 | * management of the component rendering engine. 4 | */ 5 | export const enum ContainerActionKind { 6 | LOADER_LOADED = "ContainerActionKind:LOADER_LOADED", 7 | LOADER_IN_PROGRESS_CHANGED = "ContainerActionKind:LOADER_IN_PROGRESS_CHANGED", 8 | LOADER_IS_REFETCHING_CHANGED = "ContainerActionKind:LOADER_IS_REFETCHING_CHANGED", 9 | LOADER_ERROR = "ContainerActionKind:LOADER_ERROR", 10 | EVENT_HANDLER_STARTED = "ContainerActionKind:EVENT_HANDLER_STARTED", 11 | EVENT_HANDLER_COMPLETED = "ContainerActionKind:EVENT_HANDLER_COMPLETED", 12 | EVENT_HANDLER_ERROR = "ContainerActionKind:EVENT_HANDLER_ERROR", 13 | COMPONENT_STATE_CHANGED = "ContainerActionKind:COMPONENT_STATE_CHANGED", 14 | STATE_PART_CHANGED = "ContainerActionKind:STATE_PART_CHANGED", 15 | } 16 | 17 | // Represents a reducer action dispatched in a view container 18 | export interface ContainerAction { 19 | type: ContainerActionKind; 20 | // Potential improvement: Try to specify the type with more details 21 | payload: { 22 | uid?: any; 23 | data?: any; 24 | error?: any; 25 | value?: any; 26 | byId?: any; 27 | inProgress?: any; 28 | isRefetching?: any; 29 | loaded?: any; 30 | pageInfo?: any; 31 | path?: any; 32 | target?: any; 33 | actionType?: any; 34 | state?: any; 35 | eventName?: any; 36 | localVars?: any; 37 | }; 38 | } 39 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Input/InputDivider.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type React from "react"; 2 | import classnames from "classnames"; 3 | import styles from "./InputDivider.module.scss"; 4 | 5 | /** 6 | * Props for InputDivider component. 7 | */ 8 | export interface InputDividerProps { 9 | /** The separator character/string to display */ 10 | separator: string; 11 | /** Optional custom className for styling and spacing */ 12 | className?: string; 13 | } 14 | 15 | /** 16 | * InputDivider is a simple component for displaying separators between input fields. 17 | * 18 | * This component is designed to be used in multi-field input scenarios like TimeInput 19 | * and DateInput where visual separators (like ":", "/", "-") are needed between fields. 20 | * 21 | * Key features: 22 | * - Flexible separator content (single or multi-character strings) 23 | * - CSS class-based styling for maximum flexibility 24 | * - Consistent behavior across different input components 25 | * 26 | * @example 27 | * ```tsx 28 | * // Time separator 29 | * <InputDivider separator=":" className={styles.divider} /> 30 | * 31 | * // Date separator 32 | * <InputDivider separator="/" className={styles.divider} /> 33 | * 34 | * // Custom separator 35 | * <InputDivider separator=" • " className="custom-separator" /> 36 | * ``` 37 | */ 38 | export function InputDivider({ 39 | separator, 40 | className 41 | }: InputDividerProps): React.ReactElement { 42 | return ( 43 | <span className={classnames(styles.inputDivider, className)}> 44 | {separator} 45 | </span> 46 | ); 47 | } 48 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Items/ItemsNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { Fragment, type ReactNode, useMemo } from "react"; 2 | import { isPlainObject } from "lodash-es"; 3 | 4 | // ===================================================================================================================== 5 | // React Items component implementation 6 | 7 | type Props = { 8 | items: any[]; 9 | renderItem: (contextVars: any, key: number) => ReactNode; 10 | reverse?: boolean; 11 | }; 12 | 13 | export const defaultProps: Pick<Props, "reverse"> = { 14 | reverse: false, 15 | }; 16 | 17 | export function Items({ items, renderItem, reverse = defaultProps.reverse }: Props) { 18 | const itemsToRender = useMemo(() => { 19 | if (!items) { 20 | return []; 21 | } 22 | let normalizedItems = items; 23 | if (isPlainObject(items)) { 24 | normalizedItems = Object.values(items); 25 | } 26 | return reverse ? [...normalizedItems].reverse() : normalizedItems; 27 | }, [items, reverse]); 28 | 29 | if (!itemsToRender || !Array.isArray(itemsToRender)) { 30 | return null; 31 | } 32 | 33 | return ( 34 | <> 35 | {itemsToRender.map((item, index) => { 36 | return ( 37 | <Fragment key={index}> 38 | {renderItem?.( 39 | { 40 | $item: item, 41 | $itemIndex: index, 42 | $isFirst: index === 0, 43 | $isLast: index === itemsToRender.length - 1, 44 | }, 45 | index, 46 | )} 47 | </Fragment> 48 | ); 49 | })} 50 | </> 51 | ); 52 | } 53 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/interception/apiInterceptorWorker.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { SetupWorker } from "msw/browser"; 2 | import { setupWorker } from "msw/browser"; 3 | import type { RequestHandler } from "msw"; 4 | import { http } from "msw"; 5 | import { isArray } from "lodash-es"; 6 | 7 | import type { ApiInterceptor } from "./ApiInterceptor"; 8 | 9 | // Create handlers for the specified API interceptor 10 | function createHandlers(api: ApiInterceptor) { 11 | const operations = api.getOperations(); 12 | let handlers: Array<RequestHandler> = []; 13 | Object.entries(operations).forEach(([operationId, operation]) => { 14 | let urls = [operation.url]; 15 | if (isArray(operation.url)) { 16 | urls = operation.url; 17 | } 18 | urls.forEach((operationUrl) => { 19 | handlers.push( 20 | http[operation.method]( 21 | `${api.getApiUrl()}${operationUrl}`, 22 | async ({ request, cookies, params }) => { 23 | return await api.executeOperation(operationId, request, cookies, params); 24 | }, 25 | ), 26 | ); 27 | }); 28 | }); 29 | return handlers; 30 | } 31 | 32 | // Create the worker for the ApiInterceptorProvider 33 | export const createApiInterceptorWorker = ( 34 | apiInstance: ApiInterceptor, 35 | parentWorker?: SetupWorker, 36 | ) => { 37 | const handlers = createHandlers(apiInstance); 38 | let worker = parentWorker; 39 | if (!parentWorker) { 40 | worker = setupWorker(); 41 | } 42 | // https://github.com/mswjs/msw/issues/2495 43 | worker.use(...(handlers as any)); 44 | return worker; 45 | }; 46 | ``` -------------------------------------------------------------------------------- /docs/content/components/Backdrop.md: -------------------------------------------------------------------------------- ```markdown 1 | # Backdrop [#backdrop] 2 | 3 | The `Backdrop` component is a semi-transparent overlay that appears on top of its child component to obscure or highlight the content behind it. 4 | 5 | ```xmlui-pg copy display name="Example: using Backdrop" 6 | <App> 7 | <Backdrop opacity="0.6"> 8 | <Image 9 | src="/resources/images/components/image/breakfast.jpg" 10 | fit="cover" width="400px" /> 11 | <property name="overlayTemplate"> 12 | <VStack verticalAlignment="center" height="100px"> 13 | <H1 color="white" textAlign="center">Great breakfast!</H1> 14 | </VStack> 15 | </property> 16 | </Backdrop> 17 | </App> 18 | ``` 19 | 20 | ## Properties [#properties] 21 | 22 | ### `backgroundColor` [#backgroundcolor] 23 | 24 | The background color of the backdrop. 25 | 26 | ### `opacity` [#opacity] 27 | 28 | The opacity of the backdrop. 29 | 30 | ### `overlayTemplate` [#overlaytemplate] 31 | 32 | This property defines the component template for an optional overlay to display over the component. 33 | 34 | ## Events [#events] 35 | 36 | This component does not have any events. 37 | 38 | ## Exposed Methods [#exposed-methods] 39 | 40 | This component does not expose any methods. 41 | 42 | ## Styling [#styling] 43 | 44 | ### Theme Variables [#theme-variables] 45 | 46 | | Variable | Default Value (Light) | Default Value (Dark) | 47 | | --- | --- | --- | 48 | | [backgroundColor](../styles-and-themes/common-units/#color)-Backdrop | black | black | 49 | | [opacity](../styles-and-themes/common-units/#opacity)-Backdrop | 0.1 | 0.1 | 50 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/SelectionStore/SelectionStore.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { createMetadata } from "../metadata-helpers"; 3 | import { SelectionStore, defaultProps } from "./SelectionStoreNative"; 4 | 5 | const COMP = "SelectionStore"; 6 | 7 | export const SelectionStoreMd = createMetadata({ 8 | status: "deprecated", 9 | description: 10 | `The \`${COMP}\` is a non-visual component that may wrap components (items) and manage ` + 11 | `their selection state to accommodate the usage of other actions.`, 12 | props: { 13 | idKey: { 14 | description: `The selected items in the selection store needs to have a unique ID to use as an ` + 15 | `unambiguous key for that particular item. This property uniquely identifies the ` + 16 | `selected object item via a given property. By default, the key attribute is \`"id"\`.`, 17 | defaultValue: defaultProps.idKey, 18 | }, 19 | }, 20 | }); 21 | 22 | export const selectionStoreComponentRenderer = createComponentRenderer( 23 | COMP, 24 | SelectionStoreMd, 25 | (rendererContext) => { 26 | const { node, state, updateState, renderChild, registerComponentApi } = rendererContext; 27 | 28 | return ( 29 | <SelectionStore 30 | updateState={updateState} 31 | idKey={node.props.idKey} 32 | selectedItems={state?.value} 33 | registerComponentApi={registerComponentApi} 34 | > 35 | {renderChild(node.children)} 36 | </SelectionStore> 37 | ); 38 | }, 39 | ); 40 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/abstractions/standalone.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { CompoundComponentDef, ComponentLike } from "../../abstractions/ComponentDefs"; 2 | import type { ThemeDefinition } from "../../abstractions/ThemingDefs"; 3 | import type { ApiInterceptorDefinition } from "../interception/abstractions"; 4 | 5 | // This type describes a standalone app 6 | export type StandaloneAppDescription = { 7 | // Application name 8 | name?: string; 9 | 10 | // Application version 11 | version?: string; 12 | 13 | // The apps entry point; rendering starts here 14 | entryPoint?: ComponentLike; 15 | 16 | // Optional components used by the app in the entry point 17 | components?: CompoundComponentDef[]; 18 | 19 | // Optional themes used by the app 20 | themes?: ThemeDefinition[]; 21 | 22 | // The ID of the default theme 23 | defaultTheme?: string; 24 | 25 | // The default tone ("dark" or "light") 26 | defaultTone?: string; 27 | 28 | // Resource definitions for the app 29 | resources?: Record<string, string>; 30 | 31 | // Resource map for the app 32 | resourceMap?: Record<string, string>; 33 | appGlobals?: Record<string, any>; 34 | apiInterceptor?: ApiInterceptorDefinition; 35 | sources?: Record<string, string>; 36 | }; 37 | 38 | export type StandaloneJsonConfig = { 39 | name?: string; 40 | appGlobals?: Record<string, any>; 41 | entryPoint?: string; 42 | components?: string[]; 43 | themes?: string[]; 44 | defaultTheme?: string; 45 | resources?: Record<string, string>; 46 | resourceMap?: Record<string, string>; 47 | apiInterceptor?: ApiInterceptorDefinition; 48 | }; 49 | ``` -------------------------------------------------------------------------------- /docs/public/pages/tutorial-03.md: -------------------------------------------------------------------------------- ```markdown 1 | # Navigation 2 | 3 | Let's explore a subset of the app's navigation. 4 | 5 | ```xmlui-pg name="vertical layout" 6 | ---app display {3} 7 | <App 8 | name="XMLUI Invoice" 9 | layout="vertical" 10 | > 11 | <AppHeader> 12 | <H1>XMLUI Invoice</H1> 13 | </AppHeader> 14 | <NavPanel> 15 | <NavLink label="Dashboard" to="/" /> 16 | <NavLink label="Invoices" to="/invoices" /> 17 | </NavPanel> 18 | 19 | <Pages> 20 | <Page url="/"> 21 | <Dashboard /> 22 | </Page> 23 | <Page url="/invoices"> 24 | <Invoices /> 25 | </Page> 26 | 27 | </Pages> 28 | 29 | <Footer>Built with XMLUI <ToneSwitch /> </Footer> 30 | 31 | </App> 32 | ---comp display 33 | <Component name="Dashboard"> 34 | This is Dashboard. 35 | </Component> 36 | ---comp display 37 | <Component name="Invoices"> 38 | This is Invoices. 39 | </Component> 40 | ``` 41 | 42 | ## Horizontal 43 | 44 | We've seen `vertical`, here's `horizontal`. 45 | 46 | ```xmlui-pg name="horizontal layout" 47 | ---app display {3} 48 | <App 49 | name="XMLUI Invoice" 50 | layout="horizontal" 51 | > 52 | <AppHeader> 53 | <H1>XMLUI Invoice</H1> 54 | </AppHeader> 55 | <NavPanel> 56 | <NavLink label="Dashboard" to="/" /> 57 | <NavLink label="Invoices" to="/invoices" /> 58 | </NavPanel> 59 | 60 | <Pages> 61 | <Page url="/"> 62 | <Dashboard /> 63 | </Page> 64 | <Page url="/invoices"> 65 | <Invoices /> 66 | </Page> 67 | 68 | </Pages> 69 | 70 | <Footer>Built with XMLUI <ToneSwitch /> </Footer> 71 | 72 | </App> 73 | ---comp 74 | <Component name="Dashboard"> 75 | This is Dashboard. 76 | </Component> 77 | ---comp 78 | <Component name="Invoices"> 79 | This is Invoices. 80 | </Component> 81 | ``` 82 | ``` -------------------------------------------------------------------------------- /xmlui/src/testing/drivers/TimerDriver.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ComponentDriver } from "../ComponentDrivers"; 2 | 3 | export class TimerDriver extends ComponentDriver { 4 | async isEnabled(): Promise<boolean> { 5 | const enabled = await this.component.getAttribute("data-timer-enabled"); 6 | return enabled === "true"; 7 | } 8 | 9 | async isRunning(): Promise<boolean> { 10 | const running = await this.component.getAttribute("data-timer-running"); 11 | return running === "true"; 12 | } 13 | 14 | async isPaused(): Promise<boolean> { 15 | const paused = await this.component.getAttribute("data-timer-paused"); 16 | return paused === "true"; 17 | } 18 | 19 | async isInInitialDelay(): Promise<boolean> { 20 | const inDelay = await this.component.getAttribute("data-timer-in-initial-delay"); 21 | return inDelay === "true"; 22 | } 23 | 24 | async getInterval(): Promise<number> { 25 | const interval = await this.component.getAttribute("data-timer-interval"); 26 | return parseInt(interval || "0", 10); 27 | } 28 | 29 | async getInitialDelay(): Promise<number> { 30 | const delay = await this.component.getAttribute("data-timer-initial-delay"); 31 | return parseInt(delay || "0", 10); 32 | } 33 | 34 | async isOnce(): Promise<boolean> { 35 | const once = await this.component.getAttribute("data-timer-once"); 36 | return once === "true"; 37 | } 38 | 39 | async hasExecuted(): Promise<boolean> { 40 | const executed = await this.component.getAttribute("data-timer-has-executed"); 41 | return executed === "true"; 42 | } 43 | } 44 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/NestedApp/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Convert a string to its UTF-8 bytes and compress it. 3 | * 4 | * @param {string} str 5 | * @returns {Promise<Uint8Array>} 6 | */ 7 | export async function compress(str: string) { 8 | // Convert the string to a byte stream. 9 | const stream = new Blob([str]).stream(); 10 | 11 | // Create a compressed stream. 12 | const compressedStream = stream.pipeThrough(new CompressionStream("gzip")); 13 | 14 | // Convert the string to a byte stream. 15 | const reader = compressedStream.getReader(); 16 | const chunks = []; 17 | while (true) { 18 | const { done, value } = await reader.read(); 19 | if (done) break; 20 | chunks.push(value); 21 | } 22 | 23 | return await concatUint8Arrays(chunks); 24 | } 25 | 26 | /** 27 | * Combine multiple Uint8Arrays into one. 28 | * 29 | * @param {ReadonlyArray<Uint8Array>} uint8arrays 30 | * @returns {Promise<Uint8Array>} 31 | */ 32 | async function concatUint8Arrays(uint8arrays: Uint8Array[]) { 33 | const blob = new Blob(uint8arrays); 34 | const buffer = await blob.arrayBuffer(); 35 | return new Uint8Array(buffer); 36 | } 37 | 38 | export async function createQueryString(target: any) { 39 | // Convert the Uint8Array to a Base64 string. 40 | 41 | const compressed = await compress(target); 42 | const base64 = btoa(String.fromCharCode(...compressed)); 43 | 44 | // Create a query string. 45 | return encodeURIComponent(base64); 46 | } 47 | 48 | 49 | export function withoutTrailingSlash(str: string) { 50 | if (str.endsWith("/")) { 51 | return str.substring(0, str.length - 1); 52 | } 53 | return str; 54 | } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/code-sandbox.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <path d="M14 10.6669V5.33359C13.9998 5.09978 13.938 4.87013 13.821 4.6677C13.704 4.46527 13.5358 4.29717 13.3333 4.18026L8.66667 1.51359C8.46397 1.39657 8.23405 1.33496 8 1.33496C7.76595 1.33496 7.53603 1.39657 7.33333 1.51359L2.66667 4.18026C2.46418 4.29717 2.29599 4.46527 2.17897 4.6677C2.06196 4.87013 2.00024 5.09978 2 5.33359V10.6669C2.00024 10.9007 2.06196 11.1304 2.17897 11.3328C2.29599 11.5353 2.46418 11.7034 2.66667 11.8203L7.33333 14.4869C7.53603 14.604 7.76595 14.6656 8 14.6656C8.23405 14.6656 8.46397 14.604 8.66667 14.4869L13.3333 11.8203C13.5358 11.7034 13.704 11.5353 13.821 11.3328C13.938 11.1304 13.9998 10.9007 14 10.6669Z" stroke="#3B3B3B" stroke-linecap="round" stroke-linejoin="round"/> 3 | <path d="M5 2.80664L8 4.53997L11 2.80664" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> 4 | <path d="M5 13.1933V9.73333L2 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> 5 | <path d="M14 8L11 9.73333V13.1933" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> 6 | <path d="M2.18018 4.63965L8.00018 8.00632L13.8202 4.63965" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> 7 | <path d="M8 14.72V8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> 8 | </svg> 9 | ``` -------------------------------------------------------------------------------- /xmlui/tests-e2e/children-as-template-context-vars.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { expect, test } from "../src/testing/fixtures"; 2 | 3 | test("items works", async ({ page, initTestBed }) => { 4 | await initTestBed( 5 | ` 6 | <Items data="{['1', '2']}"> 7 | <DataSource url="/data/{$item}" id="data"/> 8 | <Text testId='data{$item}_text'>{data.value}</Text> 9 | </Items> 10 | `, 11 | { 12 | apiInterceptor: { 13 | operations: { 14 | "load-api-data1": { 15 | url: "/data/:taskId", 16 | method: "get", 17 | handler: `()=>{ 18 | return 'data' + $pathParams.taskId; 19 | }`, 20 | }, 21 | }, 22 | }, 23 | }, 24 | ); 25 | await expect(page.getByTestId("data1_text")).toHaveText("data1"); 26 | await expect(page.getByTestId("data2_text")).toHaveText("data2"); 27 | }); 28 | 29 | test("list works", async ({ page, initTestBed }) => { 30 | await initTestBed( 31 | ` 32 | <List data="{['1', '2']}"> 33 | <DataSource url="/data/{$item}" id="data"/> 34 | <Text testId='data{$item}_text'>{data.value}</Text> 35 | </List> 36 | `, 37 | { 38 | apiInterceptor: { 39 | operations: { 40 | "load-api-data1": { 41 | url: "/data/:taskId", 42 | method: "get", 43 | handler: `()=>{ 44 | return 'data' + $pathParams.taskId; 45 | }`, 46 | }, 47 | }, 48 | }, 49 | }, 50 | ); 51 | 52 | await expect(page.getByTestId("data1_text")).toHaveText("data1"); 53 | await expect(page.getByTestId("data2_text")).toHaveText("data2"); 54 | }); 55 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/share.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" > <path d="M18 8C19.6569 8 21 6.65685 21 5C21 3.34315 19.6569 2 18 2C16.3431 2 15 3.34315 15 5C15 6.65685 16.3431 8 18 8Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M6 15C7.65685 15 9 13.6569 9 12C9 10.3431 7.65685 9 6 9C4.34315 9 3 10.3431 3 12C3 13.6569 4.34315 15 6 15Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M18 22C19.6569 22 21 20.6569 21 19C21 17.3431 19.6569 16 18 16C16.3431 16 15 17.3431 15 19C15 20.6569 16.3431 22 18 22Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M8.58984 13.51L15.4198 17.49" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M15.4098 6.51001L8.58984 10.49" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/Icon.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Name-based lookup**: Reference icons by name from the built-in registry (e.g., "home", "search", "trash") 5 | - **Multiple sizes**: Choose from predefined sizes (xs, sm, md, lg) or set custom dimensions 6 | - **Fallback support**: Specify backup icons when the primary icon name doesn't exist 7 | - **Interactive**: Supports click events for creating icon buttons and clickable elements 8 | 9 | 10 | %-DESC-END 11 | 12 | %-PROP-START name 13 | 14 | The engine looks up the icon in its registry and determines which icon is associated with the name that the component will show. 15 | Nothing is displayed if the icon name is not found in the registry. 16 | 17 | ```xmlui-pg copy display name="Example: name" 18 | <App> 19 | <HStack> 20 | <Icon name="message" /> 21 | <Icon name="note" /> 22 | <Icon name="cog" /> 23 | <Icon name="start" /> 24 | <Icon name="some-non-existing-icon" /> 25 | <Icon name="some-non-existing-icon-with fallback" fallback="trash" /> 26 | </HStack> 27 | </App> 28 | ``` 29 | 30 | %-PROP-END 31 | 32 | %-PROP-START fallback 33 | 34 | ```xmlui-pg copy display name="Example: fallback" 35 | <App> 36 | <Icon name="noicon" fallback="trash" /> 37 | </App> 38 | ``` 39 | 40 | %-PROP-END 41 | 42 | %-PROP-START size 43 | 44 | ```xmlui-pg copy display name="Example: size" 45 | <App> 46 | <HStack> 47 | <Icon name="like" /> 48 | <Icon name="like" size="xs" /> 49 | <Icon name="like" size="sm" /> 50 | <Icon name="like" size="md" /> 51 | <Icon name="like" size="lg" /> 52 | </HStack> 53 | </App> 54 | ``` 55 | 56 | %-PROP-END 57 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Redirect/Redirect.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | ## Using `Redirect` 4 | 5 | The following app demonstrates two different patterns for using `Redirect`. 6 | 7 | 1. When you navigate to the "Redirect #1" page, it immediately redirects the app to the "Accounts" page. By default, the `when` property of `Redirect` (and any other component) is "true", so redirection immediately happens. 8 | 2. The "Redirect #2" page expects you to click the button before redirecting. The button click sets the `when` property of `Redirect` to true, and redirection happens at that moment. 9 | 10 | ```xmlui-pg copy {14, 20} display name="Example: providing children" height="170px" 11 | <App> 12 | <NavPanel> 13 | <NavLink to="/">Home</NavLink> 14 | <NavLink to="/accounts">Accounts</NavLink> 15 | <NavLink to="/products">Products</NavLink> 16 | <NavLink to="/redirect1">Redirect #1</NavLink> 17 | <NavLink to="/redirect2">Redirect #2</NavLink> 18 | </NavPanel> 19 | <Pages> 20 | <Page url="/">Home</Page> 21 | <Page url="/accounts">Accounts</Page> 22 | <Page url="/products">Products</Page> 23 | <Page url="/redirect1"> 24 | <Redirect to="/accounts" /> 25 | Redirecting to Accounts... 26 | </Page> 27 | <Page url="/redirect2"> 28 | <Fragment var.clicked="{false}"> 29 | <Button label="Click to redirect" onClick="clicked = true"/> 30 | <Redirect when="{clicked}" to="/accounts" /> 31 | Redirecting to Accounts... 32 | </Fragment> 33 | </Page> 34 | </Pages> 35 | </App> 36 | ``` 37 | 38 | %-DESC-END 39 | ``` -------------------------------------------------------------------------------- /docs/content/components/ToneChangerButton.md: -------------------------------------------------------------------------------- ```markdown 1 | # ToneChangerButton [#tonechangerbutton] 2 | 3 | `ToneChangerButton` enables the user to switch between light and dark modes. 4 | 5 | ```xmlui-pg {4} copy display name="Example: using ToneChangerButton" 6 | <App> 7 | <AppHeader> 8 | <SpaceFiller /> 9 | <ToneChangerButton /> 10 | </AppHeader> 11 | <Card 12 | title="Tone Changer Button" 13 | subtitle="Click the button in the header to change the tone." 14 | /> 15 | </App> 16 | ``` 17 | 18 | ## Properties [#properties] 19 | 20 | ### `darkToLightIcon` (default: "darkToLight:ToneChangerButton") [#darktolighticon-default-darktolight-tonechangerbutton] 21 | 22 | The icon displayed when the theme is in dark mode and will switch to light. You can change the default icon for all ToneChangerButton instances with the "icon.darkToLight:ToneChangerButton" declaration in the app configuration file. 23 | 24 | ### `lightToDarkIcon` (default: "lightToDark:ToneChangerButton") [#lighttodarkicon-default-lighttodark-tonechangerbutton] 25 | 26 | The icon displayed when the theme is in light mode and will switch to dark. You can change the default icon for all ToneChangerButton instances with the "icon.lightToDark:ToneChangerButton" declaration in the app configuration file. 27 | 28 | ## Events [#events] 29 | 30 | ### `click` [#click] 31 | 32 | This event is triggered when the ToneChangerButton is clicked. 33 | 34 | ## Exposed Methods [#exposed-methods] 35 | 36 | This component does not expose any methods. 37 | 38 | ## Styling [#styling] 39 | 40 | This component does not have any styles. 41 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Logo/Logo.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | Most apps use `logo="path/to/logo.svg"` on the App component rather than using `<Logo>` directly. Use this component when you need custom logo positioning or want to combine logos with other elements in a `logoTemplate`. 4 | 5 | ## Using Logo 6 | 7 | The framework checks the application manifest for a logo resource (SVG file). 8 | If found, it loads and displays it in the Logo component. 9 | 10 | This is a sample manifest that shows a logo definition: 11 | 12 | ```json copy {5} 13 | { 14 | "name": "Tutorial", 15 | "version": "0.0.1", 16 | "resources": { 17 | "logo": "resources/xmlui-logo.svg", 18 | "favicon": "resources/favicon.ico" 19 | } 20 | } 21 | ``` 22 | 23 | In the following example, you can see a custom logo definition in the `AppHeader` via templating. 24 | There is a `Heading` with the title text "MyApp" before the logo. 25 | It also uses the `Logo` component within the template definition: 26 | 27 | ```xmlui-pg 28 | ---app copy display name="Example: using Logo" {6} height="200px" 29 | <App layout="horizontal"> 30 | <AppHeader> 31 | <property name="logoTemplate"> 32 | <Fragment> 33 | <Heading level="h2" value="MyApp"/> 34 | <Logo/> 35 | </Fragment> 36 | </property> 37 | </AppHeader> 38 | <NavPanel> 39 | <NavLink label="Home" to="/" icon="home"/> 40 | </NavPanel> 41 | <Pages fallbackPath="/"> 42 | <Page url="/"> 43 | <CHStack> 44 | (Sample content) 45 | </CHStack> 46 | </Page> 47 | </Pages> 48 | </App> 49 | ---desc 50 | The markup displays the app's logo: 51 | ``` 52 | 53 | %-DESC-END 54 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Theme/NotificationToast.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { CSSProperties} from "react"; 2 | import { useEffect, useState } from "react"; 3 | import { createPortal } from "react-dom"; 4 | import toast, { ToastBar, Toaster } from "react-hot-toast"; 5 | import { useTheme } from "../../components-core/theming/ThemeContext"; 6 | 7 | const TOASTER_CONTAINER_STYLE: CSSProperties = { 8 | top: 40, 9 | right: 40, 10 | bottom: 40, 11 | left: 40, 12 | position: "absolute", 13 | overflowY: "hidden", 14 | }; 15 | 16 | type NotificationToastProps = { 17 | toastDuration?: number; 18 | }; 19 | 20 | let toasterMounted = false; 21 | 22 | export const NotificationToast = ({ toastDuration }: NotificationToastProps) => { 23 | const [shouldRender, setShouldRender] = useState(false); 24 | const { root } = useTheme(); 25 | 26 | useEffect(() => { 27 | if (!toasterMounted) { 28 | toasterMounted = true; 29 | setShouldRender(true); 30 | } 31 | }, []); 32 | 33 | if (!shouldRender) return null; 34 | return createPortal( 35 | <Toaster 36 | position={"top-right"} 37 | containerStyle={TOASTER_CONTAINER_STYLE} 38 | toastOptions={{ style: { padding: "12px 16px" }, duration: toastDuration }} 39 | > 40 | {(t) => ( 41 | <div onClick={() => toast.dismiss(t.id)}> 42 | <ToastBar position={t.position} toast={t} style={{ wordBreak: "break-word" }}> 43 | {({ icon, message }) => ( 44 | <> 45 | {icon} 46 | {message} 47 | </> 48 | )} 49 | </ToastBar> 50 | </div> 51 | )} 52 | </Toaster>, 53 | root, 54 | ); 55 | }; 56 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/Legend/Legend.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../../components-core/renderers"; 2 | import { createMetadata } from "../../metadata-helpers"; 3 | import { 4 | defaultProps, 5 | horizontalAlignmentValues, 6 | Legend, 7 | verticalAlignmentValues, 8 | } from "./LegendNative"; 9 | 10 | const COMP = "Legend"; 11 | 12 | export const LegendMd = createMetadata({ 13 | status: "experimental", 14 | description: 15 | "`Legend` provides a standalone legend for chart components when you need " + 16 | "custom positioning or styling beyond the chart's built-in `showLegend` " + 17 | "property. Most charts can display legends automatically, but this component " + 18 | "offers precise control over legend placement and alignment.", 19 | props: { 20 | align: { 21 | description: "The alignment of the legend", 22 | valueType: "string", 23 | availableValues: horizontalAlignmentValues, 24 | defaultValue: defaultProps.align, 25 | }, 26 | verticalAlign: { 27 | description: "The vertical alignment of the legend", 28 | valueType: "string", 29 | availableValues: verticalAlignmentValues, 30 | defaultValue: defaultProps.verticalAlign, 31 | }, 32 | }, 33 | }); 34 | 35 | export const legendComponentRenderer = createComponentRenderer( 36 | COMP, 37 | LegendMd, 38 | ({ extractValue, node }: any) => { 39 | return ( 40 | <Legend 41 | align={extractValue.asOptionalString(node.props?.align)} 42 | verticalAlign={extractValue.asOptionalString(node.props?.verticalAlign)} 43 | /> 44 | ); 45 | }, 46 | ); 47 | ``` -------------------------------------------------------------------------------- /docs/content/components/DonutChart.md: -------------------------------------------------------------------------------- ```markdown 1 | # DonutChart [#donutchart] 2 | 3 | A derivative of [PieChart](/components/PieChart) with a hollow center. Note that the height of the component or its parent needs to be set explicitly. 4 | 5 | ## Properties [#properties] 6 | 7 | ### `data` [#data] 8 | 9 | The data to be displayed in the chart. Needs to be an array of objects. 10 | 11 | ### `dataKey` [#datakey] 12 | 13 | This property specifies the key in the data objects that will be used to render the chart. 14 | 15 | ### `innerRadius` (default: 60) [#innerradius-default-60] 16 | 17 | Sets the inner radius of the donut chart. 18 | 19 | ### `nameKey` [#namekey] 20 | 21 | Specifies the key in the data objects that will be used to label the different data series. 22 | 23 | ### `showLabel` (default: true) [#showlabel-default-true] 24 | 25 | Toggles whether to show labels (`true`) or not (`false`). 26 | 27 | ### `showLabelList` (default: false) [#showlabellist-default-false] 28 | 29 | Whether to show labels in a list (`true`) or not (`false`). 30 | 31 | ### `showLegend` (default: false) [#showlegend-default-false] 32 | 33 | Whether to show a legend (`true`) or not (`false`). 34 | 35 | ## Events [#events] 36 | 37 | This component does not have any events. 38 | 39 | ## Exposed Methods [#exposed-methods] 40 | 41 | This component does not expose any methods. 42 | 43 | ## Styling [#styling] 44 | 45 | ### Theme Variables [#theme-variables] 46 | 47 | | Variable | Default Value (Light) | Default Value (Dark) | 48 | | --- | --- | --- | 49 | | [textColor](../styles-and-themes/common-units/#color)-labelList-PieChart | $textColor-primary | $textColor-primary | 50 | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/module-parse.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it } from "vitest"; 2 | import { 3 | ModuleErrors, 4 | isModuleErrors, 5 | parseScriptModule, 6 | } from "../../../src/parsers/scripting/modules"; 7 | import { ScriptModule } from "../../../src/components-core/script-runner/ScriptingSourceTree"; 8 | 9 | const ROOT_MODULE = "test"; 10 | 11 | describe("Modules - Parse", () => { 12 | it("Empty module", async () => { 13 | // --- Arrange 14 | const source = ` 15 | `; 16 | 17 | // --- Act 18 | const result = parseModule(source); 19 | 20 | // --- Assert 21 | expect(!isModuleErrors(result)).toBe(true); 22 | }); 23 | 24 | it("Module functions", async () => { 25 | // --- Arrange 26 | const source = ` 27 | function func1() {} 28 | function func2() {} 29 | `; 30 | 31 | // --- Act 32 | const result = parseModule(source) as ScriptModule; 33 | 34 | // --- Assert 35 | expect(!isModuleErrors(result)).toBe(true); 36 | 37 | expect(Object.keys(result.functions).length).toBe(2); 38 | expect(result.functions["func1"]).toBeDefined(); 39 | expect(result.functions["func2"]).toBeDefined(); 40 | }); 41 | 42 | it("Parsing error", async () => { 43 | // --- Arrange 44 | const source = `const;`; 45 | 46 | // --- Act 47 | const result = parseModule(source); 48 | 49 | // --- Assert 50 | expect(isModuleErrors(result)).toBe(true); 51 | const errors = result as ModuleErrors; 52 | expect(Object.keys(errors).length).toBe(1); 53 | }); 54 | }); 55 | 56 | function parseModule(source: string, modules: Record<string, string> = {}) { 57 | return parseScriptModule(ROOT_MODULE, source); 58 | } 59 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Markdown/CodeText.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { CSSProperties } from "react"; 2 | import type React from "react"; 3 | import { forwardRef, useRef } from "react"; 4 | import { composeRefs } from "@radix-ui/react-compose-refs"; 5 | import classnames from "classnames"; 6 | 7 | import styles from "./CodeText.module.scss"; 8 | 9 | type CodeTextProps = { 10 | uid?: string; 11 | children?: React.ReactNode; 12 | style?: CSSProperties; 13 | className?: string; 14 | [variantSpecificProps: string]: any; 15 | }; 16 | 17 | export const defaultProps = { 18 | maxLines: 0, 19 | preserveLinebreaks: false, 20 | ellipses: true, 21 | }; 22 | 23 | /** 24 | * This type implements code text we use in Markdown codefences. 25 | */ 26 | export const CodeText = forwardRef(function CodeText( 27 | { 28 | uid, 29 | style, 30 | className, 31 | children, 32 | preserveLinebreaks = defaultProps.preserveLinebreaks, 33 | ellipses = defaultProps.ellipses, 34 | ...variantSpecificProps 35 | }: CodeTextProps, 36 | forwardedRef, 37 | ) { 38 | const innerRef = useRef<HTMLElement>(null); 39 | const ref = forwardedRef ? composeRefs(innerRef, forwardedRef) : innerRef; 40 | // NOTE: This is to accept syntax highlight classes coming from shiki 41 | // classes need not to be added to the rendered html element, so we remove them from props 42 | return ( 43 | <> 44 | <pre 45 | {...variantSpecificProps} 46 | ref={ref as any} 47 | className={classnames(styles.codeText, styles.codefence, className)} 48 | style={{ 49 | ...style, 50 | }} 51 | > 52 | {children} 53 | </pre> 54 | </> 55 | ); 56 | }); 57 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Carousel/CarouselContext.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createContext, useContext, useMemo, useState } from "react"; 2 | import produce from "immer"; 3 | 4 | export const CarouselContext = createContext({ 5 | register: (column: any) => {}, 6 | unRegister: (id: string) => {}, 7 | itemProps: {}, 8 | }); 9 | export const useCarousel = () => { 10 | const context = useContext(CarouselContext); 11 | if (!context) { 12 | throw new Error("useCarousel must be used within a Carousel"); 13 | } 14 | return context; 15 | }; 16 | 17 | export function useCarouselContextValue(isTabbed: boolean) { 18 | const [carouselItems, setCarouselItems] = useState([]); 19 | 20 | const carouselContextValue = useMemo(() => { 21 | return { 22 | register: (column: any) => { 23 | setCarouselItems( 24 | produce((draft) => { 25 | const existing = draft.findIndex((col) => col.id === column.id); 26 | if (existing < 0) { 27 | draft.push(column); 28 | } else { 29 | draft[existing] = column; 30 | } 31 | }), 32 | ); 33 | }, 34 | unRegister: (id: string) => { 35 | setCarouselItems( 36 | produce((draft) => { 37 | return draft.filter((col) => col.id !== id); 38 | }), 39 | ); 40 | }, 41 | itemProps: isTabbed 42 | ? { 43 | role: "group tabpanel", 44 | } 45 | : { 46 | role: "group", 47 | "aria-roledescription": "slide", 48 | }, 49 | }; 50 | }, [setCarouselItems, isTabbed]); 51 | 52 | return { 53 | carouselItems, 54 | carouselContextValue, 55 | }; 56 | } 57 | ``` -------------------------------------------------------------------------------- /docs/content/components/SubMenuItem.md: -------------------------------------------------------------------------------- ```markdown 1 | # SubMenuItem [#submenuitem] 2 | 3 | `SubMenuItem` creates hierarchical menu structures by acting as both a menu item and a container for nested menu items. When clicked or hovered, it reveals a submenu containing additional [MenuItem](/components/MenuItem), [MenuSeparator](/components/MenuSeparator), or other [SubMenuItem](/components/SubMenuItems) components, enabling complex multi-level navigation and action organization. 4 | 5 | **Key features:** 6 | - **Hierarchical nesting**: Creates multi-level menu structures within [DropdownMenu](/components/DropdownMenu) components 7 | - **Dual functionality**: Acts as both a clickable menu item and a container for other menu components 8 | - **Custom triggers**: Configurable trigger appearance via triggerTemplate property 9 | - **Progressive disclosure**: Reveals nested options on hover or click interaction 10 | - **Unlimited depth**: Supports multiple levels of nesting for complex menu hierarchies 11 | 12 | ## Properties [#properties] 13 | 14 | ### `label` [#label] 15 | 16 | This property sets the label of the component. If not set, the component will not display a label. 17 | 18 | ### `triggerTemplate` [#triggertemplate] 19 | 20 | This property allows you to define a custom trigger instead of the default one provided by `SubMenuItem`. 21 | 22 | ## Events [#events] 23 | 24 | This component does not have any events. 25 | 26 | ## Exposed Methods [#exposed-methods] 27 | 28 | This component does not expose any methods. 29 | 30 | ## Styling [#styling] 31 | 32 | This component does not have any styles. 33 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/rendering/collectFnVarDeps.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * This function collects all the dependencies of a function in a flat list 3 | * (no circular deps). 4 | * @param fnDeps The dependencies of the functions. Each key is a function name 5 | * and the value is an array of variables the function depends on. 6 | * @returns The flat dependencies of the functions. 7 | * 8 | * Example: 9 | * it("simple", () => { 10 | * const fnDeps = { 11 | * fn1: ["fn2", "var1", "var2"], 12 | * fn2: ["var1", "var3", "fn3"], 13 | * fn3: ["var4"] 14 | * }; 15 | * 16 | * expect(collectFnVarDeps(fnDeps)).deep.eq({ 17 | * fn1: ["var1", "var3", "var4", "var2"], 18 | * fn2: ["var1", "var3", "var4"], 19 | * fn3: ["var4"] 20 | * }); 21 | * }); 22 | */ 23 | export function collectFnVarDeps(fnDeps: Record<string, string[]> = {}): Record<string, string[]> { 24 | const fnKeys = Object.keys(fnDeps); 25 | 26 | function collectFnDeps(depKey: string, visitedPath = new Set<string>()) { 27 | if (!fnKeys.includes(depKey)) { 28 | return [depKey]; 29 | } 30 | visitedPath.add(depKey); 31 | const ret: Array<string> = []; 32 | fnDeps[depKey].forEach((key) => { 33 | if (visitedPath.has(key)) { 34 | // --- We already walked here, avoid infinite loops for circular deps 35 | return; 36 | } 37 | visitedPath.add(key); 38 | ret.push(...collectFnDeps(key, visitedPath)); 39 | }); 40 | return ret; 41 | } 42 | 43 | const flatFnDeps: Record<string, string[]> = {}; 44 | fnKeys.forEach((key) => { 45 | flatFnDeps[key] = [...new Set(collectFnDeps(key))]; 46 | }); 47 | return flatFnDeps; 48 | } 49 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ProfileMenu/ProfileMenu.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./ProfileMenu.module.scss"; 2 | 3 | import { useThemes } from "../../components-core/theming/ThemeContext"; 4 | import { Avatar } from "../Avatar/AvatarNative"; 5 | import { DropdownMenu, MenuItem, MenuSeparator } from "../DropdownMenu/DropdownMenuNative"; 6 | 7 | // ===================================================================================================================== 8 | // Heading React component 9 | 10 | type Props = { 11 | loggedInUser: any | null; 12 | }; 13 | 14 | export const ProfileMenu = ({ loggedInUser }: Props) => { 15 | const { activeThemeId, setActiveThemeId } = useThemes(); 16 | 17 | if (!loggedInUser) { 18 | return null; 19 | } 20 | const loggedInUserName = loggedInUser.name || loggedInUser.displayName; 21 | return ( 22 | <DropdownMenu triggerTemplate={<Avatar url={loggedInUser.avatarUrl} name={loggedInUserName} size={"xs"} />}> 23 | <div className={styles.loggedInUserInfoWrapper}> 24 | <div className={styles.name}>{loggedInUserName}</div> 25 | <div className={styles.email}>{loggedInUser.email}</div> 26 | </div> 27 | <MenuSeparator /> 28 | {activeThemeId.includes("dark") && <MenuItem onClick={() => setActiveThemeId(activeThemeId.replace("dark", "light"))}>Switch to light mode</MenuItem>} 29 | {activeThemeId.includes("light") && <MenuItem onClick={() => setActiveThemeId(activeThemeId.replace("light", "dark"))}>Switch to dark mode</MenuItem>} 30 | <MenuSeparator /> 31 | <MenuItem>Log out</MenuItem> 32 | </DropdownMenu> 33 | ); 34 | }; 35 | ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/StandalonePlaygroundNative.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as themes; 2 | 3 | .standalonePlayground { 4 | display: flex; 5 | flex-direction: column; 6 | height: 100%; 7 | width: 100%; 8 | min-width: 0; 9 | min-height: 0; 10 | } 11 | 12 | .loadingContainer { 13 | position: absolute; 14 | left: 50%; 15 | top: calc(50% - 25px); 16 | transform: translateX(-50%); 17 | /* Center perfectly on the X axis */ 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | /* Center the content horizontally */ 22 | width: 300px; 23 | height: 50px; 24 | } 25 | 26 | .logoWrapper { 27 | width: 50px; 28 | height: 50px; 29 | transform: translateZ(0); 30 | 31 | .animatedLogoPath { 32 | will-change: stroke-dashoffset, stroke-dasharray; 33 | /* The total length of the rectangle path is ~82. */ 34 | stroke-dasharray: 82; 35 | stroke-dashoffset: 82; 36 | 37 | /* Animation: name, duration, easing, and iteration count */ 38 | animation: draw-loop 3s ease-in-out infinite; 39 | } 40 | } 41 | 42 | .loadingText { 43 | margin-right: 12px; 44 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; 45 | font-size: 16px; 46 | /* Increased by 20% from 16px */ 47 | font-weight: 500; 48 | color: themes.$color-primary-500; 49 | text-align: right; 50 | } 51 | 52 | /* Defines the animation steps */ 53 | @keyframes draw-loop { 54 | 55 | /* At the start, the path is not drawn */ 56 | 0% { 57 | stroke-dashoffset: 82; 58 | } 59 | 60 | /* Animate to fully drawn over 40% of the duration */ 61 | 40% { 62 | stroke-dashoffset: 0; 63 | } 64 | 65 | /* Hold the fully drawn state until the end */ 66 | 100% { 67 | stroke-dashoffset: 0; 68 | } 69 | } 70 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Input/PartialInput.module.scss: -------------------------------------------------------------------------------- ```scss 1 | // Common styles for PartialInput component 2 | // These styles contain only the shared patterns from multi-field input components 3 | // Component-specific styling (including widths) should be applied via className prop 4 | 5 | @layer components { 6 | .partialInput { 7 | // Base layout and sizing 8 | height: 100%; 9 | position: relative; 10 | border: 0; 11 | background: none; 12 | color: currentColor; 13 | font: inherit; 14 | box-sizing: border-box; 15 | text-align: center; 16 | 17 | // Remove browser default styling 18 | -webkit-appearance: textfield; 19 | -moz-appearance: textfield; 20 | appearance: textfield; 21 | 22 | // Hide spin buttons for number inputs (all browsers) 23 | &::-webkit-outer-spin-button, 24 | &::-webkit-inner-spin-button { 25 | -webkit-appearance: none !important; 26 | -moz-appearance: none !important; 27 | appearance: none !important; 28 | margin: 0 !important; 29 | display: none !important; 30 | } 31 | 32 | // Hide spin buttons for Firefox specifically 33 | &[type="number"] { 34 | -moz-appearance: textfield !important; 35 | appearance: textfield !important; 36 | } 37 | 38 | // Focus state 39 | &:focus { 40 | outline: none; // Component-specific focus styling should be applied by parent 41 | } 42 | 43 | // Disabled state 44 | &:disabled { 45 | opacity: 0.6; 46 | cursor: not-allowed; 47 | } 48 | 49 | // Read-only state 50 | &[readonly] { 51 | cursor: default; 52 | } 53 | 54 | // Common placeholder styling 55 | &::placeholder { 56 | opacity: 0.6; 57 | } 58 | } 59 | } 60 | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/xmlui/xmlui.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { nodeToComponentDef } from "../../../src/parsers/xmlui-parser/transform"; 2 | import type { ComponentDef, CompoundComponentDef } from "../../../src/abstractions/ComponentDefs"; 3 | import { createXmlUiParser, ParseResult } from "../../../src/parsers/xmlui-parser/parser"; 4 | import type { GetText } from "../../../src/parsers/xmlui-parser/parser"; 5 | import { toDbgString } from "../../../src/parsers/xmlui-parser/utils"; 6 | 7 | export function transformSource( 8 | source: string, 9 | fileId?: number, 10 | printRes: boolean = false, 11 | ): ComponentDef | CompoundComponentDef | undefined { 12 | const { getText, parse } = createXmlUiParser(source); 13 | const { node, errors } = parse(); 14 | if (printRes) { 15 | console.log(toDbgString(node, getText)); 16 | console.log( 17 | "errors: \n[\n" + errors.map((e) => e.message + ` @${e.pos}`).join(";\n") + "\n]\n", 18 | ); 19 | } 20 | if (errors.length > 0) { 21 | // return {errors} 22 | throw new Error(errors[0].message); 23 | } 24 | return nodeToComponentDef(node, getText, fileId ?? 0); 25 | } 26 | 27 | export function parseSource( 28 | source: string, 29 | printRes: boolean = false, 30 | ): ParseResult & { getText: GetText } { 31 | const parser = createXmlUiParser(source); 32 | const parseRes = parser.parse(); 33 | 34 | if (printRes) { 35 | console.log(toDbgString(parseRes.node, parser.getText)); 36 | console.log( 37 | "errors: \n[\n" + parseRes.errors.map((e) => e.message + ` @${e.pos}`).join(";\n") + "\n]\n", 38 | ); 39 | } 40 | return { ...parseRes, getText: parser.getText }; 41 | } 42 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Spinner/Spinner.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./Spinner.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { Spinner, defaultProps } from "./SpinnerNative"; 6 | import { createMetadata } from "../metadata-helpers"; 7 | 8 | const COMP = "Spinner"; 9 | 10 | export const SpinnerMd = createMetadata({ 11 | status: "stable", 12 | description: 13 | "`Spinner` is an animated indicator that represents an action in progress " + 14 | "with no deterministic progress value.", 15 | props: { 16 | delay: { 17 | description: `The delay in milliseconds before the spinner is displayed.`, 18 | valueType: "number", 19 | defaultValue: defaultProps.delay, 20 | }, 21 | fullScreen: { 22 | description: `If set to \`true\`, the component will be rendered in a full screen container.`, 23 | valueType: "boolean", 24 | defaultValue: defaultProps.fullScreen, 25 | }, 26 | }, 27 | themeVars: parseScssVar(styles.themeVars), 28 | defaultThemeVars: { 29 | [`size-${COMP}`]: "$space-10", 30 | [`thickness-${COMP}`]: "$space-0_5", 31 | [`borderColor-${COMP}`]: "$color-surface-400", 32 | }, 33 | }); 34 | 35 | export const spinnerComponentRenderer = createComponentRenderer( 36 | COMP, 37 | SpinnerMd, 38 | ({ node, className, extractValue }) => { 39 | return ( 40 | <Spinner 41 | className={className} 42 | delay={extractValue.asOptionalNumber(node.props.delay)} 43 | fullScreen={extractValue.asOptionalBoolean(node.props.fullScreen)} 44 | /> 45 | ); 46 | }, 47 | ); 48 | ``` -------------------------------------------------------------------------------- /packages/xmlui-devtools/src/devtools/Tooltip.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as themes; 2 | 3 | .TooltipContent { 4 | border-radius: 4px; 5 | padding: 10px 15px; 6 | font-size: 15px; 7 | line-height: 1; 8 | color: themes.$textColor-primary; 9 | background-color: themes.$backgroundColor; 10 | box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); 11 | user-select: none; 12 | animation-duration: 400ms; 13 | animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); 14 | will-change: transform, opacity; 15 | } 16 | .TooltipContent[data-state='delayed-open'][data-side='top'] { 17 | animation-name: slideDownAndFade; 18 | } 19 | .TooltipContent[data-state='delayed-open'][data-side='right'] { 20 | animation-name: slideLeftAndFade; 21 | } 22 | .TooltipContent[data-state='delayed-open'][data-side='bottom'] { 23 | animation-name: slideUpAndFade; 24 | } 25 | .TooltipContent[data-state='delayed-open'][data-side='left'] { 26 | animation-name: slideRightAndFade; 27 | } 28 | 29 | @keyframes slideUpAndFade { 30 | from { 31 | opacity: 0; 32 | transform: translateY(2px); 33 | } 34 | to { 35 | opacity: 1; 36 | transform: translateY(0); 37 | } 38 | } 39 | 40 | @keyframes slideRightAndFade { 41 | from { 42 | opacity: 0; 43 | transform: translateX(-2px); 44 | } 45 | to { 46 | opacity: 1; 47 | transform: translateX(0); 48 | } 49 | } 50 | 51 | @keyframes slideDownAndFade { 52 | from { 53 | opacity: 0; 54 | transform: translateY(-2px); 55 | } 56 | to { 57 | opacity: 1; 58 | transform: translateY(0); 59 | } 60 | } 61 | 62 | @keyframes slideLeftAndFade { 63 | from { 64 | opacity: 0; 65 | transform: translateX(2px); 66 | } 67 | to { 68 | opacity: 1; 69 | transform: translateX(0); 70 | } 71 | } 72 | ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/Tooltip.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as themes; 2 | 3 | .TooltipContent { 4 | border-radius: 4px; 5 | padding: 10px 15px; 6 | font-size: 15px; 7 | line-height: 1; 8 | color: themes.$textColor-primary; 9 | background-color: themes.$backgroundColor; 10 | box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); 11 | user-select: none; 12 | animation-duration: 400ms; 13 | animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); 14 | will-change: transform, opacity; 15 | } 16 | .TooltipContent[data-state='delayed-open'][data-side='top'] { 17 | animation-name: slideDownAndFade; 18 | } 19 | .TooltipContent[data-state='delayed-open'][data-side='right'] { 20 | animation-name: slideLeftAndFade; 21 | } 22 | .TooltipContent[data-state='delayed-open'][data-side='bottom'] { 23 | animation-name: slideUpAndFade; 24 | } 25 | .TooltipContent[data-state='delayed-open'][data-side='left'] { 26 | animation-name: slideRightAndFade; 27 | } 28 | 29 | @keyframes slideUpAndFade { 30 | from { 31 | opacity: 0; 32 | transform: translateY(2px); 33 | } 34 | to { 35 | opacity: 1; 36 | transform: translateY(0); 37 | } 38 | } 39 | 40 | @keyframes slideRightAndFade { 41 | from { 42 | opacity: 0; 43 | transform: translateX(-2px); 44 | } 45 | to { 46 | opacity: 1; 47 | transform: translateX(0); 48 | } 49 | } 50 | 51 | @keyframes slideDownAndFade { 52 | from { 53 | opacity: 0; 54 | transform: translateY(-2px); 55 | } 56 | to { 57 | opacity: 1; 58 | transform: translateY(0); 59 | } 60 | } 61 | 62 | @keyframes slideLeftAndFade { 63 | from { 64 | opacity: 0; 65 | transform: translateX(2px); 66 | } 67 | to { 68 | opacity: 1; 69 | transform: translateX(0); 70 | } 71 | } 72 | ``` -------------------------------------------------------------------------------- /xmlui/src/logging/LoggerService.ts: -------------------------------------------------------------------------------- ```typescript 1 | type LoggerCallback = (args: any[]) => void; 2 | 3 | class LoggerService { 4 | private static instance: LoggerService; 5 | private callback?: LoggerCallback; 6 | private isDev: boolean; 7 | 8 | private constructor() { 9 | this.isDev = process.env.NODE_ENV === "development" || !!process.env.VITE_DEV_MODE; 10 | } 11 | 12 | public static getInstance(): LoggerService { 13 | if (!LoggerService.instance) { 14 | LoggerService.instance = new LoggerService(); 15 | } 16 | return LoggerService.instance; 17 | } 18 | 19 | public registerCallback(callback: LoggerCallback) { 20 | if (this.isDev) { 21 | this.callback = callback; 22 | } 23 | } 24 | 25 | public log(args: any[]) { 26 | if (!this.isDev) return; 27 | console.log("[xmlui.log]", ...args); 28 | if (this.callback) { 29 | this.callback(args); 30 | } 31 | } 32 | 33 | public warn(args: any[]) { 34 | if (!this.isDev) return; 35 | console.warn("[xmlui.warn]", ...args); 36 | if (this.callback) { 37 | this.callback(args); 38 | } 39 | } 40 | 41 | public info(args: any[]) { 42 | if (!this.isDev) return; 43 | console.info("[xmlui.info]", ...args); 44 | if (this.callback) { 45 | this.callback(args); 46 | } 47 | } 48 | 49 | public error(args: any[]) { 50 | if (!this.isDev) return; 51 | console.error("[xmlui.error]", ...args); 52 | if (this.callback) { 53 | this.callback(args); 54 | } 55 | } 56 | 57 | public trace(args: any[]) { 58 | if (!this.isDev) return; 59 | console.trace("[xmlui.trace]", ...args); 60 | if (this.callback) { 61 | this.callback(args); 62 | } 63 | } 64 | } 65 | 66 | export const loggerService = LoggerService.getInstance(); 67 | ``` -------------------------------------------------------------------------------- /xmlui/src/parsers/scripting/TokenType.ts: -------------------------------------------------------------------------------- ```typescript 1 | // Token types available for parsing 2 | export enum TokenType { 3 | Eof = -1, 4 | Ws = -2, 5 | BlockComment = -3, 6 | EolComment = -4, 7 | Unknown = 0, 8 | 9 | // --- Binding Expression specific tokens 10 | LParent, 11 | RParent, 12 | 13 | Identifier, 14 | 15 | Exponent, 16 | Divide, 17 | Multiply, 18 | Remainder, 19 | Plus, 20 | Minus, 21 | BitwiseXor, 22 | BitwiseOr, 23 | LogicalOr, 24 | BitwiseAnd, 25 | LogicalAnd, 26 | IncOp, 27 | DecOp, 28 | Assignment, 29 | 30 | AddAssignment, 31 | SubtractAssignment, 32 | ExponentAssignment, 33 | MultiplyAssignment, 34 | DivideAssignment, 35 | RemainderAssignment, 36 | ShiftLeftAssignment, 37 | ShiftRightAssignment, 38 | SignedShiftRightAssignment, 39 | BitwiseAndAssignment, 40 | BitwiseXorAssignment, 41 | BitwiseOrAssignment, 42 | LogicalAndAssignment, 43 | LogicalOrAssignment, 44 | NullCoalesceAssignment, 45 | 46 | Semicolon, 47 | Comma, 48 | Colon, 49 | LSquare, 50 | RSquare, 51 | QuestionMark, 52 | NullCoalesce, 53 | OptionalChaining, 54 | BinaryNot, 55 | LBrace, 56 | RBrace, 57 | Equal, 58 | StrictEqual, 59 | LogicalNot, 60 | NotEqual, 61 | StrictNotEqual, 62 | LessThan, 63 | LessThanOrEqual, 64 | ShiftLeft, 65 | GreaterThan, 66 | GreaterThanOrEqual, 67 | ShiftRight, 68 | SignedShiftRight, 69 | Dot, 70 | Spread, 71 | Global, 72 | Backtick, 73 | DollarLBrace, 74 | Arrow, 75 | 76 | DecimalLiteral, 77 | HexadecimalLiteral, 78 | BinaryLiteral, 79 | RealLiteral, 80 | StringLiteral, 81 | Infinity, 82 | NaN, 83 | True, 84 | False, 85 | 86 | Typeof, 87 | Null, 88 | Undefined, 89 | In, 90 | 91 | Let, 92 | Const, 93 | Var, 94 | If, 95 | Else, 96 | Return, 97 | Break, 98 | Continue, 99 | Do, 100 | While, 101 | For, 102 | Of, 103 | Try, 104 | Catch, 105 | Finally, 106 | Throw, 107 | Switch, 108 | Case, 109 | Default, 110 | Delete, 111 | Function, 112 | As, 113 | } 114 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/HtmlTags/HtmlTags.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { getBounds } from "../../testing/component-test-helpers"; 2 | import { expect, test } from "../../testing/fixtures"; 3 | 4 | test.describe("smoke tests", { tag: "@smoke" }, () => { 5 | test("htmlTable is rendered", async ({ initTestBed, createHtmlTagDriver }) => { 6 | await initTestBed(`<table />`); 7 | const driver = await createHtmlTagDriver(); 8 | 9 | await expect(driver.component).toBeAttached(); 10 | }); 11 | }); 12 | 13 | const tableCode = ` 14 | <table> 15 | <thead> 16 | <tr> 17 | <th scope="col">Person</th> 18 | <th scope="col">Most interest in</th> 19 | <th scope="col">Age</th> 20 | </tr> 21 | </thead> 22 | <tbody> 23 | <tr> 24 | <th scope="row">Chris</th> 25 | <td>HTML tables</td> 26 | <td>22</td> 27 | </tr> 28 | <tr> 29 | <th scope="row">Dennis</th> 30 | <td>Web accessibility</td> 31 | <td>45</td> 32 | </tr> 33 | <tr> 34 | <th scope="row">Sarah</th> 35 | <td>JavaScript frameworks</td> 36 | <td>29</td> 37 | </tr> 38 | <tr> 39 | <th scope="row">Karen</th> 40 | <td>Web performance</td> 41 | <td>36</td> 42 | </tr> 43 | </tbody> 44 | <tfoot> 45 | <tr> 46 | <th scope="row" colSpan="2">Average age</th> 47 | <td>33</td> 48 | </tr> 49 | </tfoot> 50 | </table> 51 | `; 52 | 53 | test("htmlTable width using themes", async ({ initTestBed, createHtmlTagDriver }) => { 54 | const { width } = await initTestBed(tableCode, { 55 | testThemeVars: { 56 | "width-HtmlTable": "100%", 57 | }, 58 | }); 59 | const driver = await createHtmlTagDriver(); 60 | const compWidth = (await getBounds(driver)).width; 61 | 62 | expect(compWidth).toEqual(width); 63 | }); 64 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileUploadDropZone/FileUploadDropZone.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $backgroundColor-FileUploadDropZone: createThemeVar("backgroundColor-FileUploadDropZone"); 11 | $textColor-FileUploadDropZone: createThemeVar("textColor-FileUploadDropZone"); 12 | $backgroundColor-dropping-FileUploadDropZone: createThemeVar( 13 | "backgroundColor-dropping-FileUploadDropZone" 14 | ); 15 | $opacity-dropping-FileUploadDropZone: createThemeVar("opacity-dropping-FileUploadDropZone"); 16 | 17 | @layer components { 18 | .wrapper { 19 | position: relative; 20 | background-color: $backgroundColor-FileUploadDropZone; 21 | color: $textColor-FileUploadDropZone; 22 | display: flex; 23 | flex-direction: column; 24 | } 25 | 26 | .dropPlaceholder { 27 | position: absolute; 28 | inset: 0; 29 | z-index: 1; 30 | pointer-events: none; 31 | display: flex; 32 | flex: 1; 33 | align-items: center; 34 | justify-content: center; 35 | flex-direction: column; 36 | 37 | &:before { 38 | content: ""; 39 | position: absolute; 40 | inset: 0; 41 | background-color: $backgroundColor-dropping-FileUploadDropZone; 42 | opacity: $opacity-dropping-FileUploadDropZone; //0.5; 43 | } 44 | } 45 | } 46 | 47 | // --- We export the theme variables to add them to the component renderer 48 | :export { 49 | themeVars: t.json-stringify($themeVars); 50 | } 51 | ```