This is page 7 of 137. Use http://codebase.md/xmlui-org/xmlui?lines=false&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/Pages/Pages.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Route coordination**: Automatically displays the correct Page based on current URL and navigation - **Default route handling**: Sets the initial page shown when the application loads - **Client-side routing**: Manages navigation without page refreshes or server requests ### Using the Pages and Page components The `Page` component has a property called `url`. This is the route associated with the `Page's` contents. You can provide a link to this route to display a particular `Page`. Currently, all navigation is done on the clientside. No page is fetched from the server, thus the application operates as a [Single Page Application](https://developer.mozilla.org/en-US/docs/Glossary/SPA). ```xmlui-pg copy {3-4, 7, 10} display name="Example: using Pages and Page" height="170px" <App> <NavPanel> <NavLink label="Home" to="/" icon="home"/> <NavLink label="Account" to="/account" icon="user"/> </NavPanel> <Pages> <Page url="/"> <Text>Hello App!</Text> </Page> <Page url="/account"> <Text>This is the account page.</Text> </Page> </Pages> </App> ``` %-DESC-END %-PROP-START fallbackPath ```xmlui-pg copy {6-13} display name="Example: fallbackPath" height="170px" <App> <NavPanel> <NavLink label="Not Home" to="/not-home" icon="trash"/> <NavLink label="Home" to="/home" icon="home"/> </NavPanel> <Pages fallbackPath="/home"> <Page url="/not-home"> <Text>This is not home...</Text> </Page> <Page url="/home"> <Text>Hello App!</Text> </Page> </Pages> </App> ``` %-PROP-END ``` -------------------------------------------------------------------------------- /xmlui/src/parsers/style-parser/errors.ts: -------------------------------------------------------------------------------- ```typescript // The common root class of all parser error objects export class StyleParserError extends Error { constructor (message: string, public code?: string) { super(message); // --- Set the prototype explicitly. Object.setPrototypeOf(this, StyleParserError.prototype); } } export type StyleErrorCodes = | "S001" | "S002" | "S003" | "S004" | "S005" | "S006" | "S007" | "S008" | "S009" | "S010" | "S011" | "S012" | "S013" | "S014" | "S015" | "S016" | "S017" | "S018" | "S019" | "S020" | "S021"; // Error message type description type ErrorText = Record<string, string>; // The error messages of error codes export const styleErrorMessages: ErrorText = { S001: "A numeric value expected", S002: "A dimension unit expected", S003: "An alignment value expected", S004: "A border style value expected", S005: "A color value or expression expected", S006: "Unknown color function name '{0}'", S007: "'(' expected, but '{0}' received", S008: "A color channel percentage value expected between 0 and 100", S009: "A color channel value expected between 0 and 255", S010: "')' expected, but '{0}' received", S011: "An alpha channel value expected between 0.0 and 1.0", S012: "A scrolling value expected", S013: "A direction value expected", S014: "A fontFamily value expected", S015: "A fontWeight value expected", S016: "Unexpected token found", S017: "A Boolean value expected", S018: "An orientation value expected", S019: "']' expected", S020: "A user-select value expected", S021: "A text transform value expected", }; ``` -------------------------------------------------------------------------------- /xmlui/src/parsers/common/InputStream.ts: -------------------------------------------------------------------------------- ```typescript // This class represents the input stream of the binding parser export class InputStream { // --- Current stream position private _pos = 0; // --- Current line number private _line = 1; // --- Current column number private _column = 0; // Creates a stream that uses the specified source code constructor (public readonly source: string) {} // Gets the current position in the stream. Starts from 0. get position (): number { return this._pos; } // Gets the current line number. Starts from 1. get line (): number { return this._line; } // Gets the current column number. Starts from 0. get column (): number { return this._column; } // Peeks the next character in the stream. Returns null, if EOF; otherwise the current source code character peek (): string | null { return this.ahead(0); } // Looks ahead with `n` characters in the stream. Returns null, if EOF; otherwise the look-ahead character ahead (n: number = 1): string | null { return this._pos + n > this.source.length - 1 ? null : this.source[this._pos + n]; } // Gets the next character from the stream get (): string | null { // --- Check for EOF if (this._pos >= this.source.length) { return null; } // --- Get the char, and keep track of position const ch = this.source[this._pos++]; if (ch === "\n") { this._line++; this._column = 0; } else { this._column++; } return ch; } // Gets the tail of the input stream getTail (start: number): string { return this.source?.substring(start) ?? ""; } } ``` -------------------------------------------------------------------------------- /xmlui/src/components/NestedApp/AppWithCodeView.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: ( ); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $component: "AppWithCodeView"; $background-sideBySide-AppWithCode: createThemeVar("background-sideBySide-#{$component}"); $border-sideBySide-AppWithCode: createThemeVar("border-sideBySide-#{$component}"); $padding-sideBySide-AppWithCode: createThemeVar("padding-sideBySide-#{$component}"); $margin-sideBySide-AppWithCode: createThemeVar("margin-sideBySide-#{$component}"); $gap-sideBySide-AppWithCode: createThemeVar("gap-sideBySide-#{$component}"); @layer components { .container { display: flex; /* default flexbox alignment */ justify-content: flex-start; align-items: stretch; min-height: 0; min-width: 0; gap: $gap-sideBySide-AppWithCode; background: $background-sideBySide-AppWithCode; border: $border-sideBySide-AppWithCode; padding: $padding-sideBySide-AppWithCode; margin: $margin-sideBySide-AppWithCode; } .vertical { flex-direction: column; gap: var(--stack-gap-default); min-height: 0; } .horizontal { flex-direction: row; } .column { width: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/rendering/StandaloneComponent.tsx: -------------------------------------------------------------------------------- ```typescript import type { ReactNode} from "react"; import { cloneElement, isValidElement, useMemo, useRef } from "react"; import type { MemoedVars } from "../abstractions/ComponentRenderer"; import { renderChild } from "./renderChild"; import { EMPTY_OBJECT, noop } from "../constants"; import type { ComponentDef } from "../../abstractions/ComponentDefs"; type RootComponentProps = { node: ComponentDef; children?: ReactNode; functions?: Record<string, any>; vars?: Record<string, any>; }; function StandaloneComponent({ node, children, functions, vars }: RootComponentProps) { const memoedVarsRef = useRef<MemoedVars>(new Map()); const rootNode = useMemo(() => { if(node.type === "Container"){ // If the node is already a Container, we can use it directly return { ...node, functions: { ...node.functions, ...functions, }, vars: { ...node.vars, ...vars, } }; } return { type: "Container", uid: "standaloneComponentRoot", children: [node], uses: [], functions, vars, }; }, [functions, node, vars]); const renderedRoot = renderChild({ node: rootNode, state: EMPTY_OBJECT, dispatch: noop, appContext: undefined, lookupAction: noop, lookupSyncCallback: noop, registerComponentApi: noop, renderChild: noop, statePartChanged: noop, cleanup: noop, memoedVarsRef, }); return !!children && isValidElement(renderedRoot) ? cloneElement(renderedRoot, null, children) : renderedRoot; } export default StandaloneComponent; ``` -------------------------------------------------------------------------------- /packages/xmlui-devtools/CHANGELOG.md: -------------------------------------------------------------------------------- ```markdown # xmlui-devtools ## 0.1.20 ### Patch Changes - 819b563: Update fontSize and lineHeight themes and style (may break existing xmlui code) ## 0.1.19 ### Patch Changes - cbe1ef2: Use grammar and syntax highlight files straight form the xmlui package, instead of duplicating them in every app. ## 0.1.18 ### Patch Changes - 7d255a9: Changed open in new window button tooltip label for all occurrences. ## 0.1.17 ### Patch Changes - 84a660d: fix: devtools - turn off stickyScroll ## 0.1.16 ### Patch Changes - 9345bd4: Turn off sticky scroll ## 0.1.15 ### Patch Changes - 9a3c3b6: feat: xmlui-devtools - start dialog animation from the click, use exit animation as well ## 0.1.14 ### Patch Changes - 9d9d930: feat: devtools - themeVariable for setting the duration of the modal's animation ## 0.1.13 ### Patch Changes - 73c2c21: wip: code inspector buttons - label change, devtools - animation update ## 0.1.12 ### Patch Changes - f5b9f15: feat: xmlui-devtools - use it in a modal dialog ## 0.1.11 ### Patch Changes - 2e88539: improve: add Tooltip to DevTools ## 0.1.10 ### Patch Changes - 3eab4a3: improve: design updates - devtools, playground, docs ## 0.1.9 ### Patch Changes - c92223c: When we launch the playground from the devtools, it now appears on a subpage instead of under the docs on a different domain. ## 0.1.8 ### Patch Changes - b0ae113: testing ## 0.1.7 ### Patch Changes - f15c018: another testing - f15c018: testing ## 0.1.6 ### Patch Changes - 421968b: testing ## 0.1.5 ### Patch Changes - 99bba69: testing ## 0.1.4 ### Patch Changes - bcf162c: testing changesets ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/providers/Toast.module.scss: -------------------------------------------------------------------------------- ```scss .ToastViewport { --viewport-padding: 25px; position: fixed; bottom: 0; right: 0; display: flex; flex-direction: column; padding: 2rem; gap: 10px; width: 390px; max-width: 100vw; margin: 0; list-style: none; z-index: 2147483647; outline: none; } .ToastClose { color: #fff; justify-self: flex-end; } .ToastRoot { background-color: rgb(23, 162, 184); border-radius: 6px; box-shadow: 0px 0.8px 2px rgba(0, 0, 0, 0.032), 0px 2.7px 6.7px rgba(0, 0, 0, 0.048), 0px 12px 30px rgba(0, 0, 0, 0.08); padding: 15px; display: grid; grid-template-areas: "title action" "description action"; grid-template-columns: auto max-content; align-items: center; } .error { background-color: rgb(220, 53, 69); } .warning { background-color: rgb(255, 193, 7); } .info { background-color: rgb(23, 162, 184); } .success { background-color: rgb(40, 167, 69); } .ToastRoot[data-state="open"] { animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1); } .ToastRoot[data-state="closed"] { animation: hide 100ms ease-in; } .ToastRoot[data-swipe="cancel"] { transform: translateX(0); transition: transform 200ms ease-out; } @keyframes hide { from { opacity: 1; } to { opacity: 0; } } @keyframes slideIn { from { transform: translateX(calc(100% + 2rem)); } to { transform: translateX(0); } } .ToastTitle { color: #fff; grid-area: title; margin-bottom: 5px; font-weight: 500; font-size: 15px; } .ToastDescription { color: #fff; grid-area: description; margin: 0; font-size: 13px; line-height: 1.3; } .ToastAction { grid-area: action; } ``` -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- ```json { "name": "xmlui-docs", "private": true, "version": "0.0.10", "scripts": { "start": "echo '====================================================================\nExecuting \"npm run watch-docs-content\" in the project root,\nyou get automatic content generation based on xmlui metadata!\n====================================================================\n' && xmlui start", "preview": "xmlui preview", "gen:releases": "node scripts/get-releases.js --output 'public/resources/files/releases.json'", "gen:download-latest-xmlui-release": "node scripts/download-latest-xmlui.js", "gen:rss": "node scripts/generate-rss.js", "build:docs": "xmlui build --buildMode=INLINE_ALL --withMock && npm run gen:download-latest-xmlui-release && npm run gen:rss", "build-optimized": "npm run gen:releases && npm run gen:download-latest-xmlui-release && npm run gen:rss && npx xmlui-optimizer", "release-ci-optimized": "npm run build-optimized && xmlui zip-dist --source=xmlui-optimized-output --target=ui-optimized.zip", "preview-optimized": "npx serve@latest ./xmlui-optimized-output" }, "dependencies": { "@shikijs/langs": "3.4.2", "shiki": "^3.3.0", "xmlui": "*", "xmlui-playground": "*", "xmlui-search": "*", "xmlui-hello-world": "*" }, "msw": { "workerDirectory": [ "public" ] }, "devDependencies": { "xmlui-website-blocks": "*", "xmlui-animations": "*", "@emotion/is-prop-valid": "^1.3.1", "@octokit/rest": "^22.0.0", "remark-parse": "11.0.0", "remark-stringify": "11.0.0", "strip-markdown": "6.0.0", "unified": "11.0.5" } } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Footer/Footer.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./Footer.module.scss"; import { createComponentRenderer } from "../../components-core/renderers"; import { parseScssVar } from "../../components-core/theming/themeVars"; import { Footer } from "./FooterNative"; import { createMetadata } from "../metadata-helpers"; import classnames from "classnames"; const COMP = "Footer"; export const FooterMd = createMetadata({ status: "stable", description: "`Footer` provides a designated area at the bottom of your application for " + "footer content such as branding, copyright notices, or utility controls like " + "theme toggles.", themeVars: parseScssVar(styles.themeVars), limitThemeVarsToComponent: true, defaultThemeVars: { [`backgroundColor-${COMP}`]: "$backgroundColor-AppHeader", [`verticalAlignment-${COMP}`]: "center", [`fontSize-${COMP}`]: "$fontSize-sm", [`textColor-${COMP}`]: "$textColor-secondary", [`maxWidth-content-${COMP}`]: "$maxWidth-content", [`borderTop-${COMP}`]: `1px solid $borderColor`, [`padding-${COMP}`]: "$space-2 $space-4", [`gap-${COMP}`]: "$space-normal", [`margin-${COMP}`]: `0 auto`, light: { // --- No light-specific theme vars }, dark: { // --- No dark-specific theme vars }, }, }); export const footerRenderer = createComponentRenderer( COMP, FooterMd, ({ node, renderChild, className, layoutContext }) => { return ( <Footer className={classnames(layoutContext?.themeClassName, className)}> {renderChild(node.children, { type: "Stack", orientation: "horizontal", })} </Footer> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Spinner/Spinner.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $size-Spinner: createThemeVar("size-Spinner"); $thickness-Spinner: createThemeVar("thickness-Spinner"); $borderColor-Spinner: createThemeVar("borderColor-Spinner"); @layer components { .lds-ring { display: inline-block; position: relative; width: $size-Spinner; height: $size-Spinner; } .lds-ring div { box-sizing: border-box; display: block; position: absolute; width: 80%; height: 80%; margin: 10%; border-width: $thickness-Spinner; border-style: solid; border-radius: 50%; animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; border-color: $borderColor-Spinner transparent transparent transparent; } .lds-ring div:nth-child(1) { animation-delay: -0.45s; } .lds-ring div:nth-child(2) { animation-delay: -0.3s; } .lds-ring div:nth-child(3) { animation-delay: -0.15s; } @keyframes lds-ring { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .fullScreenSpinnerWrapper { width: 100%; height: 100%; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/src/components/ChangeListener/ChangeListener.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer } from "../../components-core/renderers"; import { createMetadata, dDidChange } from "../metadata-helpers"; import { ChangeListener, defaultProps } from "./ChangeListenerNative"; const COMP = "ChangeListener"; export const ChangeListenerMd = createMetadata({ status: "stable", description: "`ChangeListener` is an invisible component that watches for changes in values " + "and triggers actions in response. It's essential for creating reactive behavior " + "when you need to respond to data changes, state updates, or component property " + "modifications outside of normal event handlers.", props: { listenTo: { description: "Value to the changes of which this component listens. If this property is not set, " + "the `ChangeListener` is inactive.", valueType: "any", }, throttleWaitInMs: { description: `This variable sets a throttling time (in milliseconds) to apply when executing the \`didChange\` ` + `event handler. All changes within that throttling time will only fire the \`didChange\` event once.`, valueType: "number", defaultValue: defaultProps.throttleWaitInMs, }, }, events: { didChange: dDidChange(COMP), }, }); export const changeListenerComponentRenderer = createComponentRenderer( COMP, ChangeListenerMd, ({ node, lookupEventHandler, extractValue }) => { return ( <ChangeListener listenTo={extractValue(node.props.listenTo)} throttleWaitInMs={extractValue(node.props.throttleWaitInMs)} onChange={lookupEventHandler("didChange")} /> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Badge/Badge.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $component: "Badge"; $themeVars: t.composeBorderVars($themeVars, $component); $themeVars: t.composePaddingVars($themeVars, $component); $themeVars: t.composeTextVars($themeVars, $component); $themeVars: t.composeBorderVars($themeVars, "#{$component}-pill"); $themeVars: t.composePaddingVars($themeVars, "#{$component}-pill"); $themeVars: t.composeTextVars($themeVars, "#{$component}-pill"); @layer components { .badge { @include t.paddingVars($themeVars, $component); @include t.borderVars($themeVars, $component); @include t.textVars($themeVars, $component); display: inline-block; width: fit-content; height: fit-content; vertical-align: top; min-width: fit-content; overflow: hidden; text-overflow: ellipsis; text-transform: uppercase; } .pill { @include t.paddingVars($themeVars, "#{$component}-pill"); @include t.borderVars($themeVars, "#{$component}-pill"); @include t.textVars($themeVars, "#{$component}-pill"); display: inline-block; width: fit-content; height: fit-content; vertical-align: top; min-width: fit-content; overflow: hidden; text-overflow: ellipsis; border-radius: 9999px; } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Link/Link.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START ## Using Link ### `Link` Appearance You can use the `label` and `icon` properties of a `Link` to set its text and icon to display. If you want a custom appearance, you can nest your visual representation into `Link`: ```xmlui-pg copy {3-6} display name="Example: custom Link content" <App> <Link to="https://docs.xmlui.org/" target="_blank"> <HStack verticalAlignment="center"> <Stack width="16px" height="16px" backgroundColor="purple" /> XMLUI introduction </HStack> </Link> </App> ``` %-DESC-END %-PROP-START active ```xmlui-pg copy display name="Example: active" /active/ <App> <Link>I'm an inactive link (by default)</Link> <Link active="true">I'm an active link</Link> <Link active="false">I'm an inactive link (explicit setting)</Link> </App> ``` %-PROP-END %-PROP-START enabled ```xmlui-pg copy display name="Example: enabled" /enabled/ <App> <Link>I'm an enabled link (by default)</Link> <Link enabled="false">I'm a disabled link</Link> <Link enabled="true">I'm an enabled link (explicit setting)</Link> </App> ``` %-PROP-END %-PROP-START icon ```xmlui-pg copy display name="Example: icon" <App> <Link icon="home" label="Home" /> <Link icon="drive">Drives</Link> </App> ``` >[!INFO] > If you want to specify paddings and gaps or put the icon to the right of the link text, use your custom link template (nest it into `Link`). %-PROP-END %-PROP-START target The following sample opens its link in a new tab: ```xmlui-pg copy display name="Example: target" <App> <Link to="https://docs.xmlui.org/" target="_blank"> Open XMLUI overview in a new tab </Link> </App> ``` %-PROP-END ``` -------------------------------------------------------------------------------- /docs/content/components/xmlui-website-blocks/ScrollToTop.md: -------------------------------------------------------------------------------- ```markdown # ScrollToTop [#scrolltotop] A floating button that scrolls the page to the top when clicked ## Properties ### `behavior` (default: "smooth") Scroll behavior when scrolling to top ### `icon` (default: "chevronup") Name of the icon to display in the button ### `position` (default: "end") Horizontal position of the button at the bottom of the screen ### `threshold` (default: 300) Scroll position threshold (in pixels) after which the button becomes visible ### `visible` (default: true) Whether the button is visible ## Events ### `click` Triggered when the scroll to top button is clicked ## Exposed Methods This component does not expose any methods. ## Parts The component has some parts that can be styled through layout properties and theme variables separately: - **`icon`**: The icon displayed inside the scroll to top button ## Styling ### Theme Variables | Variable | Default Value (Light) | Default Value (Dark) | | --- | --- | --- | | [backgroundColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary | $color-primary | | [borderColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary-dark | $color-primary-dark | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-ScrollToTop | $space-24 | $space-24 | | bottom-ScrollToTop | $space-16 | $space-16 | | [color](../styles-and-themes/common-units/#color)-ScrollToTop | $color-surface-0 | $color-surface-0 | | horizontalSpacing-ScrollToTop | $space-16 | $space-16 | | shadow-ScrollToTop | $shadow-lg | $shadow-lg | | [size](../styles-and-themes/common-units/#size)-ScrollToTop | 48px | 48px | | zIndex-ScrollToTop | 1000 | 1000 | ``` -------------------------------------------------------------------------------- /docs/content/extensions/xmlui-website-blocks/ScrollToTop.md: -------------------------------------------------------------------------------- ```markdown # ScrollToTop [#scrolltotop] A floating button that scrolls the page to the top when clicked ## Properties ### `behavior` (default: "smooth") Scroll behavior when scrolling to top ### `icon` (default: "chevronup") Name of the icon to display in the button ### `position` (default: "end") Horizontal position of the button at the bottom of the screen ### `threshold` (default: 300) Scroll position threshold (in pixels) after which the button becomes visible ### `visible` (default: true) Whether the button is visible ## Events ### `click` Triggered when the scroll to top button is clicked ## Exposed Methods This component does not expose any methods. ## Parts The component has some parts that can be styled through layout properties and theme variables separately: - **`icon`**: The icon displayed inside the scroll to top button ## Styling ### Theme Variables | Variable | Default Value (Light) | Default Value (Dark) | | --- | --- | --- | | [backgroundColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary | $color-primary | | [borderColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary-dark | $color-primary-dark | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-ScrollToTop | $space-24 | $space-24 | | bottom-ScrollToTop | $space-16 | $space-16 | | [color](../styles-and-themes/common-units/#color)-ScrollToTop | $color-surface-0 | $color-surface-0 | | horizontalSpacing-ScrollToTop | $space-16 | $space-16 | | shadow-ScrollToTop | $shadow-lg | $shadow-lg | | [size](../styles-and-themes/common-units/#size)-ScrollToTop | 48px | 48px | | zIndex-ScrollToTop | 1000 | 1000 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/InspectButton/InspectButton.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer } from "../../components-core/renderers"; import { Button } from "../Button/ButtonNative"; import { useInspectMode } from "../../components-core/InspectorContext"; import styles from "./InspectButton.module.scss"; import { parseScssVar } from "../../components-core/theming/themeVars"; import type { CSSProperties, ReactNode } from "react"; import { PiFileCode } from "react-icons/pi"; import { createMetadata } from "../metadata-helpers"; import classnames from "classnames"; const COMP = "InspectButton"; export const InspectButtonMd = createMetadata({ status: "experimental", description: "This component displays a button that can turn the inspection " + "mode of a running XMLUI app on or off.", props: {}, themeVars: parseScssVar(styles.themeVars), }); function InspectButton({ children, style, className }: { children: ReactNode; style?: CSSProperties, className?: string }) { const { setInspectMode, inspectMode } = useInspectMode(); return ( <Button style={style} className={classnames(styles.inspectButton, className)} themeColor={inspectMode ? "primary" : "secondary"} variant={inspectMode ? "solid" : "outlined"} onClick={() => { setInspectMode((prev: any) => !prev); }} > <PiFileCode className={styles.icon}/> {children} </Button> ); } /** * Define the renderer for the Button component */ export const inspectButtonComponentRenderer = createComponentRenderer( COMP, InspectButtonMd, ({ renderChild, node, className }) => { return <InspectButton className={className}>{renderChild(node.children)}</InspectButton>; }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/NoResult/NoResult.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./NoResult.module.scss"; import { createComponentRenderer } from "../../components-core/renderers"; import { parseScssVar } from "../../components-core/theming/themeVars"; import { createMetadata, dLabel } from "../metadata-helpers"; import { NoResult, defaultProps } from "./NoResultNative"; const COMP = "NoResult"; export const NoResultMd = createMetadata({ status: "stable", description: "`NoResult` displays a visual indication that a query or search returned nothing.", props: { label: dLabel(), icon: { description: `This property defines the icon to display with the component.`, valueType: "string", defaultValue: defaultProps.icon, }, hideIcon: { description: `This boolean property indicates if the icon should be hidden.`, valueType: "boolean", defaultValue: defaultProps.hideIcon, }, }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { [`border-${COMP}`]: "0px solid $borderColor", [`paddingVertical-${COMP}`]: "$space-2", [`gap-icon-${COMP}`]: "$space-2", [`size-icon-${COMP}`]: "$space-8", light: { // --- No light-specific theme vars }, dark: { // --- No dark-specific theme vars }, }, }); export const noResultComponentRenderer = createComponentRenderer( COMP, NoResultMd, ({ node, extractValue, className }) => { return ( <NoResult label={extractValue.asDisplayText(node.props.label || node.children || "No results found")} icon={node.props.icon} hideIcon={extractValue.asOptionalBoolean(node.props.hideIcon)} className={className} /> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/unlink.svg: -------------------------------------------------------------------------------- ``` <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M10 12.4503C10.3236 12.894 10.7365 13.2612 11.2106 13.5269C11.6847 13.7926 12.209 13.9505 12.7479 13.9901C13.2868 14.0297 13.8277 13.95 14.3339 13.7563C14.8401 13.5626 15.2998 13.2596 15.6817 12.8677L17.9424 10.549C18.6287 9.82019 19.0084 8.84404 18.9999 7.83081C18.9913 6.81758 18.595 5.84834 17.8965 5.13185C17.1979 4.41536 16.2529 4.00895 15.265 4.00015C14.2771 3.99134 13.3254 4.38085 12.6148 5.08478L11.3187 6.4064" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M13 10.5497C12.6764 10.106 12.2635 9.73881 11.7894 9.47313C11.3153 9.20745 10.791 9.04946 10.2521 9.00987C9.7132 8.97029 9.17231 9.05004 8.66611 9.24371C8.15991 9.43738 7.70024 9.74045 7.31828 10.1323L5.05764 12.451C4.37132 13.1798 3.99156 14.156 4.00014 15.1692C4.00873 16.1824 4.40497 17.1517 5.10354 17.8681C5.80211 18.5846 6.7471 18.991 7.73498 18.9999C8.72287 19.0087 9.6746 18.6192 10.3852 17.9152L11.6738 16.5936" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8.1582 2.67383L9.06952 5.30645" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M18.4316 17.667L16.8262 15.3902" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M5 5.28906L7.23775 6.94848" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M20.7402 14.2783L18.1301 13.3044" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/doc.svg: -------------------------------------------------------------------------------- ``` <svg width="22" height="28" viewBox="0 0 22 28" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0_1811_15061)"> <path d="M22.2271 7.38281L16.304 5.74219L14.6117 0H2.76551C1.36355 0 0.227051 1.10179 0.227051 2.46094V25.5391C0.227051 26.8982 1.36355 28 2.76551 28H19.6886C21.0906 28 22.2271 26.8982 22.2271 25.5391V7.38281Z" fill="currentColor"/> <path d="M22.227 7.38281H16.3039C15.3731 7.38281 14.6116 6.64453 14.6116 5.74219V0C14.8316 0 15.0516 0.0820312 15.2038 0.246148L21.9731 6.80865C22.1423 6.95625 22.227 7.16953 22.227 7.38281Z" fill="currentColor"/> <path d="M16.304 13.1797H6.15011C5.68241 13.1797 5.30396 12.8128 5.30396 12.3594C5.30396 11.906 5.68241 11.5391 6.15011 11.5391H16.304C16.7717 11.5391 17.1501 11.906 17.1501 12.3594C17.1501 12.8128 16.7717 13.1797 16.304 13.1797Z" fill="white"/> <path d="M16.304 16.4609H6.15011C5.68241 16.4609 5.30396 16.094 5.30396 15.6406C5.30396 15.1872 5.68241 14.8203 6.15011 14.8203H16.304C16.7717 14.8203 17.1501 15.1872 17.1501 15.6406C17.1501 16.094 16.7717 16.4609 16.304 16.4609Z" fill="white"/> <path d="M16.304 19.7422H6.15011C5.68241 19.7422 5.30396 19.3753 5.30396 18.9219C5.30396 18.4685 5.68241 18.1016 6.15011 18.1016H16.304C16.7717 18.1016 17.1501 18.4685 17.1501 18.9219C17.1501 19.3753 16.7717 19.7422 16.304 19.7422Z" fill="white"/> <path d="M12.9193 23.0234H6.15011C5.68241 23.0234 5.30396 22.6565 5.30396 22.2031C5.30396 21.7497 5.68241 21.3828 6.15011 21.3828H12.9193C13.387 21.3828 13.7655 21.7497 13.7655 22.2031C13.7655 22.6565 13.387 23.0234 12.9193 23.0234Z" fill="white"/> </g> <defs> <clipPath id="clip0_1811_15061"> <rect width="22" height="28" fill="white"/> </clipPath> </defs> </svg> ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/style-parser/parseHVar.test.ts: -------------------------------------------------------------------------------- ```typescript import {describe, expect, test} from "vitest"; import { parseHVar } from "../../../src/components-core/theming/hvar"; describe("Parse HVar", () => { const cases = [ { src: 'backgroundColor-Button-primary-solid--active--hover', attribute: "backgroundColor", component: "Button", traits: ["primary", "solid"], states: ["active", "hover"], }, { src: 'backgroundColor-Button--active--hover', attribute: "backgroundColor", component: "Button", states: ["active", "hover"], }, { src: "backgroundColor-Button-primary-solid", attribute: "backgroundColor", component: "Button", traits: ["primary", "solid"], }, { src: "backgroundColor-Button-primary--active", attribute: "backgroundColor", component: "Button", traits: ["primary"], states: ["active"], }, { src: "backgroundColor-Button", attribute: "backgroundColor", component: "Button", }, { src: "Input:backgroundColor-TextBox", classes: ['Input'], attribute: "backgroundColor", component: "TextBox", }, { src: "Control:Input:backgroundColor-TextBox", classes: ['Control', 'Input'], attribute: "backgroundColor", component: "TextBox", } ]; test.each(cases)('parse hvar: $src', ({src, states, traits, component, attribute, classes})=>{ const parsed = parseHVar(src)!; expect(parsed.classes).eqls(classes || []); expect(parsed.attribute).equal(attribute); expect(parsed.component).equal(component); expect(parsed.traits).eqls(traits || []); expect(parsed.states).eqls(states || []); }); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Avatar/Avatar.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $component: "Avatar"; $themeVars: t.composeBorderVars($themeVars, $component); $backgroundColor-Avatar: createThemeVar("backgroundColor-#{$component}"); $boxShadow-Avatar: createThemeVar("boxShadow-#{$component}"); $textColor-Avatar: createThemeVar("textColor-#{$component}"); $fontWeight-Avatar: createThemeVar("fontWeight-#{$component}"); @layer components { .container { @include t.borderVars($themeVars, $component); aspect-ratio: 1 / 1; background-color: $backgroundColor-Avatar; color: $textColor-Avatar; flex-shrink: 0; object-fit: cover; display: flex; align-items: center; justify-content: center; font-weight: $fontWeight-Avatar; white-space: nowrap; user-select: none; font-size: 12px; box-shadow: $boxShadow-Avatar; &.xs { width: t.$space-8; height: t.$space-8; font-size: t.$space-3; } &.sm { width: t.$space-12; height: t.$space-12; font-size: t.$space-4; } &.md { width: t.$space-16; height: t.$space-16; font-size: t.$space-5; } &.lg { width: t.$space-24; height: t.$space-24; font-size: t.$space-8; } &.clickable { cursor: pointer; } } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/process-statement-asgn.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect, it, assert } from "vitest"; import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; import { createEvalContext, parseStatements } from "./test-helpers"; describe("Process statements - assignments", () => { const asgnOps = [ "=", "+=", "-=", "**=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=", "&&=", "||=", "??=", ]; asgnOps.forEach((c) => { it(`cannot assign to non-defined variable (${c}) - sync`, () => { // --- Arrange const source = `dummy ${c} "do not allow this";`; const evalContext = createEvalContext({ localContext: {} }); const statements = parseStatements(source); // --- Act/Assert try { processStatementQueue(statements, evalContext); } catch (err: any) { expect(err.toString()).toContain("not found"); return; } assert.fail("Exception expected"); }); }); asgnOps.forEach((c) => { it(`cannot assign to non-defined variable (${c}) - async`, async () => { // --- Arrange const source = `dummy ${c} "do not allow this";`; const evalContext = createEvalContext({ localContext: {} }); const statements = parseStatements(source); // --- Act/Assert try { await processStatementQueueAsync(statements, evalContext); } catch (err: any) { expect(err.toString()).toContain("not found"); return; } assert.fail("Exception expected"); }); }); }); ``` -------------------------------------------------------------------------------- /xmlui/src/testing/infrastructure/public/resources/doc.svg: -------------------------------------------------------------------------------- ``` <svg width="22" height="28" viewBox="0 0 22 28" fill="none" xmlns="http://www.w3.org/2000/svg" data-testid="doc-svg"> <g clip-path="url(#clip0_1811_15061)"> <path d="M22.2271 7.38281L16.304 5.74219L14.6117 0H2.76551C1.36355 0 0.227051 1.10179 0.227051 2.46094V25.5391C0.227051 26.8982 1.36355 28 2.76551 28H19.6886C21.0906 28 22.2271 26.8982 22.2271 25.5391V7.38281Z" fill="currentColor"/> <path d="M22.227 7.38281H16.3039C15.3731 7.38281 14.6116 6.64453 14.6116 5.74219V0C14.8316 0 15.0516 0.0820312 15.2038 0.246148L21.9731 6.80865C22.1423 6.95625 22.227 7.16953 22.227 7.38281Z" fill="currentColor"/> <path d="M16.304 13.1797H6.15011C5.68241 13.1797 5.30396 12.8128 5.30396 12.3594C5.30396 11.906 5.68241 11.5391 6.15011 11.5391H16.304C16.7717 11.5391 17.1501 11.906 17.1501 12.3594C17.1501 12.8128 16.7717 13.1797 16.304 13.1797Z" fill="white"/> <path d="M16.304 16.4609H6.15011C5.68241 16.4609 5.30396 16.094 5.30396 15.6406C5.30396 15.1872 5.68241 14.8203 6.15011 14.8203H16.304C16.7717 14.8203 17.1501 15.1872 17.1501 15.6406C17.1501 16.094 16.7717 16.4609 16.304 16.4609Z" fill="white"/> <path d="M16.304 19.7422H6.15011C5.68241 19.7422 5.30396 19.3753 5.30396 18.9219C5.30396 18.4685 5.68241 18.1016 6.15011 18.1016H16.304C16.7717 18.1016 17.1501 18.4685 17.1501 18.9219C17.1501 19.3753 16.7717 19.7422 16.304 19.7422Z" fill="white"/> <path d="M12.9193 23.0234H6.15011C5.68241 23.0234 5.30396 22.6565 5.30396 22.2031C5.30396 21.7497 5.68241 21.3828 6.15011 21.3828H12.9193C13.387 21.3828 13.7655 21.7497 13.7655 22.2031C13.7655 22.6565 13.387 23.0234 12.9193 23.0234Z" fill="white"/> </g> <defs> <clipPath id="clip0_1811_15061"> <rect width="22" height="28" fill="white"/> </clipPath> </defs> </svg> ``` -------------------------------------------------------------------------------- /packages/xmlui-search/src/index.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer, createMetadata, parseScssVar } from "xmlui"; import { Search, defaultProps } from "./Search"; import styles from "./Search.module.scss"; const COMP = "Search"; const COMP_PANEL = `${COMP}Panel`; const COMP_ITEM = `${COMP}Item`; export const SearchMd = createMetadata({ description: `The \`${COMP}\` component provides a search component.`, status: "experimental", props: { data: { description: ``, }, limit: { description: ``, valueType: "number", defaultValue: defaultProps.limit, }, }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { [`backgroundColor-${COMP_PANEL}`]: "$color-surface-0", [`borderRadius-${COMP_PANEL}`]: "8px", [`borderWidth-${COMP_PANEL}`]: "1px", [`borderStyle-${COMP_PANEL}`]: "solid", [`borderColor-${COMP_PANEL}`]: "$color-surface-200", [`boxShadow-${COMP_PANEL}`]: "$boxShadow-xl", [`backgroundColor-${COMP_ITEM}--hover`]: "$color-primary-50", [`borderColor-${COMP_ITEM}--focus`]: "$color-primary-400", [`borderRadius-${COMP_ITEM}`]: "4px", dark: { [`backgroundColor-${COMP_PANEL}`]: "$color-surface-100", [`borderColor-${COMP_PANEL}`]: "$color-surface-300", [`backgroundColor-${COMP_ITEM}--hover`]: "rgb(from $color-primary-200 r g b / 0.4)", } }, }); const searchComponentRenderer = createComponentRenderer(COMP, SearchMd, ({ node, extractValue }) => { return ( <Search data={extractValue(node.props?.data)} limit={extractValue.asOptionalNumber(node.props?.limit, defaultProps.limit)} /> ); }); export default { namespace: "XMLUIExtensions", components: [searchComponentRenderer], }; ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/scripts-runner/process-statement-asgn.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect, it, assert } from "vitest"; import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; import { createEvalContext, parseStatements } from "./test-helpers"; describe("Process statements - assignments (exp)", () => { const asgnOps = [ "=", "+=", "-=", "**=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=", "&&=", "||=", "??=", ]; asgnOps.forEach((c) => { it(`cannot assign to non-defined variable (${c}) - sync`, () => { // --- Arrange const source = `dummy ${c} "do not allow this";`; const evalContext = createEvalContext({ localContext: {} }); const statements = parseStatements(source); // --- Act/Assert try { processStatementQueue(statements, evalContext); } catch (err: any) { expect(err.toString()).toContain("not found"); return; } assert.fail("Exception expected"); }); }); asgnOps.forEach((c) => { it(`cannot assign to non-defined variable (${c}) - async`, async () => { // --- Arrange const source = `dummy ${c} "do not allow this";`; const evalContext = createEvalContext({ localContext: {} }); const statements = parseStatements(source); // --- Act/Assert try { await processStatementQueueAsync(statements, evalContext); } catch (err: any) { expect(err.toString()).toContain("not found"); return; } assert.fail("Exception expected"); }); }); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Column/ColumnNative.tsx: -------------------------------------------------------------------------------- ```typescript import { useCallback, useId, useLayoutEffect, useMemo } from "react"; import type { ComponentDef } from "../../abstractions/ComponentDefs"; import type { RenderChildFn } from "../../abstractions/RendererDefs"; import { MemoizedItem } from "../../components/container-helpers"; import { useTableContext } from "./TableContext"; import type { OurColumnMetadata } from "./TableContext"; type Props = OurColumnMetadata & { nodeChildren?: ComponentDef[]; renderChild: RenderChildFn; }; export const defaultProps: Pick<Props, "canSort" | "canResize"> = { canSort: true, canResize: true, }; export function Column({ nodeChildren, renderChild, ...columnMetadata }: Props) { const id = useId(); const { registerColumn, unRegisterColumn } = useTableContext(); const cellRenderer = useCallback( (row: any, rowIndex: number, colIndex: number, value: any) => { return ( <MemoizedItem node={nodeChildren!} item={row} contextVars={{ $rowIndex: rowIndex, $colIndex: colIndex, $row: row, $itemIndex: rowIndex, $cell: value, }} renderChild={renderChild} /> ); }, [nodeChildren, renderChild], ); const safeCellRenderer = useMemo(() => { return nodeChildren ? cellRenderer : undefined; }, [cellRenderer, nodeChildren]); useLayoutEffect(() => { registerColumn( { ...columnMetadata, cellRenderer: safeCellRenderer, }, id, ); }, [columnMetadata, id, registerColumn, safeCellRenderer]); useLayoutEffect(() => { return () => { unRegisterColumn(id); }; }, [id, unRegisterColumn]); return null; } ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/test-metadata-handler.ts: -------------------------------------------------------------------------------- ```typescript import { ComponentMetadata } from "../../src/abstractions/ComponentDefs"; import { MetadataHandler, PropDescriptorHash } from "../../src/components-core/markup-check"; export function createTestMetadataHandler( desc: Record<string, ComponentMetadata>, ): MetadataHandler { return { componentRegistered: (type: string) => { return !!desc[type]; }, getComponentProps: (type: string) => { const compDesc = desc[type]; if (!compDesc) { return {}; } const propsToMap = compDesc.props ?? {}; const mappedProps: PropDescriptorHash = {}; Object.keys(propsToMap).forEach((key) => { const prop = propsToMap[key]!; mappedProps[key] = { type: prop.valueType, availableValues: prop.availableValues, defaultValue: prop.defaultValue, }; }); return mappedProps; }, getComponentEvents(componentName) { const compDesc = desc[componentName]; if (!compDesc) { return {}; } const eventsToMap = compDesc.events ?? {}; const mappedEvents: Record<string, any> = {}; Object.keys(eventsToMap).forEach((key) => { mappedEvents[key] = eventsToMap[key]; }); return mappedEvents; }, acceptArbitraryProps: (type: string) => { const compDesc = desc[type]; return compDesc?.allowArbitraryProps ?? false; }, getComponentValidator: (type: string) => { if (type === "Button") { return (instance, devMode) => { if (devMode && (instance.props as any)?.label?.startsWith("q")) { return "Label should not start with 'q'"; } return null; }; } }, }; } ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/StandaloneExtensionManager.ts: -------------------------------------------------------------------------------- ```typescript import type { Extension } from "../abstractions/ExtensionDefs"; type ExtensionRegisteredCallbackFn = (extension: Extension) => void; /** * This class allows external component libraries to add their components to * the xmlui component registry. The framework resolves the components used * in an application markup with this registry. */ export default class StandaloneExtensionManager { subscriptions: Set<ExtensionRegisteredCallbackFn> = new Set(); registeredExtensions: Array<Extension> = []; constructor() {} /** * You can add a callback function invoked whenever a new component is added * to the registry. When you register a new callback function, the component * manager automatically invokes it for all components already in the * registry. * @param cb Function to call when a new component is registered */ subscribeToRegistrations(cb: ExtensionRegisteredCallbackFn) { this.subscriptions.add(cb); this.registeredExtensions.forEach((component) => { cb(component); }); } /** * You can remove a function added by `subscribeToRegistrations`. After * calling this method, the particular callback function won't be invoked * for a new component registration. * @param cb Function to call when a new component is registered */ unSubscribeFromRegistrations(cb: ExtensionRegisteredCallbackFn) { this.subscriptions.delete(cb); } registerExtension(component: Extension | Extension[]) { (Array.isArray(component) ? component : [component]).forEach((component) => { this.registeredExtensions.push(component); this.subscriptions.forEach((cb: ExtensionRegisteredCallbackFn) => { cb(component); }); }); } } ``` -------------------------------------------------------------------------------- /docs/public/resources/files/howto/component-icons/up-arrow.svg: -------------------------------------------------------------------------------- ``` <?xml version="1.0" encoding="iso-8859-1"?> <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 429.658 429.658" xml:space="preserve"> <g> <g> <path d="M235.252,13.406l-0.447-0.998c-3.417-7.622-11.603-12.854-19.677-12.375l-0.3,0.016l-0.302-0.016 C214.194,0.011,213.856,0,213.524,0c-7.706,0-15.386,5.104-18.674,12.413l-0.452,0.998L13.662,176.079 c-6.871,6.183-6.495,12.657-4.971,16.999c2.661,7.559,10.361,13.373,18.313,13.82l1.592,0.297c0.68,0.168,1.356,0.348,2.095,0.427 c23.036,2.381,45.519,2.876,64.472,3.042l5.154,0.048V407.93c0,11.023,7.221,15.152,11.522,16.635l0.967,0.33l0.77,0.671 c3.105,2.717,7.02,4.093,11.644,4.093h179.215c4.626,0,8.541-1.376,11.639-4.093l0.771-0.671l0.965-0.33 c4.307-1.482,11.532-5.611,11.532-16.635V210.706l5.149-0.048c18.961-0.17,41.446-0.666,64.475-3.042 c0.731-0.079,1.407-0.254,2.082-0.422l1.604-0.302c7.952-0.447,15.65-6.262,18.312-13.82c1.528-4.336,1.899-10.811-4.972-16.998 L235.252,13.406z M344.114,173.365c-11.105,0.18-22.216,0.254-33.337,0.254c-5.153,0-9.363,1.607-12.507,4.768 c-3.372,3.4-5.296,8.48-5.266,13.932l0.005,0.65l-0.157,0.629c-0.437,1.767-0.64,3.336-0.64,4.928v194.001H137.458V198.526 c0-1.597-0.201-3.161-0.638-4.928l-0.157-0.629l0.005-0.65c0.031-5.456-1.892-10.537-5.271-13.937 c-3.141-3.161-7.353-4.763-12.507-4.768c-11.124,0-22.224-0.074-33.337-0.254l-13.223-0.218L214.834,44.897l142.503,128.249 L344.114,173.365z"/> </g> </g> </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/SlotItem.tsx: -------------------------------------------------------------------------------- ```typescript import { memo, useMemo } from "react"; import type { ComponentDef } from "../abstractions/ComponentDefs"; import type { ContainerWrapperDef } from "../components-core/rendering/ContainerWrapper"; import { useShallowCompareMemoize } from "../components-core/utils/hooks"; import { EMPTY_OBJECT } from "../components-core/constants"; import type { LayoutContext, RenderChildFn } from "../abstractions/RendererDefs"; type SlotItemProps = { node: ComponentDef | Array<ComponentDef>; slotProps?: any; renderChild: RenderChildFn; layoutContext?: LayoutContext; }; /** * This React component wraps the slot content defined in a parent component into a container. * This container may contain context values pushed from a compound component back to * the parent. */ export const SlotItem = memo( ({ node, renderChild, layoutContext, slotProps = EMPTY_OBJECT }: SlotItemProps) => { const shallowMemoedSlotProps = useShallowCompareMemoize(slotProps); // --- Transform all Slot properties into context values so that they can be // --- used in the slot content (in the parent component) const nodeWithItem = useMemo(() => { const templateProps = {}; Object.entries(shallowMemoedSlotProps).forEach(([key, value]) => { templateProps["$" + key] = value; }); // --- Create a container for the slot content with the cotext values return { type: "Container", contextVars: templateProps, children: Array.isArray(node) ? node : [node], } as ContainerWrapperDef; }, [node, shallowMemoedSlotProps]); // --- Render the slot content return <>{renderChild(nodeWithItem, layoutContext)}</>; }, ); SlotItem.displayName = "SlotItem"; ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileUploadDropZone/FileUploadDropZone.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START ## Using `FileUploadDropZone` The component provides a surface on which you can drag files or paste files from the clipboard. The following example demonstrates how to use the component. ```xmlui-pg copy display name="Example: using FileUploadDropZone" height="200px" ---app copy display <App> <H3>The cyan area below is a FileUploadDropZone</H3> <FileUploadDropZone backgroundColor="cyan" height="100px" onUpload=" (files) => { console.log(files); files.map(file => toast('file ' + file.path + ' uploaded'))}" /> </App> ---desc You can try it by dragging one or more files to the cyan surface. When you drop the file(s), the app triggers the `upload` event and displays a dialog for each file. You can also paste files from the clipboard: click the drop zone (cyan area) and then use the keyboard shortcut set on your OS. ``` %-DESC-END %-PROP-START allowPaste This property indicates if the drop zone accepts files pasted from the clipboard (`true`) or only dragged files (`false`). The following example sets this property to `false` and, thus, it turns off pasting files: ```xmlui-pg copy display name="Example: allowPaste" height="200px" ---app copy display <App> <H3>You cannot paste files from the clipboard</H3> <FileUploadDropZone backgroundColor="cyan" height="100px" allowPaste="false" onUpload="(files) => files.map(file => toast('file ' + file.path + ' uploaded'))" /> </App> ---desc Try it! When you copy a file to a clipboard, you cannot paste it with the keyboard shortcut of your OS. ``` %-PROP-END %-EVENT-START upload Each item passed in the event argument is an instance of [File](https://developer.mozilla.org/en-US/docs/Web/API/File). %-EVENT-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/Backdrop/Backdrop.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./Backdrop.module.scss"; import { createComponentRenderer } from "../../components-core/renderers"; import { parseScssVar } from "../../components-core/theming/themeVars"; import { createMetadata, dComponent } from "../../components/metadata-helpers"; import { Backdrop } from "./BackdropNative"; const COMP = "Backdrop"; // See reference implementation here: https://getbootstrap.com/docs/5.3/components/alerts/ export const BackdropMd = createMetadata({ status: "stable", description: `The \`${COMP}\` component is a semi-transparent overlay that appears on ` + `top of its child component to obscure or highlight the content behind it.`, props: { overlayTemplate: dComponent( "This property defines the component template for an optional overlay to display " + "over the component.", ), backgroundColor: { description: "The background color of the backdrop.", valueType: "string", }, opacity: { description: "The opacity of the backdrop.", valueType: "string", }, }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { [`backgroundColor-${COMP}`]: "black", [`opacity-${COMP}`]: "0.1", } }); export const backdropComponentRenderer = createComponentRenderer( COMP, BackdropMd, ({ node, extractValue, renderChild, className }) => { return ( <Backdrop className={className} overlayTemplate={renderChild(node.props?.overlayTemplate)} backgroundColor={extractValue.asOptionalString(node.props.backgroundColor, undefined)} opacity={extractValue.asOptionalString(node.props.opacity, undefined)} > {renderChild(node.children)} </Backdrop> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/FormSection/FormSection.ts: -------------------------------------------------------------------------------- ```typescript import { createUserDefinedComponentRenderer } from "../../components-core/renderers"; import { createMetadata } from "../metadata-helpers"; import componentSource from "./FormSection.xmlui"; const COMP = "FormSection"; export const FormSectionMd = createMetadata({ status: "experimental", description: "`FormSection` groups elements within a `Form`. Child components are placed in " + "a [FlowLayout](/components/FlowLayout).", props: { heading: { description: "The heading text to be displayed at the top of the form section.", type: "string", }, headingLevel: { description: "The semantic and visual level of the heading.", availableValues: ["h1", "h2", "h3", "h4", "h5", "h6"], defaultValue: "h3", }, headingWeight: { description: "The font weight of the heading.", type: "string", defaultValue: "bold", }, info: { description: "Informational text displayed below the heading.", type: "string", }, infoFontSize: { description: "The font size of the informational text.", type: "string", defaultValue: "0.8rem", }, paddingTop: { description: "The top padding of the FlowLayout where the section's children are placed.", type: "string", defaultValue: "$space-normal", }, columnGap: { description: "The gap between columns of items within the section.", type: "string", defaultValue: "3rem", }, rowGap: { description: "The gap between rows of items within the section.", type: "string", defaultValue: "$space-normal", }, }, }); export const formSectionRenderer = createUserDefinedComponentRenderer( FormSectionMd, componentSource, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/abstractions/LoaderRenderer.ts: -------------------------------------------------------------------------------- ```typescript import type { ReactNode } from "react"; import type { LookupAsyncFn, LookupSyncFn } from "../../abstractions/ActionDefs"; import type { ComponentDef, ComponentMetadata } from "../../abstractions/ComponentDefs"; import type { RegisterComponentApiFn, ValueExtractor } from "../../abstractions/RendererDefs"; import type { ContainerState } from "../rendering/ContainerWrapper"; import type { ContainerDispatcher } from "./ComponentRenderer"; // This function renders a loader definition into a React component export type LoaderRenderer<TMd extends ComponentMetadata> = ( context: RendererContext<TMd>, ) => ReactNode; // Defines the traits of a loader renderer export interface LoaderRendererDef { // The loader's type identifier type: string; // The renderer function of the loader renderer: LoaderRenderer<any>; // Loader descriptor hints?: ComponentMetadata; } export type LoaderInProgressChangedFn = (isInProgress: boolean) => void; export type LoaderLoadedFn = (data: any, pageInfo?: any) => void; export type LoaderErrorFn = (error: any) => void; export type TransformResultFn = (data: any) => any; // The context in which a particular component is rendered type RendererContext<TMd extends ComponentMetadata> = { // The definition of the loader loader: ComponentDef<TMd>; // Loader state state: ContainerState; // Dispatcher function to change the state of the component dispatch: ContainerDispatcher; registerComponentApi: RegisterComponentApiFn; extractValue: ValueExtractor; lookupAction: LookupAsyncFn; lookupSyncCallback: LookupSyncFn; loaderInProgressChanged: LoaderInProgressChangedFn; loaderIsRefetchingChanged: LoaderInProgressChangedFn; loaderLoaded: LoaderLoadedFn; loaderError: LoaderErrorFn; }; ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/Playground.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer, createMetadata, parseScssVar } from "xmlui"; import { Playground } from "./PlaygroundNative"; const COMP = "Playground"; export const PlaygroundMd = createMetadata({ description: "XMLUI Playground component.", status: "experimental", props: { app: { description: "", }, name: { description: "The name of the component to be rendered.", }, api: { description: "", }, description: { description: "The description of the component to be rendered.", }, previewOnly: { description: "If true, the component will be rendered in preview mode only.", }, }, themeVars: parseScssVar({}), defaultThemeVars: {}, }); export const playgroundComponentRenderer = createComponentRenderer( COMP, PlaygroundMd, ({ extractValue, node }: any) => { return ( <Playground height={extractValue(node.props.height)} initialEditorHeight={extractValue.asOptionalString(node.props.initialEditorHeight)} swapped={extractValue.asOptionalBoolean(node.props.swapped)} horizontal={extractValue.asOptionalBoolean(node.props.horizontal)} allowStandalone={extractValue.asOptionalBoolean(node.props.allowStandalone)} fixedTheme={extractValue.asOptionalBoolean(node.props.fixedTheme)} themes={extractValue(node.props.themes)} previewOnly={extractValue.asBoolean(node.props.previewOnly)} components={extractValue(node.props.components)} app={extractValue.asOptionalString(node.props.app)} name={extractValue.asOptionalString(node.props.name)} api={extractValue(node.props.api)} description={extractValue.asOptionalString(node.props.description)} /> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/ValidationSummary/ValidationSummary.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } @mixin validationDisplayVariant($variantName) { &.#{$variantName} { background-color: createThemeVar("backgroundColor-ValidationDisplay-#{$variantName}"); &::before { background-color: createThemeVar("color-accent-ValidationDisplay-#{$variantName}"); } .heading { color: createThemeVar("textColor-ValidationDisplay-#{$variantName}"); font-weight: bold; } } } @layer components { .summaryContainer { display: flex; flex-direction: column; gap: 0.5rem; &:empty{ display: none; } } .validationContainer { position: relative; padding-top: 0; padding-bottom: 0.5rem; padding-left: 1rem; &::before { position: absolute; top: 0; left: 0; display: block; content: ""; height: 100%; width: 2px; } ul { list-style-position: inside; padding-left: 0; } li { padding-left: 2rem; &::marker { font-size: 0.625em; } } .noMarker { list-style: none; padding-left: 1.6rem; line-height: 1.6rem; } @include validationDisplayVariant("error"); @include validationDisplayVariant("warning"); @include validationDisplayVariant("info"); @include validationDisplayVariant("valid"); } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /docs/public/pages/howto/delay-a-datasource-until-another-datasource-is-ready.md: -------------------------------------------------------------------------------- ```markdown # Delay a DataSource until another DataSource is ready ```xmlui-pg noHeader ---app <App> <Test /> </App> ---api { "apiUrl": "/api", "initialize": "$state.users_for_ds_dependency = [ { id: 1, name: 'Alice', departmentId: 1 }, { id: 2, name: 'Bob', departmentId: 2 } ]; $state.departments_with_ds_dependency = [ { id: 1, name: 'Engineering' }, { id: 2, name: 'Marketing' } ]", "operations": { "get_users_for_ds_dependency": { "url": "/users_for_ds_dependency", "method": "get", "handler": "delay(1000); return $state.users_for_ds_dependency" }, "get_departments_with_ds_dependency": { "url": "/departments_with_ds_dependency", "method": "get", "handler": "delay(1000); return $state.departments_with_ds_dependency" } } } ---comp display <Component name="Test" var.selectedId="" var.nonce="{0}"> <DataSource id="users_for_ds_dependency" url="/api/users_for_ds_dependency?nonce" inProgressNotificationMessage="Loading users..." when="{ nonce > 0 }" /> <DataSource id="departments_with_ds_dependency" url="/api/departments_with_ds_dependency" when="{ users_for_ds_dependency.loaded }" inProgressNotificationMessage="Loading departments..." /> <Select id="usersForDsDepencency" data="{users_for_ds_dependency}" when="{departments_with_ds_dependency.loaded}" onDidChange="(newVal) => selectedId = newVal" > <Items data="{users_for_ds_dependency}"> <Option value="{$item.id}" label="{$item.name} ({departments_with_ds_dependency.value.find(d => d.id === $item.departmentId)?.name})" /> </Items> </Select> <Button label="Run" onClick="{nonce++}" /> </Component> ``` ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/inspect.svg: -------------------------------------------------------------------------------- ``` <svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg"> <g filter="url(#filter0_d_8732_12671)"> <rect x="10" y="10" width="36" height="36" rx="6" fill="#143566" shape-rendering="crispEdges"/> <path d="M28.834 18.6667H23.0007C22.5586 18.6667 22.1347 18.8423 21.8221 19.1549C21.5096 19.4675 21.334 19.8914 21.334 20.3334V33.6667C21.334 34.1088 21.5096 34.5327 21.8221 34.8453C22.1347 35.1578 22.5586 35.3334 23.0007 35.3334H33.0007C33.4427 35.3334 33.8666 35.1578 34.1792 34.8453C34.4917 34.5327 34.6673 34.1088 34.6673 33.6667V24.5001L28.834 18.6667Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M28.834 18.6667V24.5001H34.6673" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M29.666 30.3333L31.3327 28.6667L29.666 27" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M26.334 26.9999L24.6673 28.6666L26.334 30.3333" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </g> <defs> <filter id="filter0_d_8732_12671" x="0" y="0" width="56" height="56" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> <feFlood flood-opacity="0" result="BackgroundImageFix"/> <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> <feOffset/> <feGaussianBlur stdDeviation="5"/> <feComposite in2="hardAlpha" operator="out"/> <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/> <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_8732_12671"/> <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_8732_12671" result="shape"/> </filter> </defs> </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/container-helpers.tsx: -------------------------------------------------------------------------------- ```typescript import { memo, useMemo } from "react"; import type { ComponentDef } from "../abstractions/ComponentDefs"; import type { LayoutContext, RenderChildFn } from "../abstractions/RendererDefs"; import type { ContainerWrapperDef } from "../components-core/rendering/ContainerWrapper"; import { EMPTY_OBJECT } from "../components-core/constants"; import { useShallowCompareMemoize } from "../components-core/utils/hooks"; import { rest } from "lodash-es"; type MemoizedItemProps = { node: ComponentDef | Array<ComponentDef>; item?: any; context?: any; renderChild: RenderChildFn; layoutContext?: LayoutContext; contextVars?: Record<string, any>; itemKey?: string; contextKey?: string; }; export const MemoizedItem = memo( ({ node, item, context, renderChild, layoutContext, contextVars = EMPTY_OBJECT, itemKey = "$item", contextKey = "$context", }: MemoizedItemProps) => { const shallowMemoedContextVars = useShallowCompareMemoize(contextVars); const nodeWithItem = useMemo(() => { if (itemKey === contextKey) { return { type: "Container", contextVars: { [itemKey]: { ...item, ...context }, ...shallowMemoedContextVars, }, children: Array.isArray(node) ? node : [node], } as ContainerWrapperDef; } return { type: "Container", contextVars: { [itemKey]: item, [contextKey]: context, ...shallowMemoedContextVars, }, children: Array.isArray(node) ? node : [node], } as ContainerWrapperDef; }, [context, item, node, shallowMemoedContextVars, itemKey, contextKey]); return <>{renderChild(nodeWithItem, layoutContext)}</>; }, ); MemoizedItem.displayName = "MemoizedItem"; ``` -------------------------------------------------------------------------------- /xmlui/src/components/ColorPicker/ColorPicker.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START ## Using `ColorPicker` This component allows you to edit or select a color using RGB, HSL, or CSS HEX notation. It displays a popup over the UI and lets you use the mouse or keyboard to edit or select a color. ```xmlui-pg copy display name="Example: using ColorPicker" <App> <ColorPicker id="colorPicker" label="Select your favorite color" /> <Text>Selected color: {colorPicker.value}</Text> </App> ``` %-DESC-END %-PROP-START initialValue ```xmlui-pg copy display name="Example: using ColorPicker" <App> <ColorPicker id="colorPicker" label="Select your favorite color" initialValue="#ff0080" /> <Text>Selected color: {colorPicker.value}</Text> </App> ``` %-PROP-END %-PROP-START validationStatus ```xmlui-pg copy display name="Example: validationStatus" <App> <ColorPicker initialValue="#c0c0ff" label="Valid" validationStatus="valid" /> <ColorPicker initialValue="#c0c0ff" label="Warning" validationStatus="warning" /> <ColorPicker initialValue="#c0c0ff" label="Error" validationStatus="error" /> </App> ``` %-PROP-END %-PROP-START readOnly ```xmlui-pg copy display name="Example: readOnly" <App> <ColorPicker initialValue="#ffff00" label="Cannot be edited" readOnly /> </App> ``` %-PROP-END %-API-START setValue ```xmlui-pg copy display name="Example: setValue" <App> <App> <ColorPicker id="colorPicker" label="Select your favorite color" initialValue="#808080" /> <HStack> <Button label="Set to red" onClick="colorPicker.setValue('#ff0000')" /> <Button label="Set to green" onClick="colorPicker.setValue('#00c000')" /> <Button label="Set to blue" onClick="colorPicker.setValue('#0000ff')" /> </HStack> </App> </App> ``` %-API-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileInput/FileInput.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } // --- The FileInput component uses a TextBox and a Button. We do not allow separately customize the // --- FileInput theme, only through the TextBox and Button themes. @layer components { .container { gap: t.$space-2; width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: flex-start; } .buttonStart { flex-direction: row-reverse; } .buttonEnd { flex-direction: row; } .textBoxWrapper { width: 100%; border-radius: t.$borderRadius; min-height: fit-content; &:focus-within { border-radius: createThemeVar("Input:borderRadius-FileInput--focus"); border-color: createThemeVar("Input:borderColor-FileInput--focus"); background-color: createThemeVar("Input:backgroundColor-FileInput--focus"); box-shadow: createThemeVar("Input:boxShadow-FileInput--focus"); color: createThemeVar("Input:textColor-FileInput--focus"); } &:has(.input:focus-visible) { outline-width: createThemeVar("Input:outlineWidth-FileInput--focus"); outline-color: createThemeVar("Input:outlineColor-FileInput--focus"); outline-style: createThemeVar("Input:outlineStyle-FileInput--focus"); outline-offset: createThemeVar("Input:outlineOffset-FileInput--focus"); } } .button { flex-shrink: 0; } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Footer/Footer.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } // --- Theme vars for paddings $component: "Footer"; $themeVars: t.composePaddingVars($themeVars, $component); $themeVars: t.composeBorderVars($themeVars, $component) !global; // --- Other theme vars $backgroundColor-Footer: createThemeVar("backgroundColor-#{$component}"); $textColor-Footer: createThemeVar("textColor-#{$component}"); $height-Footer: createThemeVar("height-#{$component}"); $fontSize-Footer: createThemeVar("fontSize-#{$component}"); $verticalAlignment-Footer: createThemeVar("verticalAlignment-#{$component}"); $maxWidth-content-Footer: createThemeVar("maxWidth-content-#{$component}"); $gap-Footer: createThemeVar("gap-#{$component}"); $maxWidth-App: createThemeVar("maxWidth-App"); $margin-Footer: createThemeVar("margin-#{$component}"); @layer components { .outerWrapper{ @include t.borderVars($themeVars, $component); background-color: $backgroundColor-Footer; color: $textColor-Footer; height: $height-Footer; } .wrapper { width: 100%; display: flex; min-height: 0; height: 100%; flex-direction: row; gap: $gap-Footer; @include t.paddingVars($themeVars, $component); font-size: $fontSize-Footer; align-items: $verticalAlignment-Footer; max-width: $maxWidth-content-Footer; margin: $margin-Footer; &.full{ max-width: $maxWidth-App; } } } // --- We export the theme variables to add them to the component renderer :export{ themeVars: t.json-stringify($themeVars) } ``` -------------------------------------------------------------------------------- /xmlui/src/components/RadioGroup/RadioItemNative.tsx: -------------------------------------------------------------------------------- ```typescript import type React from "react"; import { useCallback, useId } from "react"; import styles from "./RadioGroup.module.scss"; import * as InnerRadioGroup from "@radix-ui/react-radio-group"; import { noop } from "../../components-core/constants"; import classnames from "classnames"; import { convertOptionValue } from "../Option/OptionNative"; export const defaultProps = { checked: false, value: "", }; type RadioItemProps = { checked: boolean; style?: React.CSSProperties; value?: string; onDidChange?: (value: string) => void; }; export const RadioItem = ({ checked = defaultProps.checked, style, value = defaultProps.value, onDidChange = noop, }: RadioItemProps) => { const id = useId(); return ( <div key={id} className={styles.radioOptionContainer} style={style}> <UnwrappedRadioItem id={id} checked={checked} value={value} onDidChange={onDidChange} /> </div> ); }; type UnwrappedRadioItemProps = Omit<RadioItemProps, "style"> & { id: string; statusStyles?: Record<string, boolean>; disabled?: boolean; onDidChange?: (value: string) => void; }; export const UnwrappedRadioItem = ({ id, checked = defaultProps.checked, value = defaultProps.value, statusStyles, disabled, onDidChange = noop, }: UnwrappedRadioItemProps) => { const onInputChange = useCallback( (_: React.MouseEvent<HTMLButtonElement>) => { onDidChange(value); }, [onDidChange, value], ); return ( <InnerRadioGroup.Item className={classnames(styles.radioOption, statusStyles)} id={id} value={convertOptionValue(value)} checked={checked} disabled={disabled} onClick={onInputChange} > <InnerRadioGroup.Indicator className={classnames(styles.indicator, statusStyles)} /> </InnerRadioGroup.Item> ); }; ``` -------------------------------------------------------------------------------- /xmlui/src/components/Spinner/SpinnerNative.tsx: -------------------------------------------------------------------------------- ```typescript import type { CSSProperties, ForwardedRef } from "react"; import { forwardRef, useEffect, useState } from "react"; import styles from "./Spinner.module.scss"; import classnames from "classnames"; const PART_RING = "ring"; export const defaultProps = { delay: 400, fullScreen: false, }; type SpinnerProps = { delay?: number; fullScreen?: boolean; style?: CSSProperties; className?: string; }; // source https://loading.io/css/ export const Spinner = forwardRef(function Spinner( { delay = defaultProps.delay, fullScreen = defaultProps.fullScreen, style, className, ...rest }: SpinnerProps, forwardedRef: ForwardedRef<HTMLDivElement>, ) { const [pastDelay, setPastDelay] = useState(delay === 0); useEffect(() => { const timeout = setTimeout(() => { setPastDelay(true); }, delay); return () => { clearTimeout(timeout); }; }, [delay]); if (!pastDelay) { return null; } else { if (fullScreen) { return ( <div {...rest} role="status" aria-label="Loading" className={styles.fullScreenSpinnerWrapper} > <div className={classnames(styles["lds-ring"], className)} style={style} ref={forwardedRef} > <div data-part-id={PART_RING}></div> <div></div> <div></div> <div></div> </div> </div> ); } return ( <div {...rest} className={classnames(styles["lds-ring"], className)} role="status" aria-label="Loading" style={style} ref={forwardedRef} > <div data-part-id={PART_RING}></div> <div></div> <div></div> <div></div> </div> ); } }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/ContentSeparator/ContentSeparator.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $backgroundColor-ContentSeparator: createThemeVar("backgroundColor-ContentSeparator"); $size-ContentSeparator: createThemeVar("size-ContentSeparator"); $marginTop-ContentSeparator: createThemeVar("marginTop-ContentSeparator"); $marginBottom-ContentSeparator: createThemeVar("marginBottom-ContentSeparator"); $marginVertical-ContentSeparator: createThemeVar("marginVertical-ContentSeparator"); $marginLeft-ContentSeparator: createThemeVar("marginLeft-ContentSeparator"); $marginRight-ContentSeparator: createThemeVar("marginRight-ContentSeparator"); $marginHorizontal-ContentSeparator: createThemeVar("marginHorizontal-ContentSeparator"); @layer components { .separator { background-color: $backgroundColor-ContentSeparator; margin-top: t.createVarWithDefault("marginTop-ContentSeparator", #{$marginVertical-ContentSeparator}); margin-bottom: t.createVarWithDefault("marginBottom-ContentSeparator", #{$marginVertical-ContentSeparator}); margin-left: t.createVarWithDefault("marginLeft-ContentSeparator", #{$marginHorizontal-ContentSeparator}); margin-right: t.createVarWithDefault("marginRight-ContentSeparator", #{$marginHorizontal-ContentSeparator}); &.horizontal { height: $size-ContentSeparator; width: 100%; } &.vertical { width: $size-ContentSeparator; min-height: 100%; } } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /packages/xmlui-os-frames/src/index.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer, createMetadata } from "xmlui"; import { WindowsAppFrame } from "./WindowsAppFrame"; import { MacOSAppFrame } from "./MacOSAppFrame"; import { IPhoneFrame } from "./IPhoneFrame"; import windowsStyles from "./WindowsAppFrame.module.scss"; import macStyles from "./MacOSAppFrame.module.scss"; import iphoneStyles from "./IPhoneFrame.module.scss"; export const windowsAppFrameMd = createMetadata({ name: "WindowsAppFrame", description: `App Frame for Windows.`, status: "experimental", themeVars: windowsStyles.themeVars, defaultThemeVars: { "backgroundColor-content-WindowsAppFrame": "$backgroundColor", light: { "backgroundColor-content-WindowsAppFrame": "white" } } }); export const macAppFrameMd = createMetadata({ name: "MacOSAppFrame", description: `App Frame for MacOS.`, status: "experimental", themeVars: macStyles.themeVars, defaultThemeVars: {} }); export const iphoneFrameMd = createMetadata({ name: "IPhoneFrame", description: `App Frame for IPhone.`, status: "experimental", themeVars: iphoneStyles.themeVars, defaultThemeVars: {} }); export default { namespace: "XMLUIExtensions", components: [ createComponentRenderer( "WindowsAppFrame", windowsAppFrameMd, ({ node, renderChild }: any) => { return <WindowsAppFrame>{renderChild(node.children)}</WindowsAppFrame>; } ), createComponentRenderer( "MacOSAppFrame", macAppFrameMd, ({ node, renderChild }: any) => { return <MacOSAppFrame>{renderChild(node.children)}</MacOSAppFrame>; } ), createComponentRenderer( "IPhoneFrame", iphoneFrameMd, ({ node, renderChild }: any) => { return <IPhoneFrame>{renderChild(node.children)}</IPhoneFrame>; } ) ] }; ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/theming/extendThemeUtils.ts: -------------------------------------------------------------------------------- ```typescript import { cloneDeep } from "lodash-es"; import { RootThemeDefinition } from "../theming/themes/root"; import type { DefaultThemeVars, ThemeDefinition, ThemeDefinitionDetails, ThemeTone } from "../../abstractions/ThemingDefs"; function collectExtends(cTheme: ThemeDefinition | undefined, allThemes: Array<ThemeDefinition>) { if (!cTheme) { return []; } if (!cTheme.extends) { return []; } const arrayExtends = typeof cTheme.extends === "string" ? [cTheme.extends] : cTheme.extends; const ret: Array<ThemeDefinition> = []; arrayExtends.forEach((ext: string) => { const parentTheme = allThemes.find((theme) => theme.id === ext); if (parentTheme) { ret.push(...collectExtends(parentTheme, allThemes)); ret.push(parentTheme); } }); return ret; } export function collectThemeChainByExtends( customTheme: ThemeDefinition, allThemes: Array<ThemeDefinition>, componentDefaultThemeVars: DefaultThemeVars, ) { const rootThemeVars: Record<string, string> = cloneDeep(RootThemeDefinition.themeVars) || {}; const rootTones: Record<string | ThemeTone, ThemeDefinitionDetails> = cloneDeep(RootThemeDefinition.tones) || {}; Object.entries(componentDefaultThemeVars).forEach(([key, value]) => { if (typeof value === "string") { rootThemeVars[key.trim()] = value; } else { Object.entries(value).forEach(([themeVarKey, themeVarVal]) => { if (!rootTones[key]) { rootTones[key] = { themeVars: {} }; } rootTones[key].themeVars = { ...rootTones[key].themeVars, [themeVarKey.trim()]: themeVarVal, }; }); } }); const root = { id: "root", themeVars: rootThemeVars, resources: {}, tones: rootTones, }; return [root, ...collectExtends(customTheme, allThemes), customTheme]; } ```