This is page 6 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/Redirect/Redirect.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START ## Using `Redirect` The following app demonstrates two different patterns for using `Redirect`. 1. When you navigate to the "Redirect #1" page, it immediately redirects the app to the "Accounts" page. By default, the `when` property of `Redirect` (and any other component) is "true", so redirection immediately happens. 2. The "Redirect #2" page expects you to click the button before redirecting. The button click sets the `when` property of `Redirect` to true, and redirection happens at that moment. ```xmlui-pg copy {14, 20} display name="Example: providing children" height="170px" <App> <NavPanel> <NavLink to="/">Home</NavLink> <NavLink to="/accounts">Accounts</NavLink> <NavLink to="/products">Products</NavLink> <NavLink to="/redirect1">Redirect #1</NavLink> <NavLink to="/redirect2">Redirect #2</NavLink> </NavPanel> <Pages> <Page url="/">Home</Page> <Page url="/accounts">Accounts</Page> <Page url="/products">Products</Page> <Page url="/redirect1"> <Redirect to="/accounts" /> Redirecting to Accounts... </Page> <Page url="/redirect2"> <Fragment var.clicked="{false}"> <Button label="Click to redirect" onClick="clicked = true"/> <Redirect when="{clicked}" to="/accounts" /> Redirecting to Accounts... </Fragment> </Page> </Pages> </App> ``` %-DESC-END ``` -------------------------------------------------------------------------------- /docs/content/components/ToneChangerButton.md: -------------------------------------------------------------------------------- ```markdown # ToneChangerButton [#tonechangerbutton] `ToneChangerButton` enables the user to switch between light and dark modes. ```xmlui-pg {4} copy display name="Example: using ToneChangerButton" <App> <AppHeader> <SpaceFiller /> <ToneChangerButton /> </AppHeader> <Card title="Tone Changer Button" subtitle="Click the button in the header to change the tone." /> </App> ``` ## Properties [#properties] ### `darkToLightIcon` (default: "darkToLight:ToneChangerButton") [#darktolighticon-default-darktolight-tonechangerbutton] The icon displayed when the theme is in dark mode and will switch to light. You can change the default icon for all ToneChangerButton instances with the "icon.darkToLight:ToneChangerButton" declaration in the app configuration file. ### `lightToDarkIcon` (default: "lightToDark:ToneChangerButton") [#lighttodarkicon-default-lighttodark-tonechangerbutton] The icon displayed when the theme is in light mode and will switch to dark. You can change the default icon for all ToneChangerButton instances with the "icon.lightToDark:ToneChangerButton" declaration in the app configuration file. ## Events [#events] ### `click` [#click] This event is triggered when the ToneChangerButton is clicked. ## Exposed Methods [#exposed-methods] This component does not expose any methods. ## Styling [#styling] This component does not have any styles. ``` -------------------------------------------------------------------------------- /xmlui/src/components/Logo/Logo.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START Most apps use `logo="path/to/logo.svg"` on the App component rather than using `<Logo>` directly. Use this component when you need custom logo positioning or want to combine logos with other elements in a `logoTemplate`. ## Using Logo The framework checks the application manifest for a logo resource (SVG file). If found, it loads and displays it in the Logo component. This is a sample manifest that shows a logo definition: ```json copy {5} { "name": "Tutorial", "version": "0.0.1", "resources": { "logo": "resources/xmlui-logo.svg", "favicon": "resources/favicon.ico" } } ``` In the following example, you can see a custom logo definition in the `AppHeader` via templating. There is a `Heading` with the title text "MyApp" before the logo. It also uses the `Logo` component within the template definition: ```xmlui-pg ---app copy display name="Example: using Logo" {6} height="200px" <App layout="horizontal"> <AppHeader> <property name="logoTemplate"> <Fragment> <Heading level="h2" value="MyApp"/> <Logo/> </Fragment> </property> </AppHeader> <NavPanel> <NavLink label="Home" to="/" icon="home"/> </NavPanel> <Pages fallbackPath="/"> <Page url="/"> <CHStack> (Sample content) </CHStack> </Page> </Pages> </App> ---desc The markup displays the app's logo: ``` %-DESC-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/Theme/NotificationToast.tsx: -------------------------------------------------------------------------------- ```typescript import type { CSSProperties} from "react"; import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import toast, { ToastBar, Toaster } from "react-hot-toast"; import { useTheme } from "../../components-core/theming/ThemeContext"; const TOASTER_CONTAINER_STYLE: CSSProperties = { top: 40, right: 40, bottom: 40, left: 40, position: "absolute", overflowY: "hidden", }; type NotificationToastProps = { toastDuration?: number; }; let toasterMounted = false; export const NotificationToast = ({ toastDuration }: NotificationToastProps) => { const [shouldRender, setShouldRender] = useState(false); const { root } = useTheme(); useEffect(() => { if (!toasterMounted) { toasterMounted = true; setShouldRender(true); } }, []); if (!shouldRender) return null; return createPortal( <Toaster position={"top-right"} containerStyle={TOASTER_CONTAINER_STYLE} toastOptions={{ style: { padding: "12px 16px" }, duration: toastDuration }} > {(t) => ( <div onClick={() => toast.dismiss(t.id)}> <ToastBar position={t.position} toast={t} style={{ wordBreak: "break-word" }}> {({ icon, message }) => ( <> {icon} {message} </> )} </ToastBar> </div> )} </Toaster>, root, ); }; ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/Legend/Legend.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer } from "../../../components-core/renderers"; import { createMetadata } from "../../metadata-helpers"; import { defaultProps, horizontalAlignmentValues, Legend, verticalAlignmentValues, } from "./LegendNative"; const COMP = "Legend"; export const LegendMd = createMetadata({ status: "experimental", description: "`Legend` provides a standalone legend for chart components when you need " + "custom positioning or styling beyond the chart's built-in `showLegend` " + "property. Most charts can display legends automatically, but this component " + "offers precise control over legend placement and alignment.", props: { align: { description: "The alignment of the legend", valueType: "string", availableValues: horizontalAlignmentValues, defaultValue: defaultProps.align, }, verticalAlign: { description: "The vertical alignment of the legend", valueType: "string", availableValues: verticalAlignmentValues, defaultValue: defaultProps.verticalAlign, }, }, }); export const legendComponentRenderer = createComponentRenderer( COMP, LegendMd, ({ extractValue, node }: any) => { return ( <Legend align={extractValue.asOptionalString(node.props?.align)} verticalAlign={extractValue.asOptionalString(node.props?.verticalAlign)} /> ); }, ); ``` -------------------------------------------------------------------------------- /docs/content/components/DonutChart.md: -------------------------------------------------------------------------------- ```markdown # DonutChart [#donutchart] A derivative of [PieChart](/components/PieChart) with a hollow center. Note that the height of the component or its parent needs to be set explicitly. ## Properties [#properties] ### `data` [#data] The data to be displayed in the chart. Needs to be an array of objects. ### `dataKey` [#datakey] This property specifies the key in the data objects that will be used to render the chart. ### `innerRadius` (default: 60) [#innerradius-default-60] Sets the inner radius of the donut chart. ### `nameKey` [#namekey] Specifies the key in the data objects that will be used to label the different data series. ### `showLabel` (default: true) [#showlabel-default-true] Toggles whether to show labels (`true`) or not (`false`). ### `showLabelList` (default: false) [#showlabellist-default-false] Whether to show labels in a list (`true`) or not (`false`). ### `showLegend` (default: false) [#showlegend-default-false] Whether to show a legend (`true`) or not (`false`). ## Events [#events] This component does not have any events. ## Exposed Methods [#exposed-methods] This component does not expose any methods. ## Styling [#styling] ### Theme Variables [#theme-variables] | Variable | Default Value (Light) | Default Value (Dark) | | --- | --- | --- | | [textColor](../styles-and-themes/common-units/#color)-labelList-PieChart | $textColor-primary | $textColor-primary | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/module-parse.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect, it } from "vitest"; import { ModuleErrors, isModuleErrors, parseScriptModule, } from "../../../src/parsers/scripting/modules"; import { ScriptModule } from "../../../src/components-core/script-runner/ScriptingSourceTree"; const ROOT_MODULE = "test"; describe("Modules - Parse", () => { it("Empty module", async () => { // --- Arrange const source = ` `; // --- Act const result = parseModule(source); // --- Assert expect(!isModuleErrors(result)).toBe(true); }); it("Module functions", async () => { // --- Arrange const source = ` function func1() {} function func2() {} `; // --- Act const result = parseModule(source) as ScriptModule; // --- Assert expect(!isModuleErrors(result)).toBe(true); expect(Object.keys(result.functions).length).toBe(2); expect(result.functions["func1"]).toBeDefined(); expect(result.functions["func2"]).toBeDefined(); }); it("Parsing error", async () => { // --- Arrange const source = `const;`; // --- Act const result = parseModule(source); // --- Assert expect(isModuleErrors(result)).toBe(true); const errors = result as ModuleErrors; expect(Object.keys(errors).length).toBe(1); }); }); function parseModule(source: string, modules: Record<string, string> = {}) { return parseScriptModule(ROOT_MODULE, source); } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Markdown/CodeText.tsx: -------------------------------------------------------------------------------- ```typescript import type { CSSProperties } from "react"; import type React from "react"; import { forwardRef, useRef } from "react"; import { composeRefs } from "@radix-ui/react-compose-refs"; import classnames from "classnames"; import styles from "./CodeText.module.scss"; type CodeTextProps = { uid?: string; children?: React.ReactNode; style?: CSSProperties; className?: string; [variantSpecificProps: string]: any; }; export const defaultProps = { maxLines: 0, preserveLinebreaks: false, ellipses: true, }; /** * This type implements code text we use in Markdown codefences. */ export const CodeText = forwardRef(function CodeText( { uid, style, className, children, preserveLinebreaks = defaultProps.preserveLinebreaks, ellipses = defaultProps.ellipses, ...variantSpecificProps }: CodeTextProps, forwardedRef, ) { const innerRef = useRef<HTMLElement>(null); const ref = forwardedRef ? composeRefs(innerRef, forwardedRef) : innerRef; // NOTE: This is to accept syntax highlight classes coming from shiki // classes need not to be added to the rendered html element, so we remove them from props return ( <> <pre {...variantSpecificProps} ref={ref as any} className={classnames(styles.codeText, styles.codefence, className)} style={{ ...style, }} > {children} </pre> </> ); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Carousel/CarouselContext.tsx: -------------------------------------------------------------------------------- ```typescript import { createContext, useContext, useMemo, useState } from "react"; import produce from "immer"; export const CarouselContext = createContext({ register: (column: any) => {}, unRegister: (id: string) => {}, itemProps: {}, }); export const useCarousel = () => { const context = useContext(CarouselContext); if (!context) { throw new Error("useCarousel must be used within a Carousel"); } return context; }; export function useCarouselContextValue(isTabbed: boolean) { const [carouselItems, setCarouselItems] = useState([]); const carouselContextValue = useMemo(() => { return { register: (column: any) => { setCarouselItems( produce((draft) => { const existing = draft.findIndex((col) => col.id === column.id); if (existing < 0) { draft.push(column); } else { draft[existing] = column; } }), ); }, unRegister: (id: string) => { setCarouselItems( produce((draft) => { return draft.filter((col) => col.id !== id); }), ); }, itemProps: isTabbed ? { role: "group tabpanel", } : { role: "group", "aria-roledescription": "slide", }, }; }, [setCarouselItems, isTabbed]); return { carouselItems, carouselContextValue, }; } ``` -------------------------------------------------------------------------------- /docs/content/components/SubMenuItem.md: -------------------------------------------------------------------------------- ```markdown # SubMenuItem [#submenuitem] `SubMenuItem` creates hierarchical menu structures by acting as both a menu item and a container for nested menu items. When clicked or hovered, it reveals a submenu containing additional [MenuItem](/components/MenuItem), [MenuSeparator](/components/MenuSeparator), or other [SubMenuItem](/components/SubMenuItems) components, enabling complex multi-level navigation and action organization. **Key features:** - **Hierarchical nesting**: Creates multi-level menu structures within [DropdownMenu](/components/DropdownMenu) components - **Dual functionality**: Acts as both a clickable menu item and a container for other menu components - **Custom triggers**: Configurable trigger appearance via triggerTemplate property - **Progressive disclosure**: Reveals nested options on hover or click interaction - **Unlimited depth**: Supports multiple levels of nesting for complex menu hierarchies ## Properties [#properties] ### `label` [#label] This property sets the label of the component. If not set, the component will not display a label. ### `triggerTemplate` [#triggertemplate] This property allows you to define a custom trigger instead of the default one provided by `SubMenuItem`. ## Events [#events] This component does not have any events. ## Exposed Methods [#exposed-methods] This component does not expose any methods. ## Styling [#styling] This component does not have any styles. ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/rendering/collectFnVarDeps.ts: -------------------------------------------------------------------------------- ```typescript /** * This function collects all the dependencies of a function in a flat list * (no circular deps). * @param fnDeps The dependencies of the functions. Each key is a function name * and the value is an array of variables the function depends on. * @returns The flat dependencies of the functions. * * Example: * it("simple", () => { * const fnDeps = { * fn1: ["fn2", "var1", "var2"], * fn2: ["var1", "var3", "fn3"], * fn3: ["var4"] * }; * * expect(collectFnVarDeps(fnDeps)).deep.eq({ * fn1: ["var1", "var3", "var4", "var2"], * fn2: ["var1", "var3", "var4"], * fn3: ["var4"] * }); * }); */ export function collectFnVarDeps(fnDeps: Record<string, string[]> = {}): Record<string, string[]> { const fnKeys = Object.keys(fnDeps); function collectFnDeps(depKey: string, visitedPath = new Set<string>()) { if (!fnKeys.includes(depKey)) { return [depKey]; } visitedPath.add(depKey); const ret: Array<string> = []; fnDeps[depKey].forEach((key) => { if (visitedPath.has(key)) { // --- We already walked here, avoid infinite loops for circular deps return; } visitedPath.add(key); ret.push(...collectFnDeps(key, visitedPath)); }); return ret; } const flatFnDeps: Record<string, string[]> = {}; fnKeys.forEach((key) => { flatFnDeps[key] = [...new Set(collectFnDeps(key))]; }); return flatFnDeps; } ``` -------------------------------------------------------------------------------- /xmlui/src/components/ProfileMenu/ProfileMenu.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./ProfileMenu.module.scss"; import { useThemes } from "../../components-core/theming/ThemeContext"; import { Avatar } from "../Avatar/AvatarNative"; import { DropdownMenu, MenuItem, MenuSeparator } from "../DropdownMenu/DropdownMenuNative"; // ===================================================================================================================== // Heading React component type Props = { loggedInUser: any | null; }; export const ProfileMenu = ({ loggedInUser }: Props) => { const { activeThemeId, setActiveThemeId } = useThemes(); if (!loggedInUser) { return null; } const loggedInUserName = loggedInUser.name || loggedInUser.displayName; return ( <DropdownMenu triggerTemplate={<Avatar url={loggedInUser.avatarUrl} name={loggedInUserName} size={"xs"} />}> <div className={styles.loggedInUserInfoWrapper}> <div className={styles.name}>{loggedInUserName}</div> <div className={styles.email}>{loggedInUser.email}</div> </div> <MenuSeparator /> {activeThemeId.includes("dark") && <MenuItem onClick={() => setActiveThemeId(activeThemeId.replace("dark", "light"))}>Switch to light mode</MenuItem>} {activeThemeId.includes("light") && <MenuItem onClick={() => setActiveThemeId(activeThemeId.replace("light", "dark"))}>Switch to dark mode</MenuItem>} <MenuSeparator /> <MenuItem>Log out</MenuItem> </DropdownMenu> ); }; ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/StandalonePlaygroundNative.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as themes; .standalonePlayground { display: flex; flex-direction: column; height: 100%; width: 100%; min-width: 0; min-height: 0; } .loadingContainer { position: absolute; left: 50%; top: calc(50% - 25px); transform: translateX(-50%); /* Center perfectly on the X axis */ display: flex; align-items: center; justify-content: center; /* Center the content horizontally */ width: 300px; height: 50px; } .logoWrapper { width: 50px; height: 50px; transform: translateZ(0); .animatedLogoPath { will-change: stroke-dashoffset, stroke-dasharray; /* The total length of the rectangle path is ~82. */ stroke-dasharray: 82; stroke-dashoffset: 82; /* Animation: name, duration, easing, and iteration count */ animation: draw-loop 3s ease-in-out infinite; } } .loadingText { margin-right: 12px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 16px; /* Increased by 20% from 16px */ font-weight: 500; color: themes.$color-primary-500; text-align: right; } /* Defines the animation steps */ @keyframes draw-loop { /* At the start, the path is not drawn */ 0% { stroke-dashoffset: 82; } /* Animate to fully drawn over 40% of the duration */ 40% { stroke-dashoffset: 0; } /* Hold the fully drawn state until the end */ 100% { stroke-dashoffset: 0; } } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Input/PartialInput.module.scss: -------------------------------------------------------------------------------- ```scss // Common styles for PartialInput component // These styles contain only the shared patterns from multi-field input components // Component-specific styling (including widths) should be applied via className prop @layer components { .partialInput { // Base layout and sizing height: 100%; position: relative; border: 0; background: none; color: currentColor; font: inherit; box-sizing: border-box; text-align: center; // Remove browser default styling -webkit-appearance: textfield; -moz-appearance: textfield; appearance: textfield; // Hide spin buttons for number inputs (all browsers) &::-webkit-outer-spin-button, &::-webkit-inner-spin-button { -webkit-appearance: none !important; -moz-appearance: none !important; appearance: none !important; margin: 0 !important; display: none !important; } // Hide spin buttons for Firefox specifically &[type="number"] { -moz-appearance: textfield !important; appearance: textfield !important; } // Focus state &:focus { outline: none; // Component-specific focus styling should be applied by parent } // Disabled state &:disabled { opacity: 0.6; cursor: not-allowed; } // Read-only state &[readonly] { cursor: default; } // Common placeholder styling &::placeholder { opacity: 0.6; } } } ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/xmlui/xmlui.ts: -------------------------------------------------------------------------------- ```typescript import { nodeToComponentDef } from "../../../src/parsers/xmlui-parser/transform"; import type { ComponentDef, CompoundComponentDef } from "../../../src/abstractions/ComponentDefs"; import { createXmlUiParser, ParseResult } from "../../../src/parsers/xmlui-parser/parser"; import type { GetText } from "../../../src/parsers/xmlui-parser/parser"; import { toDbgString } from "../../../src/parsers/xmlui-parser/utils"; export function transformSource( source: string, fileId?: number, printRes: boolean = false, ): ComponentDef | CompoundComponentDef | undefined { const { getText, parse } = createXmlUiParser(source); const { node, errors } = parse(); if (printRes) { console.log(toDbgString(node, getText)); console.log( "errors: \n[\n" + errors.map((e) => e.message + ` @${e.pos}`).join(";\n") + "\n]\n", ); } if (errors.length > 0) { // return {errors} throw new Error(errors[0].message); } return nodeToComponentDef(node, getText, fileId ?? 0); } export function parseSource( source: string, printRes: boolean = false, ): ParseResult & { getText: GetText } { const parser = createXmlUiParser(source); const parseRes = parser.parse(); if (printRes) { console.log(toDbgString(parseRes.node, parser.getText)); console.log( "errors: \n[\n" + parseRes.errors.map((e) => e.message + ` @${e.pos}`).join(";\n") + "\n]\n", ); } return { ...parseRes, getText: parser.getText }; } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Spinner/Spinner.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./Spinner.module.scss"; import { createComponentRenderer } from "../../components-core/renderers"; import { parseScssVar } from "../../components-core/theming/themeVars"; import { Spinner, defaultProps } from "./SpinnerNative"; import { createMetadata } from "../metadata-helpers"; const COMP = "Spinner"; export const SpinnerMd = createMetadata({ status: "stable", description: "`Spinner` is an animated indicator that represents an action in progress " + "with no deterministic progress value.", props: { delay: { description: `The delay in milliseconds before the spinner is displayed.`, valueType: "number", defaultValue: defaultProps.delay, }, fullScreen: { description: `If set to \`true\`, the component will be rendered in a full screen container.`, valueType: "boolean", defaultValue: defaultProps.fullScreen, }, }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { [`size-${COMP}`]: "$space-10", [`thickness-${COMP}`]: "$space-0_5", [`borderColor-${COMP}`]: "$color-surface-400", }, }); export const spinnerComponentRenderer = createComponentRenderer( COMP, SpinnerMd, ({ node, className, extractValue }) => { return ( <Spinner className={className} delay={extractValue.asOptionalNumber(node.props.delay)} fullScreen={extractValue.asOptionalBoolean(node.props.fullScreen)} /> ); }, ); ``` -------------------------------------------------------------------------------- /packages/xmlui-devtools/src/devtools/Tooltip.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as themes; .TooltipContent { border-radius: 4px; padding: 10px 15px; font-size: 15px; line-height: 1; color: themes.$textColor-primary; background-color: themes.$backgroundColor; box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); user-select: none; animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; } .TooltipContent[data-state='delayed-open'][data-side='top'] { animation-name: slideDownAndFade; } .TooltipContent[data-state='delayed-open'][data-side='right'] { animation-name: slideLeftAndFade; } .TooltipContent[data-state='delayed-open'][data-side='bottom'] { animation-name: slideUpAndFade; } .TooltipContent[data-state='delayed-open'][data-side='left'] { animation-name: slideRightAndFade; } @keyframes slideUpAndFade { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideRightAndFade { from { opacity: 0; transform: translateX(-2px); } to { opacity: 1; transform: translateX(0); } } @keyframes slideDownAndFade { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideLeftAndFade { from { opacity: 0; transform: translateX(2px); } to { opacity: 1; transform: translateX(0); } } ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/Tooltip.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as themes; .TooltipContent { border-radius: 4px; padding: 10px 15px; font-size: 15px; line-height: 1; color: themes.$textColor-primary; background-color: themes.$backgroundColor; box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); user-select: none; animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; } .TooltipContent[data-state='delayed-open'][data-side='top'] { animation-name: slideDownAndFade; } .TooltipContent[data-state='delayed-open'][data-side='right'] { animation-name: slideLeftAndFade; } .TooltipContent[data-state='delayed-open'][data-side='bottom'] { animation-name: slideUpAndFade; } .TooltipContent[data-state='delayed-open'][data-side='left'] { animation-name: slideRightAndFade; } @keyframes slideUpAndFade { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideRightAndFade { from { opacity: 0; transform: translateX(-2px); } to { opacity: 1; transform: translateX(0); } } @keyframes slideDownAndFade { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideLeftAndFade { from { opacity: 0; transform: translateX(2px); } to { opacity: 1; transform: translateX(0); } } ``` -------------------------------------------------------------------------------- /xmlui/src/logging/LoggerService.ts: -------------------------------------------------------------------------------- ```typescript type LoggerCallback = (args: any[]) => void; class LoggerService { private static instance: LoggerService; private callback?: LoggerCallback; private isDev: boolean; private constructor() { this.isDev = process.env.NODE_ENV === "development" || !!process.env.VITE_DEV_MODE; } public static getInstance(): LoggerService { if (!LoggerService.instance) { LoggerService.instance = new LoggerService(); } return LoggerService.instance; } public registerCallback(callback: LoggerCallback) { if (this.isDev) { this.callback = callback; } } public log(args: any[]) { if (!this.isDev) return; console.log("[xmlui.log]", ...args); if (this.callback) { this.callback(args); } } public warn(args: any[]) { if (!this.isDev) return; console.warn("[xmlui.warn]", ...args); if (this.callback) { this.callback(args); } } public info(args: any[]) { if (!this.isDev) return; console.info("[xmlui.info]", ...args); if (this.callback) { this.callback(args); } } public error(args: any[]) { if (!this.isDev) return; console.error("[xmlui.error]", ...args); if (this.callback) { this.callback(args); } } public trace(args: any[]) { if (!this.isDev) return; console.trace("[xmlui.trace]", ...args); if (this.callback) { this.callback(args); } } } export const loggerService = LoggerService.getInstance(); ``` -------------------------------------------------------------------------------- /xmlui/src/parsers/scripting/TokenType.ts: -------------------------------------------------------------------------------- ```typescript // Token types available for parsing export enum TokenType { Eof = -1, Ws = -2, BlockComment = -3, EolComment = -4, Unknown = 0, // --- Binding Expression specific tokens LParent, RParent, Identifier, Exponent, Divide, Multiply, Remainder, Plus, Minus, BitwiseXor, BitwiseOr, LogicalOr, BitwiseAnd, LogicalAnd, IncOp, DecOp, Assignment, AddAssignment, SubtractAssignment, ExponentAssignment, MultiplyAssignment, DivideAssignment, RemainderAssignment, ShiftLeftAssignment, ShiftRightAssignment, SignedShiftRightAssignment, BitwiseAndAssignment, BitwiseXorAssignment, BitwiseOrAssignment, LogicalAndAssignment, LogicalOrAssignment, NullCoalesceAssignment, Semicolon, Comma, Colon, LSquare, RSquare, QuestionMark, NullCoalesce, OptionalChaining, BinaryNot, LBrace, RBrace, Equal, StrictEqual, LogicalNot, NotEqual, StrictNotEqual, LessThan, LessThanOrEqual, ShiftLeft, GreaterThan, GreaterThanOrEqual, ShiftRight, SignedShiftRight, Dot, Spread, Global, Backtick, DollarLBrace, Arrow, DecimalLiteral, HexadecimalLiteral, BinaryLiteral, RealLiteral, StringLiteral, Infinity, NaN, True, False, Typeof, Null, Undefined, In, Let, Const, Var, If, Else, Return, Break, Continue, Do, While, For, Of, Try, Catch, Finally, Throw, Switch, Case, Default, Delete, Function, As, } ``` -------------------------------------------------------------------------------- /xmlui/src/components/HtmlTags/HtmlTags.spec.ts: -------------------------------------------------------------------------------- ```typescript import { getBounds } from "../../testing/component-test-helpers"; import { expect, test } from "../../testing/fixtures"; test.describe("smoke tests", { tag: "@smoke" }, () => { test("htmlTable is rendered", async ({ initTestBed, createHtmlTagDriver }) => { await initTestBed(`<table />`); const driver = await createHtmlTagDriver(); await expect(driver.component).toBeAttached(); }); }); const tableCode = ` <table> <thead> <tr> <th scope="col">Person</th> <th scope="col">Most interest in</th> <th scope="col">Age</th> </tr> </thead> <tbody> <tr> <th scope="row">Chris</th> <td>HTML tables</td> <td>22</td> </tr> <tr> <th scope="row">Dennis</th> <td>Web accessibility</td> <td>45</td> </tr> <tr> <th scope="row">Sarah</th> <td>JavaScript frameworks</td> <td>29</td> </tr> <tr> <th scope="row">Karen</th> <td>Web performance</td> <td>36</td> </tr> </tbody> <tfoot> <tr> <th scope="row" colSpan="2">Average age</th> <td>33</td> </tr> </tfoot> </table> `; test("htmlTable width using themes", async ({ initTestBed, createHtmlTagDriver }) => { const { width } = await initTestBed(tableCode, { testThemeVars: { "width-HtmlTable": "100%", }, }); const driver = await createHtmlTagDriver(); const compWidth = (await getBounds(driver)).width; expect(compWidth).toEqual(width); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileUploadDropZone/FileUploadDropZone.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-FileUploadDropZone: createThemeVar("backgroundColor-FileUploadDropZone"); $textColor-FileUploadDropZone: createThemeVar("textColor-FileUploadDropZone"); $backgroundColor-dropping-FileUploadDropZone: createThemeVar( "backgroundColor-dropping-FileUploadDropZone" ); $opacity-dropping-FileUploadDropZone: createThemeVar("opacity-dropping-FileUploadDropZone"); @layer components { .wrapper { position: relative; background-color: $backgroundColor-FileUploadDropZone; color: $textColor-FileUploadDropZone; display: flex; flex-direction: column; } .dropPlaceholder { position: absolute; inset: 0; z-index: 1; pointer-events: none; display: flex; flex: 1; align-items: center; justify-content: center; flex-direction: column; &:before { content: ""; position: absolute; inset: 0; background-color: $backgroundColor-dropping-FileUploadDropZone; opacity: $opacity-dropping-FileUploadDropZone; //0.5; } } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Fragment/Fragment.spec.ts: -------------------------------------------------------------------------------- ```typescript import { expect, test } from "../../testing/fixtures"; // ============================================================================= // BASIC FUNCTIONALITY TESTS // ============================================================================= test.describe("Basic Functionality", () => { test("parent has items", async ({ page, initTestBed }) => { const expectedChildCount = 3; const expectedChildrenDisplay = ["Item #1", "Item #2", "Item #3"]; await initTestBed(` <Stack testId="container"> <Fragment> <Text value='Item #1' /> <Text value='Item #2' /> <Text value='Item #3' /> </Fragment> </Stack>`); const children = page.locator(`[data-testid="container"] > *`); await expect(children).toHaveCount(expectedChildCount); await expect(children).toHaveText(expectedChildrenDisplay); }); test("parent style affects children", async ({ page, initTestBed }) => { const expectedFontSize = "14px"; await initTestBed(` <Stack testId="container" fontSize="${expectedFontSize}"> <Fragment> <Text value='Item #1' /> <Text value='Item #2' /> <Text value='Item #3' /> </Fragment> </Stack>`, { testThemeVars: { "font-size": "16px", }, }, ); for (const child of await page.locator(`[data-testid="container"] > *`).all()) { await expect(child).toHaveCSS("font-size", expectedFontSize); } }); }); ``` -------------------------------------------------------------------------------- /tools/create-app/create-app.ts: -------------------------------------------------------------------------------- ```typescript import { green } from "picocolors"; import path from "path"; import { makeDir } from "./helpers/make-dir"; import { tryGitInit } from "./helpers/git"; import { isFolderEmpty } from "./helpers/is-folder-empty"; import { isWriteable } from "./helpers/is-writeable"; import type { PackageManager } from "./helpers/get-pkg-manager"; import type { TemplateType } from "./templates"; import { installTemplate } from "./templates"; export async function createApp({ appPath, packageManager, useGit }: { appPath: string; packageManager: PackageManager; useGit: boolean; }): Promise<void> { const template: TemplateType = "default"; const root = path.resolve(appPath); if (!(await isWriteable(path.dirname(root)))) { console.error("The application path is not writable, please check folder permissions and try again."); console.error("It is likely you do not have write permissions for this folder."); process.exit(1); } const appName = path.basename(root); await makeDir(root); if (!isFolderEmpty(root, appName)) { process.exit(1); } console.log(`Creating a new UI Engine app in ${green(root)}.`); console.log(); process.chdir(root); await installTemplate({ appName, root, template, packageManager, useGit }); if (useGit && tryGitInit(root)) { console.log("Initialized a git repository."); console.log(); } console.log(`${green("Success!")} Created ${appName} at ${appPath}`); console.log(); } ``` -------------------------------------------------------------------------------- /blog/package.json: -------------------------------------------------------------------------------- ```json { "name": "xmlui-blog", "private": true, "version": "0.0.9", "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": "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": { "@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" } } ``` -------------------------------------------------------------------------------- /docs/public/pages/howto/make-a-set-of-equal-width-cards.md: -------------------------------------------------------------------------------- ```markdown # Make a set of equal-width cards ```xmlui-pg noHeader ---app <App> <Test /> </App> ---api { "apiUrl": "/api", "initialize": "$state.dashboard_stats = { \"draft_invoices\":6, \"outstanding\":3502.9, \"paid_invoices\":79, \"paid_this_year\":1745.18, \"sent_invoices\":43, \"total_clients\":30, \"total_invoices\":91 }", "operations": { "get-dashboard-stats": { "url": "/dashboard_stats", "method": "get", "handler": "$state.dashboard_stats" } } } ---comp display <Component name="Test" > <DataSource id="dashboard_stats" url="/api/dashboard_stats" method="GET" /> <FlowLayout> <InfoCard width="*" <!-- use star sizing --> title="Outstanding" value="{ dashboard_stats.value.outstanding }" /> <InfoCard width="*" title="Paid This Year" value="{ dashboard_stats.value.paid_this_year }" /> <InfoCard width="*" title="Draft" value="{ dashboard_stats.value.draft_invoices }" /> <InfoCard width="*" title="Sent" value="{ dashboard_stats.value.sent_invoices }" /> </FlowLayout> </Component> ---comp display <Component name="InfoCard"> <Card width="{$props.width}" borderRadius="8px" boxShadow="$boxShadow-spread"> <Text>{$props.title}</Text> <Text fontWeight="$fontWeight-extra-bold" fontSize="larger"> { $props.value } </Text> </Card> </Component> ``` ``` -------------------------------------------------------------------------------- /xmlui/src/components/Tabs/TabContext.tsx: -------------------------------------------------------------------------------- ```typescript import { createContext, useContext, useMemo, useState } from "react"; import produce from "immer"; import { EMPTY_ARRAY } from "../../components-core/constants"; import type { Tab } from "../abstractions"; type TabItem = Tab & { innerId: string }; interface ITabContext{ register: (tabItem: TabItem) => void; unRegister: (innerId: string) => void; activeTabId: string; } export const TabContext = createContext<ITabContext>({ register: (tabItem) => {}, unRegister: (innerId: string) => {}, activeTabId: "", }); export function useTabContextValue() { const [tabItems, setTabItems] = useState<TabItem[]>(EMPTY_ARRAY); const [activeTabId, setActiveTabId] = useState<string>(""); const tabContextValue = useMemo(() => { return { register: (tabItem: TabItem) => { setTabItems( produce((draft) => { const existing = draft.findIndex((col) => col.innerId === tabItem.innerId); if (existing < 0) { draft.push(tabItem); } else { draft[existing] = tabItem; } }), ); }, unRegister: (innerId: string) => { setTabItems( produce((draft) => { return draft.filter((col) => col.innerId !== innerId); }), ); }, activeTabId, setActiveTabId, }; }, [activeTabId]); return { tabItems, tabContextValue, }; } export function useTabContext() { return useContext(TabContext); } ``` -------------------------------------------------------------------------------- /xmlui/src/components/EmojiSelector/EmojiSelectorNative.tsx: -------------------------------------------------------------------------------- ```typescript import { forwardRef } from "react"; import type { Theme as EmojiPickerTheme } from "emoji-picker-react"; import EmojiPicker, { EmojiStyle } from "emoji-picker-react"; import type { AsyncFunction } from "../../abstractions/FunctionDefs"; import { noop } from "../../components-core/constants"; // ===================================================================================================================== // React EmojiSelector component implementation export type EmojiSelectorProps = { theme?: EmojiPickerTheme; select?: AsyncFunction; autoFocus?: boolean; }; export const defaultProps: Pick<EmojiSelectorProps, "theme" | "select" | "autoFocus"> = { theme: "light" as EmojiPickerTheme, select: noop, autoFocus: false, }; export const EmojiSelector = forwardRef<HTMLDivElement, EmojiSelectorProps>( ( { select = defaultProps.select, theme = defaultProps.theme, autoFocus = defaultProps.autoFocus, ...rest }, ref, ) => ( <div {...rest} ref={ref} style={{ display: "inline-block", width: "fit-content", height: "fit-content" }} > <EmojiPicker autoFocusSearch={autoFocus} onEmojiClick={async (emojiObject) => { await select(emojiObject.emoji); }} lazyLoadEmojis={true} theme={theme} previewConfig={{ showPreview: false }} skinTonesDisabled={true} height={360} emojiStyle={EmojiStyle.NATIVE} /> </div> ), ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/App/SearchContext.tsx: -------------------------------------------------------------------------------- ```typescript import { createContext, useContextSelector } from "use-context-selector"; import { useCallback, useMemo, useState } from "react"; import { EMPTY_OBJECT } from "../../components-core/constants"; type SearchEntry = { path: string; title: string; content: string }; type ISearchContext = { content: Record<string, SearchEntry>; storeContent: ({ path, title, content }: SearchEntry) => void; isIndexing: boolean; setIsIndexing: (isIndexing: boolean) => void; }; const SearchContext = createContext<ISearchContext | null>(null); export const SearchContextProvider = ({children})=>{ const [content, setContent] = useState<Record<string, SearchEntry>>(EMPTY_OBJECT); const [isIndexing, setIsIndexing] = useState(true); const storeContent = useCallback((entry: SearchEntry) => { setContent((prevContent) => ({ ...prevContent, [entry.path]: entry, })); }, []); const value = useMemo(()=>({ content, storeContent, isIndexing, setIsIndexing }), [content, isIndexing, storeContent]); return <SearchContext.Provider value={value}>{children}</SearchContext.Provider> }; export const useSearchContextUpdater = () => { return useContextSelector(SearchContext, (value) => value.storeContent); }; export const useSearchContextContent = () => { return useContextSelector(SearchContext, (value) => value?.content); }; export const useSearchContextSetIndexing = () => { return useContextSelector(SearchContext, (value) => value.setIsIndexing); }; ``` -------------------------------------------------------------------------------- /xmlui/src/components/FlowLayout/FlowLayout.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); } @layer components { .outer { contain: layout; } .flowContainer { display: flex; flex-wrap: wrap; &.horizontal { flex-direction: row; .flowItem { flex-shrink: 0; & > * { width: 100% !important; //reset the wrapped content's width-like properties to their default min-width: 0 !important; max-width: none !important; } } } &.vertical { height: 100%; flex-direction: column; align-content: flex-start; .flowItem { width: auto; flex-shrink: 0; & > * { height: auto !important; min-height: 0 !important; max-height: none !important; } } } } //we use the .break class to create a new line in the flow // if there are multiple star-sized elements in a row, the break will be ignored between them .break:not(.forceBreak):has(+ .starSized) { display: none; } .break, .forceBreak { flex-basis: 100%; height: 0; } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/utils/DataLoaderQueryKeyGenerator.ts: -------------------------------------------------------------------------------- ```typescript import { isEqual } from "lodash-es"; import type { Query, QueryKey } from "@tanstack/react-query"; // base on this: https://tkdodo.eu/blog/effective-react-query-keys type UrlKeyPart = string; type UrlQueryParamsPart = Record<string, any>; type DataLoaderQueryKey = [UrlKeyPart, UrlQueryParamsPart?]; export class DataLoaderQueryKeyGenerator { private readonly url: UrlKeyPart; private readonly queryParams?: UrlQueryParamsPart; private key?: DataLoaderQueryKey; constructor( url: UrlKeyPart, queryParams: UrlQueryParamsPart | undefined, apiUrl: string | undefined, body: any, rawBody: any, ) { this.url = url; this.queryParams = queryParams; this.key = [url]; if (queryParams) { this.key.push(queryParams); } if (apiUrl) { this.key.push(apiUrl); } if (body) { this.key.push(body); } if (rawBody) { this.key.push(rawBody); } } asKey() { if (!this.key) { throw new Error("no key defined"); } return this.key; } asPredicate(): { predicate: (query: Query<unknown, unknown, unknown, QueryKey>) => boolean; } { if (!this.key) { throw new Error("no key defined"); } return { predicate: (query: Query<unknown, unknown, unknown, QueryKey>) => { const queryKey = query.queryKey as DataLoaderQueryKey; return ( queryKey[0] === this.url && (!this.queryParams || isEqual(queryKey[1], this.queryParams)) ); }, }; } } ``` -------------------------------------------------------------------------------- /xmlui/src/components/ProgressBar/ProgressBar.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./ProgressBar.module.scss"; import { createComponentRenderer } from "../../components-core/renderers"; import { parseScssVar } from "../../components-core/theming/themeVars"; import { ProgressBar, defaultProps } from "./ProgressBarNative"; import { createMetadata } from "../metadata-helpers"; const COMP = "ProgressBar"; export const ProgressBarMd = createMetadata({ status: "stable", description: "`ProgressBar` provides a visual indicator showing the completion percentage " + "of tasks, processes, or any measurable progress. It displays as a horizontal " + "bar that fills from left to right based on the provided value between 0 " + "(empty) and 1 (complete).", props: { value: { description: `This property defines the progress value with a number between 0 and 1.`, valueType: "number", defaultValue: defaultProps.value, }, }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { [`borderRadius-${COMP}`]: "$borderRadius", [`borderRadius-indicator-${COMP}`]: "0px", [`thickness-${COMP}`]: "$space-2", [`backgroundColor-${COMP}`]: "$color-surface-200", [`color-indicator-${COMP}`]: "$color-primary-500", }, }); export const progressBarComponentRenderer = createComponentRenderer( COMP, ProgressBarMd, ({ node, extractValue, className }) => { return ( <ProgressBar value={Math.max(0, Math.min(1, extractValue(node.props.value)))} className={className} /> ); }, ); ``` -------------------------------------------------------------------------------- /tools/vscode/src/extension.ts: -------------------------------------------------------------------------------- ```typescript import * as path from 'path'; import { ExtensionContext, } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node'; let client: LanguageClient; export function activate(context: ExtensionContext) { const serverModule = getPathToLangServer(context); // If the extension is launched in debug mode then the debug server options are used // Otherwise the run options are used const serverOptions: ServerOptions = { run: { module: serverModule, transport: TransportKind.ipc }, debug: { module: serverModule, transport: TransportKind.ipc, } }; // Options to control the language client const clientOptions: LanguageClientOptions = { documentSelector: [ { language: 'xmlui' } ] }; // Create the language client and start the client. client = new LanguageClient( 'XMLUILanguageService', 'XMLUI Language Service', serverOptions, clientOptions ); // Start the client. This will also launch the server client.start(); } export function deactivate(): Thenable<void> | undefined { if (!client) { return undefined; } return client.stop(); } function getPathToLangServer(context: ExtensionContext) { const localLangServPath = null; if (!localLangServPath){ const bundledLangServPath = context.asAbsolutePath(path.join('dist', 'server.js')); console.log({ bundledLangServPath }); return bundledLangServPath; } return localLangServPath; } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/Tooltip/TooltipContent.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $componentName: "TooltipContent"; @layer components { .tooltipContainer { display: grid; min-width: 8rem; align-items: start; gap: 1.5px; border-radius: 8px; background: t.$backgroundColor-secondary; padding: 0.4rem; font-size: 0.75rem; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); } .gridGap { display: grid; gap: 1.5px; } .labelGrid { display: grid; gap: 1.5px; } .itemContainer { display: flex; width: 100%; flex-wrap: wrap; align-items: center; gap: 4px; } .valueContainer { display: flex; flex: 1; gap: t.$space-2; justify-content: space-between; align-items: center; } .indicator { flex-shrink: 0; border-radius: 2px; } .dot { width: 10px; height: 10px; } .line { width: 4px; } .dashed { width: 0; border: 1.5px dashed; background: transparent; } } .mutedText { color: t.$textColor-secondary; font-size: 0.75rem; } .valueText { color: t.$textColor-primary; font-weight: 500; font-size: 0.75rem; } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/tests-e2e/datasource-and-api-usage-in-var.spec.ts: -------------------------------------------------------------------------------- ```typescript import { expect, test } from "../src/testing/fixtures"; test("regression: datasource usage in compound component var", async ({ page, initTestBed }) => { await initTestBed(`<TestComp />`, { components: [ ` <Component name="TestComp"> <Fragment var.sortFiles="{(files)=> files}"> <DataSource url="/data1" id="datasource"/> <variable name="x" value="{datasource.value.listing[0].name + '_transformed'}"/> <Text testId="transformed_text" value="{x}"/> </Fragment> </Component> `, ], apiInterceptor: { operations: { "load-api-data1": { url: "/data1", method: "get", handler: `() => { return {listing: [{name: 'data1'}]}; }`, }, }, }, }); await expect(page.getByTestId("transformed_text")).toHaveText("data1_transformed"); }); test("regression: api usage in compound component var", async ({ page, initTestBed }) => { await initTestBed(`<TestComp />`, { components: [ ` <Component name="TestComp"> <Stack var.itemsInVar="{uploadQueue.getQueuedItems()}"> <Queue id="uploadQueue" clearAfterFinish="false"/> <Button onClick="uploadQueue.enqueueItems([1, 2, 3])" testId="button">add to queue</Button> <Text testId="result">{itemsInVar.map(item => item.item)}</Text> </Stack> </Component> `, ], }); await page.getByTestId("button").click(); await expect(page.getByTestId("result")).toHaveText("123"); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/xls.svg: -------------------------------------------------------------------------------- ``` <svg width="22" height="28" viewBox="0 0 22 28" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M21.9982 7.38281L16.0756 5.74219L14.3835 0H1.69217C0.757586 0 0 0.734508 0 1.64062V26.3594C0 27.2655 0.757586 28 1.69217 28H20.3061C21.2407 28 21.9982 27.2655 21.9982 26.3594V7.38281Z" fill="#59C36A"/> <path d="M21.9982 7.38281V26.3594C21.9982 27.2655 21.2406 28 20.306 28H11.1345V0H14.3834L16.0756 5.74219L21.9982 7.38281Z" fill="#59C36A"/> <path d="M16.0753 11.5391H5.92226C5.4546 11.5391 5.07617 11.906 5.07617 12.3594V22.2031C5.07617 22.6565 5.4546 23.0234 5.92226 23.0234H16.0753C16.543 23.0234 16.9214 22.6565 16.9214 22.2031V12.3594C16.9214 11.906 16.543 11.5391 16.0753 11.5391ZM6.76834 16.4609H10.1527V18.1016H6.76834V16.4609ZM11.8449 16.4609H15.2292V18.1016H11.8449V16.4609ZM15.2292 14.8203H11.8449V13.1797H15.2292V14.8203ZM10.1527 13.1797V14.8203H6.76834V13.1797H10.1527ZM6.76834 19.7422H10.1527V21.3828H6.76834V19.7422ZM11.8449 21.3828V19.7422H15.2292V21.3828H11.8449Z" fill="white"/> <path d="M16.0758 11.5391H11.1365V23.0234H16.0758C16.5435 23.0234 16.9219 22.6565 16.9219 22.2031V12.3594C16.9219 11.906 16.5435 11.5391 16.0758 11.5391ZM15.2297 21.3828H11.8454V19.7422H15.2297V21.3828ZM15.2297 18.1016H11.8454V16.4609H15.2297V18.1016ZM15.2297 14.8203H11.8454V13.1797H15.2297V14.8203Z" fill="white"/> <path d="M21.9995 7.38281H16.0769C15.1462 7.38281 14.3848 6.64453 14.3848 5.74219V0C14.6047 0 14.8247 0.0820312 14.977 0.246148L21.7457 6.80865C21.9149 6.95625 21.9995 7.16953 21.9995 7.38281Z" fill="#A0DEB0"/> </svg> ``` -------------------------------------------------------------------------------- /packages/xmlui-website-blocks/src/Carousel/CarouselContext.tsx: -------------------------------------------------------------------------------- ```typescript import { createContext, useContext, useMemo, useState } from "react"; import produce from "immer"; interface CarouselItem { id: string; [key: string]: any; } export const CarouselContext = createContext({ register: (column: CarouselItem) => {}, unRegister: (id: string) => {}, itemProps: {}, }); export const useCarousel = () => { const context = useContext(CarouselContext); if (!context) { throw new Error("useCarousel must be used within a Carousel"); } return context; }; export function useCarouselContextValue(isTabbed: boolean) { const [carouselItems, setCarouselItems] = useState<CarouselItem[]>([]); const carouselContextValue = useMemo(() => { return { register: (column: CarouselItem) => { setCarouselItems( produce((draft: CarouselItem[]) => { const existing = draft.findIndex((col) => col.id === column.id); if (existing < 0) { draft.push(column); } else { draft[existing] = column; } }), ); }, unRegister: (id: string) => { setCarouselItems( produce((draft) => { return draft.filter((col) => col.id !== id); }), ); }, itemProps: isTabbed ? { role: "group tabpanel", } : { role: "group", "aria-roledescription": "slide", }, }; }, [setCarouselItems, isTabbed]); return { carouselItems, carouselContextValue, }; } ``` -------------------------------------------------------------------------------- /docs/public/pages/xmlui-charts/PieChart.md: -------------------------------------------------------------------------------- ```markdown # PieChart [#piechart] >[!WARNING] > This component is in an **experimental** state; you can use it in your app. However, we may modify it, and it may even have breaking changes in the future.Represents a pie chart component. ## Properties ### `data` The data to be displayed in the chart. Needs to be an array of objects. ### `dataKeys` This property specifies the keys in the data objects that should be used for rendering the bars. ### `height` The height of the chart ### `labelListPosition (default: "inside")` The position of the label list. Available values: `top`, `left`, `right`, `bottom`, `inside` **(default)**, `outside`, `insideLeft`, `insideRight`, `insideTop`, `insideBottom`, `insideTopLeft`, `insideBottomLeft`, `insideTopRight`, `insideBottomRight`, `insideStart`, `insideEnd`, `end`, `center`, `centerTop`, `centerBottom`, `middle` ### `nameKey` Specifies the key in the data objects that will be used to label the different data series. ### `showLabel (default: true)` Toggles whether to show labels (`true`) or not (`false`). ### `showLabelList (default: false)` Whether to show labels in a list (`true`) or not (`false`). ### `width` The width of the chart ## Events This component does not have any events. ## Exposed Methods This component does not expose any methods. ## Styling ### Theme Variables | Variable | Default Value (Light) | Default Value (Dark) | | --- | --- | --- | | [textColor](../styles-and-themes/common-units/#color)-labelList-PieChart | $textColor-primary | $textColor-primary | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/txt.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_14941)"> <path d="M21.7881 7.38281L16.374 5.74219L14.8271 0H3.22559C2.37125 0 1.67871 0.734508 1.67871 1.64062V26.3594C1.67871 27.2655 2.37125 28 3.22559 28H20.2412C21.0956 28 21.7881 27.2655 21.7881 26.3594V7.38281Z" fill="#EDEDED"/> <path d="M21.7881 7.38281V26.3594C21.7881 27.2655 21.0955 28 20.2412 28H11.8572V0H14.8271L16.374 5.74219L21.7881 7.38281Z" fill="#EDEDED"/> <path d="M21.7881 7.38281H16.374C15.5232 7.38281 14.8271 6.64453 14.8271 5.74219V0C15.0282 0 15.2293 0.0820312 15.3685 0.246148L21.556 6.80865C21.7107 6.95625 21.7881 7.16953 21.7881 7.38281Z" fill="#BDBDBD"/> <path d="M20.5333 22.4259C20.5333 22.925 20.1483 23.3333 19.6778 23.3333H0.855556C0.385 23.3333 0 22.925 0 22.4259V13.3519C0 12.8528 0.385 12.4444 0.855556 12.4444H19.6778C20.1483 12.4444 20.5333 12.8528 20.5333 13.3519V22.4259Z" fill="#161C28"/> <path d="M4.62629 16.508V20H5.56828V16.508H6.85229V15.716H3.34229V16.508H4.62629Z" fill="white"/> <path d="M8.99625 17.756L7.50225 20H8.55825L9.51225 18.518L10.4483 20H11.5703L10.0763 17.762L11.4503 15.716H10.4183L9.54825 17.084L8.70825 15.716H7.61625L8.99625 17.756Z" fill="white"/> <path d="M13.4962 16.508V20H14.4382V16.508H15.7222V15.716H12.2122V16.508H13.4962Z" fill="white"/> </g> <defs> <clipPath id="clip0_1811_14941"> <rect width="22" height="28" fill="white"/> </clipPath> </defs> </svg> ``` -------------------------------------------------------------------------------- /docs/content/components/ToneSwitch.md: -------------------------------------------------------------------------------- ```markdown # ToneSwitch [#toneswitch] `ToneSwitch` enables the user to switch between light and dark modes using a switch control. ```xmlui-pg {4} copy display name="Example: using ToneSwitch" <App> <AppHeader> <SpaceFiller /> <ToneSwitch /> </AppHeader> <Card title="Tone Switch" subtitle="Toggle the switch to change the tone." /> </App> ``` ## Properties [#properties] ### `iconDark` (default: "moon") [#icondark-default-moon] Icon to display for dark mode ### `iconLight` (default: "sun") [#iconlight-default-sun] Icon to display for light mode ## Events [#events] This component does not have any events. ## Exposed Methods [#exposed-methods] This component does not expose any methods. ## Styling [#styling] ### Theme Variables [#theme-variables] | Variable | Default Value (Light) | Default Value (Dark) | | --- | --- | --- | | [backgroundColor](../styles-and-themes/common-units/#color)-ToneSwitch-dark | $color-primary-500 | $color-primary-500 | | [backgroundColor](../styles-and-themes/common-units/#color)-ToneSwitch-light | $color-surface-200 | $color-surface-700 | | [borderColor](../styles-and-themes/common-units/#color)-ToneSwitch | $color-surface-200 | $color-surface-600 | | [borderColor](../styles-and-themes/common-units/#color)-ToneSwitch--hover | $color-surface-300 | $color-surface-500 | | [color](../styles-and-themes/common-units/#color)-ToneSwitch-dark | $color-surface-0 | $color-surface-0 | | [color](../styles-and-themes/common-units/#color)-ToneSwitch-light | $color-text-primary | $color-text-primary | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/admonition_warning.svg: -------------------------------------------------------------------------------- ``` <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M15.8283 6C16.5981 4.66667 18.5226 4.66667 19.2924 6L31.8498 27.75C32.6196 29.0833 31.6573 30.75 30.1177 30.75H5.00298C3.46338 30.75 2.50113 29.0833 3.27093 27.75L15.8283 6Z" fill="url(#paint0_linear_11265_27105)"/> <path d="M15.8278 6C16.5976 4.66667 18.5229 4.66667 19.2927 6L31.8493 27.75C32.595 29.0416 31.7158 30.646 30.2605 30.7451L30.1179 30.75V29.75C30.8875 29.7498 31.3687 28.9166 30.9841 28.25L18.4265 6.5C18.0657 5.87505 17.1975 5.83607 16.7731 6.38281L16.694 6.5L4.13643 28.25C3.75177 28.9166 4.23306 29.7498 5.00264 29.75V30.75L4.86006 30.7451C3.4517 30.6492 2.5826 29.1436 3.20381 27.876L3.2712 27.75L15.8278 6ZM30.1179 29.75V30.75H5.00264V29.75H30.1179Z" fill="#FFD93C"/> <path d="M18.9476 12.0938L18.7226 22.572H16.5272L16.309 12.0938H18.9476ZM17.6249 27.0938C17.2112 27.0938 16.8567 26.9392 16.5612 26.63C16.2703 26.3208 16.1249 25.944 16.1249 25.4995C16.1249 25.0648 16.2703 24.6928 16.5612 24.3836C16.8567 24.0744 17.2112 23.9198 17.6249 23.9198C18.0294 23.9198 18.3794 24.0744 18.6749 24.3836C18.9749 24.6928 19.1249 25.0648 19.1249 25.4995C19.1249 25.7942 19.0544 26.0623 18.9135 26.3039C18.7772 26.5454 18.5953 26.7387 18.3681 26.8836C18.1453 27.0237 17.8976 27.0938 17.6249 27.0938Z" fill="black"/> <defs> <linearGradient id="paint0_linear_11265_27105" x1="17.5604" y1="3" x2="17.5604" y2="40" gradientUnits="userSpaceOnUse"> <stop stop-color="#FFE980"/> <stop offset="0.932692" stop-color="#FFD93C"/> </linearGradient> </defs> </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/LabelList/LabelList.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./LabelListNative.module.scss"; import { defaultProps, LabelList } from "./LabelListNative"; import { LabelPositionValues } from "../utils/abstractions"; import { parseScssVar } from "../../../components-core/theming/themeVars"; import { createComponentRenderer } from "../../../components-core/renderers"; import { createMetadata } from "../../metadata-helpers"; const COMP = "LabelList"; export const LabelListMd = createMetadata({ status: "experimental", description: "`LabelList` adds custom data labels to chart components when automatic " + "labeling isn't sufficient. It's a specialized component for advanced chart " + "customization scenarios where you need precise control over label positioning " + "and appearance.", props: { key: { description: "The key that needs to be matched to the data series.", valueType: "string", }, position: { description: "The position of the label list", valueType: "string", availableValues: LabelPositionValues, defaultValue: defaultProps.position, }, }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { "textColor-LabelList": "$textColor-primary", }, }); export const labelListComponentRenderer = createComponentRenderer( COMP, LabelListMd, ({ extractValue, node, className }: any) => { return ( <LabelList key={extractValue(node.props?.dataKey)} position={extractValue.asOptionalString(node.props?.position)} className={className} /> ); }, ); ``` -------------------------------------------------------------------------------- /docs/public/resources/files/downloads/downloads.json: -------------------------------------------------------------------------------- ```json { "engine": { "availableVersions": [ { "version": "0.9.51", "changelogs": [ "Added theme variable for setting the horizontal alignment of the logo in the NavPanel.", "Moved Drawer logo position to left.", "fix: Slider - min/max value validation", "fix: Markdown renders inline/block images correclty" ], "fileName": "xmlui-0.9.51.js", "fileSize": "3.81 MB" }, { "version": "0.9.50", "changelogs": [ "standalone usage: explicit codeBehind reference", "Make the \"marked\" Text variant have lighter background color in dark mode.", "Tweaked Search dropdown panel styles. Corrected Link component text and decoration hover and active colors" ], "fileName": "xmlui-0.9.51.js", "fileSize": "3.75 MB" }, { "version": "0.9.49", "changelogs": [ "Added new features for better user experience.", "Enhanced documentation and examples.", "Fixed issues with previous versions." ], "fileName": "xmlui-0.9.51.js", "fileSize": "3.70 MB" } ] }, "packages": [ { "name": "xmlui-search", "version": "0.1.3", "changelogs": [ "Tweaked Search dropdown panel styles. Corrected Link component text and decoration hover and active colors" ], "fileName": "xmlui-search-0.1.3.js", "fileSize": "3.70 MB" } ] } ``` -------------------------------------------------------------------------------- /xmlui/src/components/AppHeader/AppHeader.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Logo customization**: Use `logoTemplate` to create rich logo designs beyond simple images - **Profile menu**: Add user authentication displays, settings menus, or action buttons via `profileMenuTemplate` - **Layout integration**: Automatically positioned and styled based on your App's `layout` property %-DESC-END %-PROP-START logoTemplate This property defines the template to use for the logo. With this property, you can construct your custom logo instead of using a single image. ```xmlui-pg copy display {3-8} name="Example: logoTemplate" height="170px" <App> <AppHeader> <property name="logoTemplate"> <H3> <Icon name="drive" /> DriveDiag </H3> </property> </AppHeader> <NavPanel> <NavLink label="Home" to="/" icon="home"/> <NavLink label="Page 1" to="/page1"/> </NavPanel> <Pages fallbackPath="/"> <Page url="/"> <Text value="Home" /> </Page> <Page url="/page1"> <Text value="Page 1" /> </Page> </Pages> </App> ``` %-PROP-END %-PROP-START profileMenuTemplate This property makes the profile menu slot of the `AppHeader` component customizable. It accepts component definitions. ```xmlui-pg copy display {3-9} name="Example: profileMenuTemplate" height="150px" <App> <AppHeader> <property name="profileMenuTemplate"> <DropdownMenu> <property name="triggerTemplate"> <Avatar name="Joe" size="xs" borderRadius="50%"/> </property> </DropdownMenu> </property> </AppHeader> </App> ``` %-PROP-END ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/ConfirmationDialog.tsx: -------------------------------------------------------------------------------- ```typescript import * as AlertDialog from "@radix-ui/react-alert-dialog"; import styles from "./ConfirmationDialog.module.scss"; import classnames from "classnames"; import { useTheme } from "xmlui"; const ConfirmationDialog = ({ open, onOpenChange, title, description, onConfirm, confirmText = "Confirm", cancelText = "Cancel", }: { open: boolean; onOpenChange: (open: boolean) => void; title: string; description: string; onConfirm: () => void; confirmText?: string; cancelText?: string; }) => { const { root } = useTheme(); return ( <AlertDialog.Root open={open} onOpenChange={onOpenChange}> <AlertDialog.Portal container={root}> <AlertDialog.Overlay className={styles.Overlay} /> <AlertDialog.Content className={styles.Content}> <AlertDialog.Title className={styles.Title}>{title}</AlertDialog.Title> <AlertDialog.Description className={styles.Description}> {description} </AlertDialog.Description> <div className={styles.Actions}> <AlertDialog.Cancel asChild> <button className={classnames(styles.Button, styles.mauve)}>{cancelText}</button> </AlertDialog.Cancel> <AlertDialog.Action asChild> <button className={classnames(styles.Button, styles.red)} onClick={onConfirm}> {confirmText} </button> </AlertDialog.Action> </div> </AlertDialog.Content> </AlertDialog.Portal> </AlertDialog.Root> ); }; export default ConfirmationDialog; ``` -------------------------------------------------------------------------------- /xmlui/bin/start.ts: -------------------------------------------------------------------------------- ```typescript import type { InlineConfig } from "vite"; import { createServer } from "vite"; import { getViteConfig } from "./viteConfig"; type XmlUiStartOptions = { port?: number; withMock?: boolean; proxy?: string; }; export const start = async ({ port, withMock = true, proxy, }: XmlUiStartOptions) => { console.log("Starting with options:", { withMock, withProxy: proxy, }); let proxyDef; if (proxy) { const splitProxy = proxy.split("->"); if (splitProxy.length !== 2) { console.error("Invalid proxy definition. Example: /api->http://localhost:3000"); } else { proxyDef = { [splitProxy[0]]: { target: splitProxy[1], changeOrigin: true, }, }; } } try { let viteConfig = await getViteConfig({}); const server = await createServer({ ...viteConfig, server: { port, proxy: proxyDef, }, define: { ...viteConfig.define, "process.env.VITE_BUILD_MODE": JSON.stringify("ALL"), "process.env.VITE_DEV_MODE": true, "process.env.VITE_STANDALONE": process.env.VITE_STANDALONE, "process.env.VITE_MOCK_ENABLED": withMock, "process.env.VITE_INCLUDE_ALL_COMPONENTS": JSON.stringify("true"), "process.env.VITE_USER_COMPONENTS_Inspect": JSON.stringify("true"), }, } as InlineConfig); // server. if (!server.httpServer) { throw new Error("HTTP server not available"); } await server.listen(); server.printUrls(); } catch (e) { process.exit(1); } }; ``` -------------------------------------------------------------------------------- /tools/create-app/helpers/is-folder-empty.ts: -------------------------------------------------------------------------------- ```typescript import { green, blue } from 'picocolors' import fs from 'fs' import path from 'path' export function isFolderEmpty(root: string, name: string): boolean { const validFiles = [ '.DS_Store', '.git', '.gitattributes', '.gitignore', '.gitlab-ci.yml', '.hg', '.hgcheck', '.hgignore', '.idea', '.npmignore', '.travis.yml', 'LICENSE', 'Thumbs.db', 'docs', 'mkdocs.yml', 'npm-debug.log', 'yarn-debug.log', 'yarn-error.log', 'yarnrc.yml', '.yarn', ] const conflicts = fs .readdirSync(root) .filter((file) => !validFiles.includes(file)) // Support IntelliJ IDEA-based editors .filter((file) => !/\.iml$/.test(file)) if (conflicts.length > 0) { console.log( `The directory ${green(name)} contains files that could conflict:` ) console.log() for (const file of conflicts) { try { const stats = fs.lstatSync(path.join(root, file)) if (stats.isDirectory()) { console.log(` ${blue(file)}/`) } else { console.log(` ${file}`) } } catch { console.log(` ${file}`) } } console.log() console.log( 'Either try using a new directory name, or remove the files listed above.' ) console.log() return false } return true } ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/utils/mergeProps.ts: -------------------------------------------------------------------------------- ```typescript type AnyProps = Record<string, any>; /** * Merges child component props with rest props. * @param childProps Child component props * @param restProps Rest props * @returns * * The origin of this method is: * https://github.com/radix-ui/primitives/blob/c31c97274ff357aea99afe6c01c1c8c58b6356e0/packages/react/slot/src/Slot.tsx#L91 */ export function mergeProps(childProps: AnyProps, restProps: AnyProps) { // --- All child props should override const overrideProps = { ...restProps }; for (const propName in restProps) { const childPropValue = childProps[propName]; const restPropValue = restProps[propName]; const isHandler = /^on[A-Z]/.test(propName); if (isHandler) { // --- If the handler exists on both, we compose them... if (childPropValue && restPropValue) { overrideProps[propName] = (...args: unknown[]) => { restPropValue(...args); childPropValue(...args); }; } // --- ...but if it exists only on the original, we use only this one else if (childPropValue) { overrideProps[propName] = childPropValue; } } else if (propName === "style") { // --- We merge `style` overrideProps[propName] = restPropValue ? { ...restPropValue, ...childPropValue } : childPropValue; } else if (propName === "className") { // --- We merge `className` overrideProps[propName] = restPropValue ? [restPropValue, childPropValue].filter(Boolean).join(" ") : childPropValue; } } return { ...childProps, ...overrideProps }; } ``` -------------------------------------------------------------------------------- /xmlui/src/testing/infrastructure/public/resources/txt.svg: -------------------------------------------------------------------------------- ``` <svg width="22" height="28" viewBox="0 0 22 28" fill="none" xmlns="http://www.w3.org/2000/svg" data-testid="txt-svg"> <g clip-path="url(#clip0_1811_14941)"> <path d="M21.7881 7.38281L16.374 5.74219L14.8271 0H3.22559C2.37125 0 1.67871 0.734508 1.67871 1.64062V26.3594C1.67871 27.2655 2.37125 28 3.22559 28H20.2412C21.0956 28 21.7881 27.2655 21.7881 26.3594V7.38281Z" fill="#EDEDED"/> <path d="M21.7881 7.38281V26.3594C21.7881 27.2655 21.0955 28 20.2412 28H11.8572V0H14.8271L16.374 5.74219L21.7881 7.38281Z" fill="#EDEDED"/> <path d="M21.7881 7.38281H16.374C15.5232 7.38281 14.8271 6.64453 14.8271 5.74219V0C15.0282 0 15.2293 0.0820312 15.3685 0.246148L21.556 6.80865C21.7107 6.95625 21.7881 7.16953 21.7881 7.38281Z" fill="#BDBDBD"/> <path d="M20.5333 22.4259C20.5333 22.925 20.1483 23.3333 19.6778 23.3333H0.855556C0.385 23.3333 0 22.925 0 22.4259V13.3519C0 12.8528 0.385 12.4444 0.855556 12.4444H19.6778C20.1483 12.4444 20.5333 12.8528 20.5333 13.3519V22.4259Z" fill="#161C28"/> <path d="M4.62629 16.508V20H5.56828V16.508H6.85229V15.716H3.34229V16.508H4.62629Z" fill="white"/> <path d="M8.99625 17.756L7.50225 20H8.55825L9.51225 18.518L10.4483 20H11.5703L10.0763 17.762L11.4503 15.716H10.4183L9.54825 17.084L8.70825 15.716H7.61625L8.99625 17.756Z" fill="white"/> <path d="M13.4962 16.508V20H14.4382V16.508H15.7222V15.716H12.2122V16.508H13.4962Z" fill="white"/> </g> <defs> <clipPath id="clip0_1811_14941"> <rect width="22" height="28" fill="white"/> </clipPath> </defs> </svg> ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/style-parser/generateHvarChain.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect, it } from "vitest"; import { matchThemeVar } from "../../../src/components-core/theming/hvar"; describe("Generate HVar chain", () => { it("Input:backgroundColor-TextArea-default--hover", () => { const ret = matchThemeVar("Input:backgroundColor-TextArea-default--hover", [ { "backgroundColor-Input": "$yeah", "backgroundColor-TextArea-default--hover": "$something else" }, { "backgroundColor-Input--hover": "$something", } ]); const expected = { forValue: "backgroundColor-TextArea-default--hover", matchedValue: "backgroundColor-Input--hover", from: [ "backgroundColor-TextArea-default--hover", "backgroundColor-Input-default--hover", "backgroundColor-TextArea--hover", "backgroundColor-Input--hover", "backgroundColor-TextArea-default", "backgroundColor-Input-default", "backgroundColor-TextArea", "backgroundColor-Input", ], }; expect(ret).eqls(expected); }); it("backgroundColor-Button-primary-solid", () => { const ret = matchThemeVar("backgroundColor-Button-primary-solid", [{ "backgroundColor-Button-primary-solid": "$something", }]); const expected = { forValue: "backgroundColor-Button-primary-solid", matchedValue: "backgroundColor-Button-primary-solid", from: ["backgroundColor-Button-primary-solid", "backgroundColor-Button-primary", "backgroundColor-Button-solid", "backgroundColor-Button"], }; expect(ret).eqls(expected); }); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Badge/BadgeNative.tsx: -------------------------------------------------------------------------------- ```typescript import { type CSSProperties, type ForwardedRef, forwardRef } from "react"; import classnames from "classnames"; import styles from "./Badge.module.scss"; export const badgeVariantValues = ["badge", "pill"] as const; export type BadgeVariant = (typeof badgeVariantValues)[number]; export type BadgeColors = { label: string; background: string; }; // --- Type guard for BadgeColors --- export function isBadgeColors(color: any): color is BadgeColors { return ( typeof color === "object" && color !== null && "label" in color && "background" in color && typeof color.label === "string" && typeof color.background === "string" ); } type Props = { children?: React.ReactNode; variant?: BadgeVariant; color?: string | BadgeColors; style?: CSSProperties; className?: string; }; export const defaultProps: Pick<Props, "variant"> = { variant: "badge", }; export const Badge = forwardRef(function Badge( { children, color, variant = defaultProps.variant, style, className, ...rest }: Props, forwardedRef: ForwardedRef<HTMLDivElement>, ) { return ( <div {...rest} ref={forwardedRef} className={classnames( { [styles.badge]: variant === "badge", [styles.pill]: variant === "pill", }, className, )} style={{ ...(color ? typeof color === "string" ? { backgroundColor: color } : { backgroundColor: color.background, color: color.label } : {}), ...style, }} > {children} </div> ); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/theming/themes/xmlui.ts: -------------------------------------------------------------------------------- ```typescript import type { ThemeDefinition } from "../../../abstractions/ThemingDefs"; import { cyanThemeColors, grayThemeColors, greenThemeColors, orangeThemeColors, purpleThemeColors, redThemeColors, } from "./theme-colors"; export const XmlUiThemeDefinition: ThemeDefinition = { id: "xmlui", resources: { // "font.inter": "https://rsms.me/inter/inter.css", }, themeVars: { "font-size": "15px", "boxShadow-Input": "$boxShadow-sm", }, tones: { light: { themeVars: { "backgroundColor-ModalDialog": "white", "backgroundColor-checked-RadioGroupOption": "$color-primary-400", } }, dark: { themeVars: { "color-error": "$color-danger-400", } } } }; export const XmlUiGreenThemeDefinition: ThemeDefinition = { id: "xmlui-green", extends: "xmlui", themeVars: { ...greenThemeColors }, }; export const XmlUiGrayThemeDefinition: ThemeDefinition = { id: "xmlui-gray", extends: "xmlui", themeVars: { ...grayThemeColors }, }; export const XmlUiOrangeThemeDefinition: ThemeDefinition = { id: "xmlui-orange", extends: "xmlui", themeVars: { ...orangeThemeColors }, }; export const XmlUiPurpleThemeDefinition: ThemeDefinition = { id: "xmlui-purple", extends: "xmlui", themeVars: { ...purpleThemeColors }, }; export const XmlUiCyanThemeDefinition: ThemeDefinition = { id: "xmlui-cyan", extends: "xmlui", themeVars: { ...cyanThemeColors }, }; export const XmlUiRedThemeDefinition: ThemeDefinition = { id: "xmlui-red", extends: "xmlui", themeVars: { ...redThemeColors }, }; ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/descriptorHelper.ts: -------------------------------------------------------------------------------- ```typescript // We need these keys for extracting layout parameters export const layoutOptionKeys = [ "horizontalAlignment", "verticalAlignment", "orientation", "width", "minWidth", "maxWidth", "height", "minHeight", "maxHeight", "gap", "border", "borderTop", "borderRight", "borderBottom", "borderLeft", "borderColor", "borderStyle", "borderWidth", "borderHorizontal", "borderVertical", "borderRadius", "radiusTopLeft", "radiusTopRight", "radiusBottomLeft", "radiusBottomRight", "padding", "paddingHorizontal", "paddingVertical", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft", "backgroundColor", "background", "boxShadow", "direction", "overflowX", "overflowY", "zIndex", "color", "fontFamily", "fontSize", "fontWeight", "fontStyle", "textDecoration", "wrapContent", "canShrink", "margin", "marginHorizontal", "marginVertical", "marginTop", "marginRight", "marginBottom", "marginLeft", "userSelect", "letterSpacing", "textTransform", "lineHeight", "opacity", "cursor", "textWrap", "textAlign", "textAlignLast", "top", "right", "bottom", "left", "zoom", "whiteSpace", "textDecoration", "textDecorationLine", "textDecorationColor", "textDecorationStyle", "textDecorationThickness", "textUnderlineOffset", "outline", "outlineWidth", "outlineColor", "outlineStyle", "outlineOffset", "fontVariant", "lineBreak", "textIndent", "textShadow", "wordBreak", "wordSpacing", "wordWrap", "writingMode", "transition", "transform", ]; ``` -------------------------------------------------------------------------------- /xmlui/src/components/APICall/APICallNative.tsx: -------------------------------------------------------------------------------- ```typescript import { useEffect } from "react"; import type { RegisterComponentApiFn } from "../../abstractions/RendererDefs"; import type { ActionExecutionContext } from "../../abstractions/ActionDefs"; import { useEvent } from "../../components-core/utils/misc"; import { callApi } from "../../components-core/action/APICall"; import type { ApiActionComponent } from "../../components/APICall/APICall"; interface Props { registerComponentApi: RegisterComponentApiFn; node: ApiActionComponent; uid: symbol; } export const defaultProps = { method: "get", }; export function APICallNative({ registerComponentApi, node, uid }: Props) { // TODO pause until the apiInterceptorContext is initialized (to make sure the API calls are intercepted) const execute = useEvent( async (executionContext: ActionExecutionContext, ...eventArgs: any[]) => { const options = eventArgs[1]; return await callApi( executionContext, { ...node.props, body: node.props.body || (options?.passAsDefaultBody ? eventArgs[0] : undefined), uid: uid, params: { $param: eventArgs[0], $params: eventArgs }, onError: node.events?.error, onProgress: node.events?.progress, onBeforeRequest: node.events?.beforeRequest, onSuccess: node.events?.success, }, { resolveBindingExpressions: true, }, ); }, ); useEffect(() => { registerComponentApi({ execute: execute, _SUPPORT_IMPLICIT_CONTEXT: true, }); }, [execute, registerComponentApi]); return null; } ```