This is page 26 of 182. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ ├── config.json │ ├── cool-queens-look.md │ ├── hot-berries-argue.md │ ├── twelve-guests-care.md │ └── wise-towns-dance.md ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-blog.yml │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── 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 │ │ ├── netlify.toml │ │ ├── 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 │ │ │ ├── Debug.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ ├── Main.xmlui.xs │ │ └── 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 │ │ │ │ ├── make-navpanel-width-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── build-system.md │ ├── build-xmlui.md │ ├── component-behaviors.md │ ├── containers.md │ ├── glossary.md │ ├── index.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── standalone-app.md │ ├── state-management.md │ ├── ud-components.md │ └── xmlui-repo.md ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── 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 │ │ │ └── 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/Icon/svg/admonition_danger.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <path d="M18 6C11.3831 6 6 11.3832 6 18C6 24.6168 11.3831 30 18 30C24.6169 30 30 24.6168 30 18C30 11.3832 24.6169 6 18 6ZM18 8.32256C20.2505 8.32256 22.324 9.09473 23.9696 10.3881L10.3881 23.9696C9.09478 22.324 8.32256 20.2505 8.32256 18C8.32256 12.6639 12.6639 8.32256 18 8.32256ZM18 27.6774C15.7495 27.6774 13.676 26.9053 12.0304 25.6119L25.6119 12.0304C26.9053 13.676 27.6774 15.7495 27.6774 18C27.6774 23.3361 23.3361 27.6774 18 27.6774Z" fill="url(#paint0_linear_11264_27095)"/> 3 | <path d="M29.9167 18C29.9167 11.4292 24.5709 6.08333 18 6.08333C11.4291 6.08333 6.08333 11.4292 6.08333 18C6.08333 24.5708 11.4291 29.9167 18 29.9167C24.5709 29.9167 29.9167 24.5708 29.9167 18ZM26.527 18C26.527 16.4591 26.1141 15.0148 25.3961 13.7661L13.7661 25.3961C15.0148 26.1141 16.4591 26.527 18 26.527V27.6104L17.5832 27.6008C15.5096 27.5119 13.6043 26.763 12.0723 25.559L25.559 12.0723C26.8433 13.7064 27.6104 15.7652 27.6104 18L27.5977 18.4941C27.3396 23.5644 23.1334 27.6104 18 27.6104V26.527C22.7007 26.527 26.527 22.7007 26.527 18ZM18.4168 8.39917C20.4904 8.4881 22.3957 9.23699 23.9277 10.441L10.441 23.9277C9.23704 22.3957 8.4881 20.4904 8.39917 18.4168L8.38965 18C8.38965 12.7009 12.7009 8.38965 18 8.38965L18.4168 8.39917ZM9.47298 18C9.47298 19.5404 9.88531 20.9844 10.6029 22.2328L22.2328 10.6029C20.9844 9.8853 19.5404 9.47298 18 9.47298C13.2993 9.47298 9.47298 13.2993 9.47298 18ZM31 18C31 25.1691 25.1692 31 18 31C10.8308 31 5 25.1691 5 18C5 10.8309 10.8308 5 18 5C25.1692 5 31 10.8309 31 18Z" fill="url(#paint1_linear_11264_27095)"/> 4 | <path d="M31 18C31 10.9428 25.35 5.18214 18.335 5.00391L18 5C10.8308 5 5 10.8309 5 18L5.00391 18.335C5.17931 25.2386 10.7613 30.8207 17.665 30.9961L18 31C25.0573 31 30.8179 25.3499 30.9961 18.335L31 18ZM29.6162 18C29.6162 11.5949 24.4052 6.38379 18 6.38379C11.5948 6.38379 6.38379 11.5949 6.38379 18C6.38379 24.4051 11.5948 29.6162 18 29.6162V29.917L17.6934 29.9131C11.3655 29.7525 6.24754 24.6344 6.08691 18.3066L6.08301 18C6.08301 11.4292 11.4291 6.08301 18 6.08301L18.3066 6.08691C24.7364 6.25013 29.917 11.5317 29.917 18L29.9131 18.3066C29.7499 24.7363 24.4684 29.917 18 29.917V29.6162C24.4052 29.6162 29.6162 24.4051 29.6162 18ZM25.7949 11.8867C27.1191 13.5716 27.9102 15.6954 27.9102 18V18.0078L27.8975 18.502V18.5098C27.6311 23.7386 23.2939 27.9102 18 27.9102H17.9932L17.5762 27.9004H17.5703C15.4317 27.8087 13.4663 27.0363 11.8867 25.7949L11.6211 25.5859L25.5859 11.6211L25.7949 11.8867ZM12.0723 25.5586C13.6042 26.7626 15.5095 27.5116 17.583 27.6006L18 27.6104C22.9728 27.6104 27.0751 23.8136 27.5615 18.9668L27.5977 18.4941L27.6104 18C27.6104 15.7652 26.8429 13.7063 25.5586 12.0723L12.0723 25.5586ZM26.2266 18C26.2266 16.6539 25.8993 15.3851 25.3232 14.2627L14.2627 25.3232C15.3851 25.8993 16.6539 26.2266 18 26.2266C22.5351 26.2266 26.2266 22.5351 26.2266 18ZM18.0068 8.08984L18.4238 8.09961H18.4297L18.8281 8.125C20.8122 8.28992 22.6323 9.04117 24.1133 10.2051L24.3789 10.4141L10.4141 24.3789L10.2051 24.1133C8.96372 22.5337 8.19133 20.5683 8.09961 18.4297V18.4238L8.08984 18.0068V18L8.10254 17.4912C8.36824 12.262 12.7059 8.08984 18 8.08984H18.0068ZM18 8.38965C12.7009 8.38965 8.38965 12.7009 8.38965 18L8.39941 18.417C8.48838 20.4905 9.23748 22.3958 10.4414 23.9277L23.9277 10.4414C22.3958 9.23743 20.4905 8.48838 18.417 8.39941L18 8.38965ZM18.2881 9.47754C19.7202 9.52552 21.0622 9.93004 22.2324 10.6025L10.6025 22.2324C9.93005 21.0622 9.52552 19.7202 9.47754 18.2881L9.47266 18C9.47266 13.2993 13.2993 9.47266 18 9.47266L18.2881 9.47754ZM9.77344 18C9.77344 19.3452 10.0995 20.6135 10.6748 21.7354L21.7354 10.6748C20.6135 10.0995 19.3452 9.77344 18 9.77344C13.4649 9.77344 9.77344 13.4649 9.77344 18ZM26.5156 18.4385C26.2865 22.9364 22.5537 26.5273 18 26.5273C16.4593 26.5273 15.0152 26.1143 13.7666 25.3965L25.3965 13.7666C26.1143 15.0152 26.5273 16.4593 26.5273 18L26.5156 18.4385ZM31.2998 18C31.2998 25.3348 25.3349 31.2998 18 31.2998C10.6651 31.2998 4.7002 25.3348 4.7002 18C4.7002 10.6652 10.6651 4.7002 18 4.7002C25.3349 4.7002 31.2998 10.6652 31.2998 18Z" fill="url(#paint2_linear_11264_27095)"/> 5 | <defs> 6 | <linearGradient id="paint0_linear_11264_27095" x1="18" y1="6" x2="18" y2="30" gradientUnits="userSpaceOnUse"> 7 | <stop stop-color="#ED5A3B"/> 8 | <stop offset="1" stop-color="#AF3117"/> 9 | </linearGradient> 10 | <linearGradient id="paint1_linear_11264_27095" x1="18" y1="5" x2="18" y2="31" gradientUnits="userSpaceOnUse"> 11 | <stop stop-color="#EC5A3A"/> 12 | <stop offset="1" stop-color="#AF3117"/> 13 | </linearGradient> 14 | <linearGradient id="paint2_linear_11264_27095" x1="18" y1="5" x2="18" y2="31" gradientUnits="userSpaceOnUse"> 15 | <stop stop-color="#EB5A39"/> 16 | <stop offset="1" stop-color="#B03117"/> 17 | </linearGradient> 18 | </defs> 19 | </svg> 20 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileUploadDropZone/FileUploadDropZoneNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type * as React from "react"; 2 | import type { CSSProperties, ForwardedRef, ReactNode } from "react"; 3 | import { forwardRef, useCallback, useEffect } from "react"; 4 | import * as dropzone from "react-dropzone"; 5 | 6 | import styles from "./FileUploadDropZone.module.scss"; 7 | import classnames from "classnames"; 8 | 9 | import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs"; 10 | import { useEvent } from "../../components-core/utils/misc"; 11 | import { asyncNoop } from "../../components-core/constants"; 12 | import { Icon } from "../Icon/IconNative"; 13 | import { getComposedRef } from "../component-utils"; 14 | 15 | // https://github.com/react-dropzone/react-dropzone/issues/1259 16 | const { useDropzone } = dropzone; 17 | 18 | // ===================================================================================================================== 19 | // React FileUploadDropZone component implementation 20 | 21 | type Props = { 22 | children: ReactNode; 23 | onUpload: (files: File[]) => void; 24 | uid?: string; 25 | registerComponentApi: RegisterComponentApiFn; 26 | style?: CSSProperties; 27 | className?: string; 28 | allowPaste?: boolean; 29 | text?: string; 30 | disabled?: boolean; 31 | updateState?: UpdateStateFn; 32 | acceptedFileTypes?: string; 33 | maxFiles?: number; 34 | }; 35 | 36 | export const defaultProps: Pick<Props, "onUpload" | "uid" | "allowPaste" | "text" | "disabled"> = { 37 | onUpload: asyncNoop, 38 | uid: "fileUploadDialog", 39 | allowPaste: true, 40 | text: "Drop files here", 41 | disabled: false, 42 | }; 43 | 44 | export const FileUploadDropZone = forwardRef(function FileUploadDropZone( 45 | { 46 | children, 47 | onUpload = defaultProps.onUpload, 48 | uid = defaultProps.uid, 49 | registerComponentApi, 50 | style, 51 | className, 52 | allowPaste = defaultProps.allowPaste, 53 | text = defaultProps.text, 54 | disabled = defaultProps.disabled, 55 | updateState, 56 | acceptedFileTypes, 57 | maxFiles, 58 | ...rest 59 | }: Props, 60 | forwardedRef: ForwardedRef<HTMLDivElement>, 61 | ) { 62 | //accept is in the format {'image/*': [], 'video/*': []} see https://react-dropzone.js.org/#section-accepting-specific-file-types 63 | const accept = acceptedFileTypes 64 | ? acceptedFileTypes.split(",").reduce((acc, type) => ({ ...acc, [type.trim()]: [] }), {}) 65 | : undefined; 66 | const onDrop = useCallback( 67 | (acceptedFiles: File[]) => { 68 | if (!acceptedFiles.length) { 69 | return; 70 | } 71 | updateState?.({ 72 | value: acceptedFiles, 73 | }); 74 | onUpload?.(acceptedFiles); 75 | }, 76 | [onUpload, updateState], 77 | ); 78 | 79 | const { getRootProps, getInputProps, isDragActive, open, inputRef, isDragAccept } = useDropzone({ 80 | onDrop, 81 | noClick: true, 82 | noKeyboard: true, 83 | noDragEventsBubbling: true, 84 | disabled, 85 | accept, 86 | maxFiles, 87 | }); 88 | 89 | const doOpen = useEvent(() => { 90 | open(); 91 | }); 92 | 93 | const handleOnPaste = useCallback( 94 | (event: React.ClipboardEvent) => { 95 | if (!allowPaste) { 96 | return; 97 | } 98 | if (!inputRef.current) { 99 | return; 100 | } 101 | if (event.clipboardData?.files) { 102 | const items = event.clipboardData?.items || []; 103 | const files: File[] = []; 104 | for (let i = 0; i < items.length; i++) { 105 | const item = items[i]; 106 | if (item.kind === "file") { 107 | const file = item.getAsFile(); 108 | if (file !== null) { 109 | files.push(file); 110 | } 111 | } 112 | } 113 | if (files.length > 0) { 114 | //the clipboardData.files doesn't necessarily contains files... so we have to double check it 115 | event.stopPropagation(); //it's for nested file upload dropzones 116 | event.preventDefault(); // and this one is for preventing to paste in the file name, if we a have stored file on the clipboard (copied from finder/windows explorer for example) 117 | 118 | //stolen from here: https://github.com/react-dropzone/react-dropzone/issues/1210#issuecomment-1537862105 119 | (inputRef.current as unknown as HTMLInputElement).files = event.clipboardData.files; 120 | inputRef.current.dispatchEvent(new Event("change", { bubbles: true })); 121 | } 122 | } 123 | }, 124 | [allowPaste, inputRef], 125 | ); 126 | 127 | useEffect(() => { 128 | registerComponentApi({ 129 | open: doOpen, 130 | }); 131 | }, [doOpen, registerComponentApi, uid]); 132 | 133 | const { ref, ...rootProps } = getRootProps({ 134 | ...rest, 135 | style, 136 | className: classnames(styles.wrapper, className), 137 | onPaste: handleOnPaste, 138 | } as React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>); 139 | 140 | const rootRef = getComposedRef(ref, forwardedRef); 141 | return ( 142 | <div {...rootProps} data-drop-enabled={!disabled} ref={rootRef}> 143 | <input {...getInputProps()} /> 144 | {children} 145 | {isDragActive && isDragAccept && ( 146 | <div className={styles.dropPlaceholder}> 147 | <Icon name={"upload"}></Icon> 148 | {text} 149 | </div> 150 | )} 151 | </div> 152 | ); 153 | }); 154 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/scripts-runner/eval-tree-func-decl-async.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it } from "vitest"; 2 | 3 | import { evalBindingAsync } from "../../../src/components-core/script-runner/eval-tree-async"; 4 | import {createEvalContext} from "./test-helpers"; 5 | import { Parser } from "../../../src/parsers/scripting/Parser"; 6 | 7 | describe("Evaluate function expressions (exp)", () => { 8 | it("Funcion decl #1", async () => { 9 | // --- Arrange 10 | const source = "(function(x) { return 2 * x })(4)"; 11 | const context = createEvalContext({}); 12 | 13 | // --- Act 14 | const value = await evalAsync(source, context); 15 | 16 | // --- Arrange 17 | expect(value).equal(8); 18 | }); 19 | 20 | it("Function decl #2", async () => { 21 | // --- Arrange 22 | const source = "(function (x, y) { return x + y })(1, 2)"; 23 | const context = createEvalContext({}); 24 | 25 | // --- Act 26 | const value = await evalAsync(source, context); 27 | 28 | // --- Arrange 29 | expect(value).equal(3); 30 | }); 31 | 32 | it("Function decl #3", async () => { 33 | // --- Arrange 34 | const source = "(function (x, y) { return x + y })(1, 2)"; 35 | const context = createEvalContext({}); 36 | 37 | // --- Act 38 | const value = await evalAsync(source, context); 39 | 40 | // --- Arrange 41 | expect(value).equal(3); 42 | }); 43 | 44 | it("Function decl #4", async () => { 45 | // --- Arrange 46 | const source = "(function (x) { return (++x.h); })(count)"; 47 | const context = createEvalContext({ 48 | localContext: { 49 | count: { h: 3 } 50 | } 51 | }); 52 | 53 | // --- Act 54 | const value = await evalAsync(source, context); 55 | 56 | // --- Arrange 57 | expect(value).equal(4); 58 | }); 59 | 60 | it("Function decl #5", async () => { 61 | // --- Arrange 62 | const source = "(function (x) { return x += 2 })(count)"; 63 | const context = createEvalContext({ 64 | localContext: { 65 | count: 3 66 | } 67 | }); 68 | 69 | // --- Act 70 | const value = await evalAsync(source, context); 71 | 72 | // --- Arrange 73 | expect(value).equal(5); 74 | }); 75 | 76 | it("Function decl #6", async () => { 77 | // --- Arrange 78 | const source = "(function (x) { return x += 2 })(count + 4)"; 79 | const context = createEvalContext({ 80 | localContext: { 81 | count: 3 82 | } 83 | }); 84 | 85 | // --- Act 86 | const value = await evalAsync(source, context); 87 | 88 | // --- Arrange 89 | expect(value).equal(9); 90 | }); 91 | 92 | it("Function decl #7", async () => { 93 | // --- Arrange 94 | const source = "[1,2,3,4,5].filter(function (x) { return x % 2 === 0})[1]"; 95 | const context = createEvalContext({ 96 | localContext: { 97 | count: 3 98 | } 99 | }); 100 | 101 | // --- Act 102 | const value = await evalAsync(source, context); 103 | 104 | // --- Arrange 105 | expect(value).equal(4); 106 | }); 107 | 108 | it("Function decl #8", async () => { 109 | // --- Arrange 110 | const source = "containsArray.array.filter(function (item) {return item % 2 === 0})[1]"; 111 | const context = createEvalContext({ 112 | localContext: { 113 | containsArray: { 114 | array: [5, 4, 3, 2, 1] 115 | } 116 | } 117 | }); 118 | 119 | // --- Act 120 | const value = await evalAsync(source, context); 121 | 122 | // --- Arrange 123 | expect(value).equal(2); 124 | }); 125 | 126 | it("Function decl #9", async () => { 127 | // --- Arrange 128 | const source = "array.reduce(function (acc, item) { return acc + item}, 0)"; 129 | const context = createEvalContext({ 130 | localContext: { 131 | array: [5, 4, 3, 2, 1] 132 | } 133 | }); 134 | 135 | // --- Act 136 | const value = await evalAsync(source, context); 137 | 138 | // --- Arrange 139 | expect(value).equal(15); 140 | }); 141 | 142 | it("Function decl with rest #1", async () => { 143 | // --- Arrange 144 | const source = "(function (...a) { return a[0] + a[1] })(1, 2)"; 145 | const context = createEvalContext({}); 146 | 147 | // --- Act 148 | const value = await evalAsync(source, context); 149 | 150 | // --- Arrange 151 | expect(value).equal(3); 152 | }); 153 | 154 | it("Function decl with rest #2", async () => { 155 | // --- Arrange 156 | const source = "(function (x, ...a) { return x + a[0] + a[1] })(1, 2, 3)"; 157 | const context = createEvalContext({}); 158 | 159 | // --- Act 160 | const value = await evalAsync(source, context); 161 | 162 | // --- Arrange 163 | expect(value).equal(6); 164 | }); 165 | 166 | it("Function decl reccursive #1", async () => { 167 | // --- Arrange 168 | const source = "(function factorial(n) { return n <= 0 ? 1 : n * factorial(n - 1)})(3)"; 169 | const context = createEvalContext({}); 170 | 171 | // --- Act 172 | const value = await evalAsync(source, context); 173 | 174 | // --- Arrange 175 | expect(value).equal(6); 176 | }); 177 | }); 178 | 179 | 180 | async function evalAsync(source: string, evalContext: any): Promise<any> { 181 | const wParser = new Parser(source); 182 | const tree = wParser.parseExpr(); 183 | if (tree === null) { 184 | // --- This should happen only when an expression is empty 185 | return undefined; 186 | } 187 | 188 | // --- Check for expression termination 189 | if (!wParser.isEof) { 190 | throw new Error("Expression is not terminated properly"); 191 | } 192 | 193 | // --- Ok, valid source, evaluate 194 | return await evalBindingAsync(tree, evalContext, undefined); 195 | } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Button/Button.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Visual hierarchy**: Choose from `solid`, `outlined`, or `ghost` variants to indicate importance 5 | - **Theme colors**: Use `primary`, `secondary`, or `attention` colors for different action types 6 | - **Icon support**: Add icons before or after text, or create icon-only buttons 7 | - **Form integration**: Automatically handles form submission when used in forms 8 | 9 | %-DESC-END 10 | 11 | %-PROP-START autoFocus 12 | 13 | %-PROP-END 14 | 15 | %-PROP-START contentPosition 16 | 17 | ```xmlui-pg copy display name="Example: content position" 18 | <App> 19 | <Button width="200px" icon="drive" label="Button" contentPosition="center" /> 20 | <Button width="200px" icon="drive" label="Button" contentPosition="start" /> 21 | <Button width="200px" icon="drive" label="Button" contentPosition="end" /> 22 | <Button width="200px" contentPosition="end"> 23 | This is a nested text 24 | </Button> 25 | </App> 26 | ``` 27 | 28 | %-PROP-END 29 | 30 | %-PROP-START icon 31 | 32 | ```xmlui-pg copy display name="Example: icon" 33 | <App> 34 | <HStack> 35 | <Button icon="drive" label="Let there be drive" /> 36 | <Button icon="drive" /> 37 | </HStack> 38 | </App> 39 | ``` 40 | 41 | %-PROP-END 42 | 43 | %-PROP-START iconPosition 44 | 45 | ```xmlui-pg copy display name="Example: icon position" 46 | <App> 47 | <HStack> 48 | <Button icon="drive" label="Left" /> 49 | <Button icon="drive" label="Right" iconPosition="right" /> 50 | </HStack> 51 | <HStack> 52 | <Button icon="drive" label="Start" iconPosition="start" /> 53 | <Button icon="drive" label="End" iconPosition="end" /> 54 | </HStack> 55 | <HStack> 56 | <Button 57 | icon="drive" 58 | label="Start (right-to-left)" 59 | iconPosition="start" 60 | direction="rtl" /> 61 | <Button 62 | icon="drive" 63 | label="End (right-to-left)" 64 | iconPosition="end" 65 | direction="rtl" /> 66 | </HStack> 67 | </App> 68 | ``` 69 | 70 | %-PROP-END 71 | 72 | %-PROP-START label 73 | 74 | ```xmlui-pg copy display name="Example: label" 75 | <App> 76 | <Button label="I am the button label" /> 77 | <Button /> 78 | <Button label="I am the button label"> 79 | <Icon name="trash" /> 80 | I am a text nested into Button 81 | </Button> 82 | </App> 83 | ``` 84 | 85 | %-PROP-END 86 | 87 | %-PROP-START variant 88 | 89 | ```xmlui-pg copy display name="Example: variant" 90 | <App> 91 | <HStack> 92 | <Button label="default (solid)" /> 93 | <Button label="solid" variant="solid" /> 94 | <Button label="outlined" variant="outlined" /> 95 | <Button label="ghost" variant="ghost" /> 96 | </HStack> 97 | </App> 98 | ``` 99 | 100 | %-PROP-END 101 | 102 | %-PROP-START themeColor 103 | 104 | ```xmlui-pg copy display name="Example: theme colors" 105 | <App> 106 | <HStack> 107 | <Button label="Button" themeColor="primary" /> 108 | <Button label="Button" themeColor="secondary" /> 109 | <Button label="Button" themeColor="attention" /> 110 | </HStack> 111 | </App> 112 | ``` 113 | 114 | %-PROP-END 115 | 116 | %-PROP-START enabled 117 | 118 | ```xmlui-pg copy display name="Example: enabled" 119 | <App> 120 | <HStack> 121 | <Button label="I am enabled (by default)" /> 122 | <Button label="I am enabled explicitly" enabled="true" /> 123 | <Button label="I am not enabled" enabled="false" /> 124 | </HStack> 125 | </App> 126 | ``` 127 | 128 | %-PROP-END 129 | 130 | %-PROP-START size 131 | 132 | ```xmlui-pg copy display name="Example: size" 133 | <App> 134 | <HStack> 135 | <Button icon="drive" label="default" /> 136 | <Button icon="drive" label="extra-small" size="xs" /> 137 | <Button icon="drive" label="small" size="sm" /> 138 | <Button icon="drive" label="medium" size="md" /> 139 | <Button icon="drive" label="large" size="lg" /> 140 | </HStack> 141 | <HStack> 142 | <Button label="default" /> 143 | <Button label="extra-small" size="xs" /> 144 | <Button label="small" size="sm" /> 145 | <Button label="medium" size="md" /> 146 | <Button label="large" size="lg" /> 147 | </HStack> 148 | </App> 149 | ``` 150 | 151 | %-PROP-END 152 | 153 | %-EVENT-START click 154 | 155 | ```xmlui-pg copy display name="Example: click" 156 | <App> 157 | <Button label="Click me!" onClick="toast('Button clicked')" /> 158 | </App> 159 | ``` 160 | 161 | %-EVENT-END 162 | 163 | %-EVENT-START gotFocus 164 | 165 | ```xmlui-pg copy display name="Example: gotFocus" 166 | <App var.text="No event" > 167 | <HStack verticalAlignment="center" > 168 | <Button label="First, click me!" 169 | onGotFocus="text = 'Focus received'" 170 | onLostFocus="text = 'Focus lost'" /> 171 | <Text value="Then, me!"/> 172 | </HStack> 173 | <Text value="{text}" /> 174 | </App> 175 | ``` 176 | 177 | %-EVENT-END 178 | 179 | %-EVENT-START lostFocus 180 | 181 | (See the example above) 182 | 183 | %-EVENT-END 184 | 185 | %-STYLE-START 186 | 187 | ### Fixed width and height 188 | 189 | Using a set of buttons with a fixed width or height is often helpful. So `Button` supports these theme variables: 190 | - `width-Button` 191 | - `height-Button` 192 | 193 | Avoid setting the `width-Button` and `height-Button` styles in the theme definition. Instead, wrap the affected button group into a `Theme` component as in the following example: 194 | 195 | ```xmlui-pg copy name="Example: Buttons with fixed width" 196 | <App> 197 | <HStack> 198 | <Theme width-Button="120px"> 199 | <Button label="Short" /> 200 | <Button label="Longer" /> 201 | <Button label="Longest" /> 202 | <Button label="Disabled" enabled="false" /> 203 | <Button label="Outlined" variant="outlined" /> 204 | </Theme> 205 | </HStack> 206 | </App> 207 | ``` 208 | 209 | %-STYLE-END 210 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Accordion/Accordion.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./Accordion.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { 6 | createMetadata, 7 | dCollapse, 8 | dDidChange, 9 | dExpand, 10 | dExpanded, 11 | dFocus, 12 | } from "../../components/metadata-helpers"; 13 | import { triggerPositionNames } from "../../components/abstractions"; 14 | import { AccordionComponent, defaultProps } from "./AccordionNative"; 15 | 16 | const COMP = "Accordion"; 17 | 18 | // See reference implementation here: https://getbootstrap.com/docs/5.3/components/accordion/ 19 | // Make the header focusable, handle ARIA attributes, and manage the state of the accordion. 20 | 21 | export const AccordionMd = createMetadata({ 22 | status: "in progress", 23 | description: 24 | `(**NOT IMPLEMENTED YET**) The \`${COMP}\` component is a collapsible container that toggles ` + 25 | `the display of content sections. It helps organize information by expanding or collapsing it ` + 26 | `based on user interaction.`, 27 | props: { 28 | triggerPosition: { 29 | description: 30 | `This property indicates the position where the trigger icon should be displayed. The \`start\` ` + 31 | `value signs the trigger is before the header text (template), and \`end\` indicates that it ` + 32 | `follows the header.`, 33 | defaultValue: defaultProps.triggerPosition, 34 | valueType: "string", 35 | availableValues: triggerPositionNames, 36 | }, 37 | collapsedIcon: { 38 | description: 39 | "This property is the name of the icon that is displayed when the accordion is " + 40 | "collapsed. This property is the name of the icon that is displayed when the accordion is expanded. If not provided, a chevron-down icon is used.", 41 | valueType: "string", 42 | defaultValue: defaultProps.collapsedIcon, 43 | }, 44 | expandedIcon: { 45 | description: 46 | "This property is the name of the icon that is displayed when the accordion is " + 47 | "expanded. If not provided, a chevron-up icon is used.", 48 | valueType: "string", 49 | }, 50 | hideIcon: { 51 | description: `This property indicates that the trigger icon is not displayed (\`true\`).`, 52 | defaultValue: defaultProps.hideIcon, 53 | valueType: "boolean", 54 | }, 55 | rotateExpanded: { 56 | description: `This optional property defines the rotation angle of the expanded icon (relative to the collapsed icon).`, 57 | valueType: "string", 58 | defaultValue: defaultProps.rotateExpanded, 59 | }, 60 | }, 61 | events: { 62 | displayDidChange: dDidChange(COMP), 63 | }, 64 | apis: { 65 | expanded: { 66 | description: `This method returns \`true\` if the accordion is expanded, and \`false\` if it is collapsed.`, 67 | signature: "get expanded(): boolean", 68 | }, 69 | expand: { 70 | description: `This method expands the accordion, making its content visible.`, 71 | signature: "expand()", 72 | }, 73 | collapse: { 74 | description: `This method collapses the accordion, hiding its content.`, 75 | signature: "collapse()", 76 | }, 77 | toggle: { 78 | description: `This method toggles the state of the ${COMP} between expanded and collapsed.`, 79 | signature: "toggle()", 80 | }, 81 | focus: dFocus(COMP), 82 | }, 83 | themeVars: parseScssVar(styles.themeVars), 84 | defaultThemeVars: { 85 | [`paddingHorizontal-header-${COMP}`]: "$space-3", 86 | [`paddingVertical-header-${COMP}`]: "$space-3", 87 | [`verticalAlignment-header-${COMP}`]: "center", 88 | [`fontSize-header-${COMP}`]: "$fontSize-base", 89 | [`fontWeight-header-${COMP}`]: "$fontWeight-normal", 90 | [`fontFamily-header-${COMP}`]: "$fontFamily", 91 | [`border-${COMP}`]: "0px solid $borderColor", 92 | [`width-icon-${COMP}`]: "", 93 | [`height-icon-${COMP}`]: "", 94 | [`backgroundColor-header-${COMP}`]: "$color-primary-500", 95 | [`backgroundColor-header-${COMP}-hover`]: "$color-primary-400", 96 | [`color-header-${COMP}`]: "$color-surface-50", 97 | [`color-content-${COMP}`]: "$textColor-primary", 98 | [`backgroundColor-content-${COMP}`]: "transparent", 99 | [`color-icon-${COMP}`]: "$color-surface-50", 100 | }, 101 | }); 102 | 103 | export const accordionComponentRenderer = createComponentRenderer( 104 | COMP, 105 | AccordionMd, 106 | ({ node, renderChild, extractValue, lookupEventHandler, registerComponentApi, className }) => { 107 | return ( 108 | <AccordionComponent 109 | className={className} 110 | triggerPosition={extractValue(node.props?.triggerPosition)} 111 | collapsedIcon={extractValue(node.props.collapsedIcon)} 112 | expandedIcon={extractValue(node.props.expandedIcon)} 113 | hideIcon={extractValue.asOptionalBoolean(node.props.hideIcon)} 114 | rotateExpanded={extractValue(node.props.rotateExpanded)} 115 | onDisplayDidChange={lookupEventHandler("displayDidChange")} 116 | registerComponentApi={registerComponentApi} 117 | > 118 | {renderChild(node.children)} 119 | </AccordionComponent> 120 | ); 121 | }, 122 | ); 123 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Checkbox/Checkbox.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "../Toggle/Toggle.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { 6 | createMetadata, 7 | dAutoFocus, 8 | dClick, 9 | dComponent, 10 | dDidChange, 11 | dEnabled, 12 | dGotFocus, 13 | dIndeterminate, 14 | dInitialValue, 15 | dInternal, 16 | dLostFocus, 17 | dReadonly, 18 | dRequired, 19 | dValidationStatus, 20 | } from "../../components/metadata-helpers"; 21 | import { defaultProps as toggleDefaultProps, Toggle } from "../Toggle/Toggle"; 22 | import { MemoizedItem } from "../container-helpers"; 23 | 24 | export const defaultProps = { 25 | ...toggleDefaultProps, 26 | labelPosition: "end", 27 | }; 28 | 29 | const COMP = "Checkbox"; 30 | 31 | export const CheckboxMd = createMetadata({ 32 | status: "stable", 33 | description: 34 | "`Checkbox` allows users to make binary choices with a clickable box that shows " + 35 | "checked/unchecked states. It's essential for settings, preferences, multi-select " + 36 | "lists, and accepting terms or conditions.", 37 | parts: { 38 | label: { 39 | description: "The label displayed for the checkbox.", 40 | }, 41 | input: { 42 | description: "The checkbox input area.", 43 | }, 44 | }, 45 | props: { 46 | indeterminate: dIndeterminate(toggleDefaultProps.indeterminate), 47 | required: dRequired(), 48 | initialValue: dInitialValue(toggleDefaultProps.initialValue), 49 | autoFocus: dAutoFocus(), 50 | readOnly: dReadonly(), 51 | enabled: dEnabled(), 52 | validationStatus: dValidationStatus(toggleDefaultProps.validationStatus), 53 | description: dInternal( 54 | `(*** NOT IMPLEMENTED YET ***) This optional property displays an alternate description ` + 55 | `of the ${COMP} besides its label.`, 56 | ), 57 | inputTemplate: dComponent("This property is used to define a custom checkbox input template"), 58 | }, 59 | childrenAsTemplate: "inputTemplate", 60 | events: { 61 | click: dClick(COMP), 62 | gotFocus: dGotFocus(COMP), 63 | lostFocus: dLostFocus(COMP), 64 | didChange: dDidChange(COMP), 65 | }, 66 | apis: { 67 | value: { 68 | description: `This method returns the current value of the ${COMP}.`, 69 | signature: "get value(): boolean", 70 | }, 71 | setValue: { 72 | description: `This method sets the current value of the ${COMP}.`, 73 | signature: "set value(value: boolean): void", 74 | parameters: { 75 | value: "The new value to set for the checkbox.", 76 | }, 77 | }, 78 | }, 79 | themeVars: parseScssVar(styles.themeVars), 80 | limitThemeVarsToComponent: true, 81 | defaultThemeVars: { 82 | [`borderColor-checked-${COMP}-error`]: `$borderColor-${COMP}-error`, 83 | [`backgroundColor-checked-${COMP}-error`]: `$borderColor-${COMP}-error`, 84 | [`borderColor-checked-${COMP}-warning`]: `$borderColor-${COMP}-warning`, 85 | [`backgroundColor-checked-${COMP}-warning`]: `$borderColor-${COMP}-warning`, 86 | [`borderColor-checked-${COMP}-success`]: `$borderColor-${COMP}-success`, 87 | [`backgroundColor-checked-${COMP}-success`]: `$borderColor-${COMP}-success`, 88 | [`backgroundColor-indicator-${COMP}`]: "$backgroundColor-primary", 89 | [`borderColor-checked-${COMP}`]: "$color-primary-500", 90 | [`backgroundColor-checked-${COMP}`]: "$color-primary-500", 91 | [`backgroundColor-${COMP}--disabled`]: "$color-surface-200", 92 | }, 93 | }); 94 | 95 | export const checkboxComponentRenderer = createComponentRenderer( 96 | COMP, 97 | CheckboxMd, 98 | ({ 99 | node, 100 | extractValue, 101 | className, 102 | updateState, 103 | lookupEventHandler, 104 | state, 105 | registerComponentApi, 106 | renderChild, 107 | layoutContext, 108 | }) => { 109 | const inputTemplate = node.props.inputTemplate; 110 | return ( 111 | <Toggle 112 | inputRenderer={ 113 | inputTemplate 114 | ? (contextVars) => ( 115 | <MemoizedItem 116 | contextVars={contextVars} 117 | node={inputTemplate} 118 | renderChild={renderChild} 119 | layoutContext={layoutContext} 120 | /> 121 | ) 122 | : undefined 123 | } 124 | enabled={extractValue.asOptionalBoolean(node.props.enabled)} 125 | className={className} 126 | initialValue={extractValue.asOptionalBoolean( 127 | node.props.initialValue, 128 | defaultProps.initialValue, 129 | )} 130 | value={state?.value} 131 | readOnly={extractValue.asOptionalBoolean(node.props.readOnly)} 132 | validationStatus={extractValue(node.props.validationStatus)} 133 | updateState={updateState} 134 | onClick={lookupEventHandler("click")} 135 | onDidChange={lookupEventHandler("didChange")} 136 | onFocus={lookupEventHandler("gotFocus")} 137 | onBlur={lookupEventHandler("lostFocus")} 138 | required={extractValue.asOptionalBoolean(node.props.required)} 139 | indeterminate={extractValue.asOptionalBoolean(node.props.indeterminate)} 140 | registerComponentApi={registerComponentApi} 141 | autoFocus={extractValue.asOptionalBoolean(node.props.autoFocus)} 142 | /> 143 | ); 144 | }, 145 | ); 146 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/RadarChart/RadarChart.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { RadarChart, defaultProps } from "./RadarChartNative"; 2 | import { createComponentRenderer } from "../../../components-core/renderers"; 3 | import { createMetadata } from "../../metadata-helpers"; 4 | import { MemoizedItem } from "../../container-helpers"; 5 | 6 | const COMP = "RadarChart"; 7 | 8 | export const RadarChartMd = createMetadata({ 9 | status: "experimental", 10 | description: "Interactive radar chart for displaying multivariate data in a two-dimensional chart of three or more quantitative variables", 11 | docFolder: "Charts/RadarChart", 12 | 13 | props: { 14 | data: { 15 | description: 16 | "This property is used to provide the component with data to display. " + 17 | "The data needs to be an array of objects.", 18 | }, 19 | dataKeys: { 20 | description: 21 | "This property specifies the keys in the data objects that should be used for rendering the chart elements. " + 22 | "E.g. 'value' or 'amount'.", 23 | valueType: "string", 24 | }, 25 | nameKey: { 26 | description: 27 | "Specifies the key in the data objects that will be used to label the different data series.", 28 | valueType: "string", 29 | }, 30 | hideGrid: { 31 | description: 32 | "Determines whether the polar grid should be hidden. If set to `true`, the grid will not be rendered.", 33 | valueType: "boolean", 34 | defaultValue: defaultProps.hideGrid, 35 | }, 36 | hideAngleAxis: { 37 | description: 38 | "Determines whether the angle axis should be hidden. If set to `true`, the angle axis will not be rendered.", 39 | valueType: "boolean", 40 | defaultValue: defaultProps.hideAngleAxis, 41 | }, 42 | hideRadiusAxis: { 43 | description: 44 | "Determines whether the radius axis should be hidden. If set to `true`, the radius axis will not be rendered.", 45 | valueType: "boolean", 46 | defaultValue: defaultProps.hideRadiusAxis, 47 | }, 48 | hideTooltip: { 49 | description: 50 | "Determines whether the tooltip should be hidden. If set to `true`, the tooltip will not be rendered.", 51 | valueType: "boolean", 52 | defaultValue: defaultProps.hideTooltip, 53 | }, 54 | showLegend: { 55 | description: 56 | "Determines whether the legend should be shown. If set to `true`, the legend will be rendered.", 57 | valueType: "boolean", 58 | defaultValue: defaultProps.showLegend, 59 | }, 60 | filled: { 61 | description: 62 | "Determines whether the radar areas should be filled. If set to `true`, areas will be filled with color.", 63 | valueType: "boolean", 64 | defaultValue: defaultProps.filled, 65 | }, 66 | strokeWidth: { 67 | description: 68 | "Sets the stroke width for the radar lines. Higher values create thicker lines.", 69 | valueType: "number", 70 | defaultValue: defaultProps.strokeWidth, 71 | }, 72 | fillOpacity: { 73 | description: 74 | "Sets the fill opacity for the radar areas when filled is true. Value between 0 and 1.", 75 | valueType: "number", 76 | defaultValue: defaultProps.fillOpacity, 77 | }, 78 | tooltipTemplate: { 79 | description: "This property allows replacing the default template to display a tooltip.", 80 | }, 81 | }, 82 | 83 | events: { 84 | // Standard chart events - customize based on chart type 85 | }, 86 | 87 | apis: { 88 | // Chart-specific APIs if needed 89 | }, 90 | 91 | contextVars: { 92 | // Add context variables if needed 93 | }, 94 | }); 95 | 96 | // Component renderer 97 | export const radarChartComponentRenderer = createComponentRenderer( 98 | COMP, 99 | RadarChartMd, 100 | ({ extractValue, node, className, renderChild }: any) => { 101 | return ( 102 | <RadarChart 103 | className={className} 104 | data={extractValue(node.props?.data)} 105 | nameKey={extractValue(node.props?.nameKey)} 106 | dataKeys={extractValue(node.props?.dataKeys)} 107 | hideGrid={extractValue.asOptionalBoolean(node.props?.hideGrid)} 108 | hideAngleAxis={extractValue.asOptionalBoolean(node.props?.hideAngleAxis)} 109 | hideRadiusAxis={extractValue.asOptionalBoolean(node.props?.hideRadiusAxis)} 110 | hideTooltip={extractValue.asOptionalBoolean(node.props?.hideTooltip)} 111 | showLegend={extractValue.asOptionalBoolean(node.props?.showLegend)} 112 | filled={extractValue.asOptionalBoolean(node.props?.filled)} 113 | strokeWidth={extractValue.asOptionalNumber(node.props?.strokeWidth)} 114 | fillOpacity={extractValue.asOptionalNumber(node.props?.fillOpacity)} 115 | tooltipRenderer={ 116 | node.props.tooltipTemplate 117 | ? (tooltipData) => { 118 | return ( 119 | <MemoizedItem 120 | node={node.props.tooltipTemplate} 121 | item={tooltipData} 122 | contextVars={{ 123 | $tooltip: tooltipData, 124 | }} 125 | renderChild={renderChild} 126 | /> 127 | ); 128 | } 129 | : undefined 130 | } 131 | > 132 | {renderChild(node.children)} 133 | </RadarChart> 134 | ); 135 | }, 136 | ); 137 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/TextBox/TextBox.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: ( 5 | ); 6 | 7 | @function createThemeVar($componentVariable) { 8 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 9 | @return t.getThemeVar($themeVars, $componentVariable); 10 | } 11 | 12 | $componentName: "TextBox"; 13 | $themeVars: t.composePaddingVars($themeVars, $componentName); 14 | 15 | // --- CSS properties of a particular TextBox variant 16 | @mixin variant($variantName) { 17 | border-radius: createThemeVar("Input:borderRadius-#{$componentName}-#{$variantName}"); 18 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}"); 19 | border-width: createThemeVar("Input:borderWidth-#{$componentName}-#{$variantName}"); 20 | border-style: createThemeVar("Input:borderStyle-#{$componentName}-#{$variantName}"); 21 | font-size: createThemeVar("Input:fontSize-#{$componentName}-#{$variantName}"); 22 | 23 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}"); 24 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}"); 25 | color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}"); 26 | 27 | &:hover { 28 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--hover"); 29 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}--hover" 30 | ); 31 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--hover"); 32 | color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}--hover"); 33 | } 34 | 35 | &:focus-within { 36 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--focus"); 37 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}-#{$variantName}--focus" 38 | ); 39 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--focus"); 40 | color: createThemeVar("Input:textColor-#{$componentName}-#{$variantName}--focus"); 41 | } 42 | 43 | &:has(.input:focus-visible) { 44 | outline-width: createThemeVar("Input:outlineWidth-#{$componentName}-#{$variantName}--focus"); 45 | outline-color: createThemeVar("Input:outlineColor-#{$componentName}-#{$variantName}--focus"); 46 | outline-style: createThemeVar("Input:outlineStyle-#{$componentName}-#{$variantName}--focus"); 47 | outline-offset: createThemeVar("Input:outlineOffset-#{$componentName}-#{$variantName}--focus"); 48 | } 49 | 50 | .input { 51 | &::placeholder { 52 | color: createThemeVar("Input:textColor-placeholder-#{$componentName}-#{$variantName}"); 53 | font-size: createThemeVar("Input:fontSize-placeholder-#{$componentName}-#{$variantName}"); 54 | } 55 | } 56 | 57 | .adornment { 58 | color: createThemeVar("Input:color-adornment-#{$componentName}-#{$variantName}"); 59 | } 60 | 61 | .passwordToggle { 62 | cursor: pointer; 63 | color: createThemeVar("Input:color-passwordToggle-#{$componentName}"); 64 | padding-left: createThemeVar("Input:paddingLeft-passwordToggle-#{$componentName}"); 65 | padding-right: createThemeVar("Input:paddingRight-passwordToggle-#{$componentName}"); 66 | 67 | &:hover { 68 | color: createThemeVar("Input:color-passwordToggle-#{$componentName}--hover"); 69 | } 70 | 71 | &:focus { 72 | color: createThemeVar("Input:color-passwordToggle-#{$componentName}--focus"); 73 | } 74 | } 75 | } 76 | 77 | @layer components { 78 | .inputRoot { 79 | display: flex; 80 | align-items: center; 81 | gap: createThemeVar("Input:gap-adornment-#{$componentName}"); 82 | width: 100%; 83 | border-style: solid; 84 | border-width: 1px; 85 | transition: background-color ease-in 0.1s; 86 | overflow: hidden; 87 | @include t.paddingVars($themeVars, $componentName); 88 | @include variant("default"); 89 | 90 | &.error { 91 | @include variant("error"); 92 | } 93 | 94 | &.warning { 95 | @include variant("warning"); 96 | } 97 | 98 | &.valid { 99 | @include variant("success"); 100 | } 101 | 102 | &:has(.input:is(:disabled)) { 103 | cursor: not-allowed; 104 | background-color: createThemeVar("Input:backgroundColor-#{$componentName}--disabled"); 105 | color: createThemeVar("Input:textColor-#{$componentName}--disabled"); 106 | border-color: createThemeVar("Input:borderColor-#{$componentName}--disabled"); 107 | } 108 | 109 | .input { 110 | font-size: inherit; 111 | color: inherit; 112 | border: 0; 113 | outline: none !important; 114 | background-color: transparent; 115 | width: 100%; 116 | padding: 0; 117 | cursor: inherit; 118 | 119 | &::-ms-reveal { 120 | display: none; 121 | } 122 | 123 | // Remove default search input styles if type="search" 124 | &[type="search"]::-webkit-search-cancel-button, 125 | &[type="search"]::-webkit-search-decoration { 126 | -webkit-appearance: none; 127 | appearance: none; 128 | } 129 | } 130 | } 131 | 132 | .readOnly { 133 | cursor: default; 134 | } 135 | } 136 | 137 | // --- We export the theme variables to add them to the component renderer 138 | :export { 139 | themeVars: t.json-stringify($themeVars); 140 | } ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/devtools/InspectorDialog.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React, { type CSSProperties, type ReactNode, useEffect, useRef, useState } from "react"; 2 | import { composeRefs } from "@radix-ui/react-compose-refs"; 3 | import classnames from "classnames"; 4 | import * as Dialog from "@radix-ui/react-dialog"; 5 | 6 | import styles from "./InspectorDialog.module.scss"; 7 | import { useTheme } from "xmlui"; 8 | import { motion, AnimatePresence } from "framer-motion"; 9 | 10 | // ===================================================================================================================== 11 | // React component definition 12 | 13 | const MotionContent = motion.create(Dialog.Content); 14 | 15 | type ModalProps = { 16 | style?: CSSProperties; 17 | children?: ReactNode; 18 | isOpen: boolean; 19 | setIsOpen: (isOpen: boolean) => void; 20 | clickPosition: { x: number; y: number }; 21 | }; 22 | 23 | const overlayVariants = { 24 | visible: { opacity: 1 }, 25 | hidden: { opacity: 0 }, 26 | }; 27 | 28 | const contentVariants = { 29 | initial: (custom: { x: number; y: number }) => ({ 30 | opacity: 0, 31 | scale: 0.2, 32 | x: custom.x - window.innerWidth / 2, 33 | y: custom.y - window.innerHeight / 2, 34 | }), 35 | animate: { 36 | opacity: 1, 37 | scale: 1, 38 | x: 0, 39 | y: 0, 40 | }, 41 | exit: { 42 | opacity: 0, 43 | scale: 0.2, 44 | transition: { duration: 0.2 }, 45 | }, 46 | }; 47 | 48 | function durationToSeconds(durationString?: string) { 49 | if (!durationString) { 50 | return undefined; 51 | } 52 | const trimmedString = durationString.trim(); 53 | 54 | if (trimmedString.endsWith("ms")) { 55 | const milliseconds = parseFloat(trimmedString); 56 | return milliseconds / 1000; 57 | } else if (trimmedString.endsWith("s")) { 58 | return parseFloat(trimmedString); 59 | } else { 60 | return parseFloat(trimmedString); 61 | } 62 | } 63 | 64 | export const InspectorDialog = React.forwardRef( 65 | ( 66 | { children, style, isOpen, setIsOpen, clickPosition }: ModalProps, 67 | ref: React.Ref<HTMLDivElement>, 68 | ) => { 69 | const { root, getThemeVar } = useTheme(); 70 | const modalRef = useRef<HTMLDivElement>(null); 71 | const composedRef = ref ? composeRefs(ref, modalRef) : modalRef; 72 | const [rendered, setRendered] = useState(true); 73 | 74 | useEffect(() => { 75 | if (isOpen) { 76 | modalRef.current?.focus(); 77 | } 78 | }, [isOpen]); 79 | 80 | // https://github.com/radix-ui/primitives/issues/2122#issuecomment-2140827998 81 | useEffect(() => { 82 | if (isOpen) { 83 | // Pushing the change to the end of the call stack 84 | const timer = setTimeout(() => { 85 | document.body.style.pointerEvents = ""; 86 | }, 0); 87 | 88 | return () => clearTimeout(timer); 89 | } else { 90 | document.body.style.pointerEvents = "auto"; 91 | } 92 | }, [isOpen]); 93 | 94 | if (!root) { 95 | return null; 96 | } 97 | 98 | const onExitComplete = () => { 99 | setIsOpen(false); 100 | }; 101 | 102 | return ( 103 | <Dialog.Root defaultOpen={false} open={isOpen} onOpenChange={setRendered}> 104 | <Dialog.Portal container={root}> 105 | <AnimatePresence onExitComplete={onExitComplete}> 106 | {rendered && ( 107 | <Dialog.Overlay className={styles.overlay} forceMount> 108 | <motion.div 109 | key="overlay" 110 | className={styles.overlayBg} 111 | variants={overlayVariants} 112 | initial="hidden" 113 | animate="visible" 114 | exit="hidden" 115 | transition={{ 116 | duration: 0.2, 117 | ease: [0.16, 1, 0.3, 1], 118 | }} 119 | /> 120 | <MotionContent 121 | forceMount 122 | onPointerDownOutside={(event) => { 123 | if ( 124 | event.target instanceof Element && 125 | event.target.closest("._debug-inspect-button") !== null 126 | ) { 127 | //we prevent the auto modal close on clicking the inspect button 128 | event.preventDefault(); 129 | } 130 | }} 131 | > 132 | <motion.div 133 | ref={composedRef} 134 | className={classnames(styles.content, styles.contentWrapper)} 135 | variants={contentVariants} 136 | custom={{ x: clickPosition.x, y: clickPosition.y }} 137 | initial="initial" 138 | animate="animate" 139 | exit="exit" 140 | transition={{ 141 | duration: 142 | durationToSeconds(getThemeVar("duration-startAnimation-ModalDialog")) || 143 | 0.8, 144 | ease: [0.16, 1, 0.3, 1], 145 | }} 146 | style={{ ...style, gap: undefined }} 147 | > 148 | {children} 149 | </motion.div> 150 | </MotionContent> 151 | </Dialog.Overlay> 152 | )} 153 | </AnimatePresence> 154 | </Dialog.Portal> 155 | </Dialog.Root> 156 | ); 157 | }, 158 | ); 159 | 160 | InspectorDialog.displayName = "InspectorDialog"; 161 | ``` -------------------------------------------------------------------------------- /xmlui/dev-docs/next/tiptap-design-considerations.md: -------------------------------------------------------------------------------- ```markdown 1 | # Tiptap Design Considerations for XMLUI Markdown Interop 2 | 3 | ## Context 4 | 5 | XMLUI uses Markdown as the source of truth for all documentation and rich text content. The Markdown component supports a wide range of features, including GFM (GitHub Flavored Markdown) extensions, and maps Markdown/HTML tags to XMLUI's own React components for consistent theming and behavior. 6 | 7 | ## Interop Challenges 8 | 9 | - **Tiptap (rich editor) natively produces HTML, not Markdown.** 10 | - **Markdown is less expressive than HTML.** Some HTML features cannot be round-tripped to Markdown. 11 | - **XMLUI Markdown only supports a subset of HTML tags,** mapped via the HTMLTags component. Arbitrary HTML is not guaranteed to render. 12 | 13 | ## Design Options 14 | 15 | ### 1. Plain Markdown Editor 16 | - Simple `<textarea>` for raw Markdown editing. 17 | - No conversion needed; what the user sees is what is stored. 18 | - Power users can use all Markdown features. 19 | 20 | ### 2. Rich Editor Producing Markdown 21 | - Use Tiptap for WYSIWYG editing, but restrict features to those that map cleanly to Markdown and XMLUI's Markdown component. 22 | - On save, convert Tiptap's HTML output to Markdown (using a library like turndown). 23 | - On load, convert Markdown to HTML for editing (using marked or markdown-it). 24 | - Warn users about possible formatting loss if switching between modes. 25 | 26 | ## Supported Features 27 | 28 | | Feature/Tag | Markdown | HTML | XMLUI Markdown Support | Notes | 29 | |------------------|----------|-----------------------------|-----------------------|------------------------------| 30 | | Headings | Yes | `<h1>`-`<h6>` | Yes | Standard | 31 | | Bold/Italic | Yes | `<b>`, `<i>`, `<strong>`, `<em>` | Yes | Mapped to Text variants | 32 | | Lists | Yes | `<ul>`, `<ol>`, `<li>` | Yes | Standard | 33 | | Links | Yes | `<a>` | Yes | Mapped to LinkNative | 34 | | Code/Pre | Yes | `<code>`, `<pre>` | Yes | Mapped to Text/PreTag | 35 | | Tables | GFM | `<table>` | Yes (GFM) | Supported via remark-gfm | 36 | | Images | Yes | `<img>` | Yes | Standard | 37 | | Blockquote | Yes | `<blockquote>` | Yes | Standard | 38 | | Details/Section | No (MD) | `<details>`, `<section>` | Yes (custom) | Special handling | 39 | | Custom HTML | No | Most | No/Partial | Only mapped tags allowed | 40 | 41 | ## Best Practices 42 | 43 | - **Keep Markdown as the canonical format.** 44 | - **Restrict Tiptap features to those that map to XMLUI Markdown/HTMLTags.** 45 | - **Warn users about possible formatting loss when switching between rich/plain modes.** 46 | - **Test round-tripping** (Markdown → HTML → Markdown) for fidelity. 47 | - **Document any limitations or unsupported features.** 48 | 49 | ## Open Questions 50 | 51 | - Should the conversion logic live in the editor component or in global handlers? 52 | - How should we handle features/extensions that are not supported by XMLUI Markdown? 53 | - What is the best UX for switching between plain and rich modes? 54 | 55 | ## Focused Scope for Tiptap/TableEditor Exercise 56 | 57 | ### Motivation 58 | The primary motivation for the Tiptap exercise is to demonstrate, in developer documentation, how to wrap a real, useful component in XMLUI. The chosen example is a TableEditor, which addresses a common pain point: creating and editing Markdown tables for documentation. 59 | 60 | ### Scope and Workflow 61 | - The TableEditor provides a spreadsheet-like UI for editing tables. 62 | - Users can switch to a code view to see the generated Markdown table. 63 | - The workflow is: edit your table visually, copy the Markdown, and paste it into your documentation (e.g., in VSCode). 64 | - There is no need to solve file persistence, in-situ editing, or live site integration for this exercise. 65 | - This mirrors the current workflow where developers use external tools to generate Markdown tables, but brings the experience into the XMLUI/React context. 66 | 67 | ### Rationale 68 | - This approach is practical and developer-focused, providing immediate utility without overcomplicating the implementation. 69 | - It showcases XMLUI's extensibility and ability to integrate rich, interactive components. 70 | - The TableEditor can be demonstrated as a standalone tool or as a Tiptap node/component, but the main value is in Markdown table generation. 71 | 72 | ### Next Step 73 | The next step is to rename the Editor component to TableEditor to reflect this focused scope. 74 | 75 | --- 76 | 77 | *This document is a living record of design considerations for Tiptap/Markdown interop in XMLUI. Update as the implementation evolves.* ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/AreaChart/AreaChart.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | Interactive area chart for showing data trends over time with filled areas under the curve. 4 | 5 | **Key features:** 6 | - **Time series visualization**: Perfect for showing data trends over time with filled areas under the curve 7 | - **Multiple data series**: Display several metrics on the same chart with different colored areas 8 | - **Stacked vs overlapping**: Stack areas on top of each other or display them overlapping 9 | - **Curved lines**: Use smooth curves for more visually appealing continuous data 10 | - **Custom formatting**: Use `tickFormatter` to format axis labels 11 | 12 | %-DESC-END 13 | 14 | %-PROP-START data 15 | 16 | ```xml 17 | <AreaChart 18 | nameKey="name" 19 | data="{[ 20 | { name: 'Jan', value: 100 }, 21 | { name: 'Feb', value: 150 }, 22 | { name: 'Mar', value: 120 } 23 | ]}" 24 | dataKeys="{['value']}" 25 | /> 26 | ``` 27 | 28 | %-PROP-END 29 | 30 | %-PROP-START nameKey 31 | 32 | ```xml 33 | <AreaChart 34 | nameKey="month" 35 | data="{[ 36 | { month: 'Jan', sales: 1200, profit: 400 }, 37 | { month: 'Feb', sales: 1900, profit: 600 }, 38 | { month: 'Mar', sales: 1500, profit: 500 } 39 | ]}" 40 | dataKeys="{['sales', 'profit']}" 41 | /> 42 | ``` 43 | 44 | %-PROP-END 45 | 46 | %-PROP-START dataKeys 47 | 48 | ```xml 49 | <AreaChart 50 | nameKey="category" 51 | data="{[ 52 | { category: 'A', value1: 100, value2: 200 }, 53 | { category: 'B', value1: 150, value2: 250 }, 54 | { category: 'C', value1: 120, value2: 180 } 55 | ]}" 56 | dataKeys="{['value1', 'value2']}" 57 | /> 58 | ``` 59 | 60 | %-PROP-END 61 | 62 | %-PROP-START hideX 63 | 64 | ```xml 65 | <AreaChart 66 | nameKey="name" 67 | data="{[ 68 | { name: 'A', value: 100 }, 69 | { name: 'B', value: 200 }, 70 | { name: 'C', value: 150 } 71 | ]}" 72 | dataKeys="{['value']}" 73 | hideX="true" 74 | /> 75 | ``` 76 | 77 | %-PROP-END 78 | 79 | %-PROP-START hideY 80 | 81 | ```xml 82 | <AreaChart 83 | nameKey="name" 84 | data="{[ 85 | { name: 'A', value: 100 }, 86 | { name: 'B', value: 200 }, 87 | { name: 'C', value: 150 } 88 | ]}" 89 | dataKeys="{['value']}" 90 | hideY="true" 91 | /> 92 | ``` 93 | 94 | %-PROP-END 95 | 96 | %-PROP-START hideTickX 97 | 98 | ```xml 99 | <AreaChart 100 | nameKey="name" 101 | data="{[ 102 | { name: 'A', value: 100 }, 103 | { name: 'B', value: 200 }, 104 | { name: 'C', value: 150 } 105 | ]}" 106 | dataKeys="{['value']}" 107 | hideTickX="true" 108 | /> 109 | ``` 110 | 111 | %-PROP-END 112 | 113 | %-PROP-START hideTickY 114 | 115 | ```xml 116 | <AreaChart 117 | nameKey="name" 118 | data="{[ 119 | { name: 'A', value: 100 }, 120 | { name: 'B', value: 200 }, 121 | { name: 'C', value: 150 } 122 | ]}" 123 | dataKeys="{['value']}" 124 | hideTickY="true" 125 | /> 126 | ``` 127 | 128 | %-PROP-END 129 | 130 | %-PROP-START hideTooltip 131 | 132 | ```xml 133 | <AreaChart 134 | nameKey="name" 135 | data="{[ 136 | { name: 'A', value: 100 }, 137 | { name: 'B', value: 200 }, 138 | { name: 'C', value: 150 } 139 | ]}" 140 | dataKeys="{['value']}" 141 | hideTooltip="true" 142 | /> 143 | ``` 144 | 145 | %-PROP-END 146 | 147 | %-PROP-START showLegend 148 | 149 | ```xml 150 | <AreaChart 151 | nameKey="quarter" 152 | data="{[ 153 | { quarter: 'Q1', revenue: 1000, expenses: 800, profit: 200 }, 154 | { quarter: 'Q2', revenue: 1200, expenses: 900, profit: 300 }, 155 | { quarter: 'Q3', revenue: 1100, expenses: 850, profit: 250 }, 156 | { quarter: 'Q4', revenue: 1400, expenses: 1000, profit: 400 } 157 | ]}" 158 | dataKeys="{['revenue', 'expenses', 'profit']}" 159 | showLegend="true" 160 | /> 161 | ``` 162 | 163 | %-PROP-END 164 | 165 | %-PROP-START stacked 166 | 167 | ```xml 168 | <AreaChart 169 | nameKey="category" 170 | data="{[ 171 | { category: 'A', value1: 100, value2: 200 }, 172 | { category: 'B', value1: 150, value2: 250 }, 173 | { category: 'C', value1: 120, value2: 180 } 174 | ]}" 175 | dataKeys="{['value1', 'value2']}" 176 | stacked="true" 177 | /> 178 | ``` 179 | 180 | %-PROP-END 181 | 182 | %-PROP-START curved 183 | 184 | ```xml 185 | <AreaChart 186 | nameKey="time" 187 | data="{[ 188 | { time: '00:00', temperature: 18 }, 189 | { time: '06:00', temperature: 15 }, 190 | { time: '12:00', temperature: 25 }, 191 | { time: '18:00', temperature: 22 }, 192 | { time: '24:00', temperature: 19 } 193 | ]}" 194 | dataKeys="{['temperature']}" 195 | curved="true" 196 | /> 197 | ``` 198 | 199 | %-PROP-END 200 | 201 | %-PROP-START tooltipTemplate 202 | 203 | ```xmlui-pg copy display height="320px" name="Example: tooltipTemplate" /tooltipTemplate/ 204 | <App> 205 | <AreaChart 206 | height="240px" 207 | data="{[ 208 | { 'month': 'Jan', 'sales': 1200, 'profit': 400 }, 209 | { 'month': 'Feb', 'sales': 1900, 'profit': 600 }, 210 | { 'month': 'Mar', 'sales': 1500, 'profit': 500 }, 211 | { 'month': 'Apr', 'sales': 1800, 'profit': 700 } 212 | ]}" 213 | dataKeys="{['sales', 'profit']}" 214 | nameKey="month" 215 | > 216 | <property name="tooltipTemplate"> 217 | <VStack backgroundColor='white' padding="$space-2"> 218 | <Text fontWeight='bold'>{$tooltip.label}</Text> 219 | <HStack> 220 | <Text color='blue'>Sales: {$tooltip.payload.sales}</Text> 221 | <Text color='green'>Profit: {$tooltip.payload.profit}</Text> 222 | </HStack> 223 | </VStack> 224 | </property> 225 | </AreaChart> 226 | </App> 227 | ``` 228 | 229 | The `tooltipTemplate` prop allows you to customize the appearance and content of chart tooltips. The template receives a `$tooltip` context variable containing: 230 | 231 | - `$tooltip.label`: The label for the data point (typically the nameKey value) 232 | - `$tooltip.payload`: An object containing all data values for the hovered point 233 | - `$tooltip.active`: Boolean indicating if the tooltip is currently active 234 | 235 | %-PROP-END 236 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Heading/HeadingNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React, { 2 | type CSSProperties, 3 | type ForwardedRef, 4 | forwardRef, 5 | type ReactNode, 6 | useCallback, 7 | useContext, 8 | useEffect, 9 | useRef, 10 | useState, 11 | } from "react"; 12 | import { composeRefs } from "@radix-ui/react-compose-refs"; 13 | import classnames from "classnames"; 14 | 15 | import styles from "./Heading.module.scss"; 16 | 17 | import { getMaxLinesStyle } from "../../components-core/utils/css-utils"; 18 | import { TableOfContentsContext } from "../../components-core/TableOfContentsContext"; 19 | import { useIsomorphicLayoutEffect } from "../../components-core/utils/hooks"; 20 | import type { HeadingLevel } from "./abstractions"; 21 | import { Link } from "@remix-run/react"; 22 | import { useAppContext } from "../../components-core/AppContext"; 23 | import type { RegisterComponentApiFn } from "../../abstractions/RendererDefs"; 24 | 25 | export type HeadingProps = { 26 | uid?: string; 27 | level?: HeadingLevel; 28 | children: ReactNode; 29 | sx?: CSSProperties; 30 | style?: CSSProperties; 31 | maxLines?: number; 32 | preserveLinebreaks?: boolean; 33 | ellipses?: boolean; 34 | title?: string; 35 | className?: string; 36 | showAnchor?: boolean; 37 | registerComponentApi?: RegisterComponentApiFn; 38 | [furtherProps: string]: any; 39 | }; 40 | 41 | export const defaultProps: Pick< 42 | HeadingProps, 43 | "level" | "ellipses" | "omitFromToc" | "maxLines" | "preserveLinebreaks" | "showAnchor" 44 | > = { 45 | level: "h1", 46 | ellipses: true, 47 | omitFromToc: false, 48 | maxLines: 0, 49 | preserveLinebreaks: false, 50 | showAnchor: false, 51 | }; 52 | 53 | export const Heading = forwardRef(function Heading( 54 | { 55 | uid, 56 | level = defaultProps.level, 57 | children, 58 | sx, 59 | style, 60 | title, 61 | maxLines = defaultProps.maxLines, 62 | preserveLinebreaks, 63 | ellipses = defaultProps.ellipses, 64 | className, 65 | omitFromToc = defaultProps.omitFromToc, 66 | showAnchor, 67 | registerComponentApi, 68 | ...furtherProps 69 | }: HeadingProps, 70 | forwardedRef: ForwardedRef<HTMLHeadingElement>, 71 | ) { 72 | const Element = level?.toLowerCase() as HeadingLevel; 73 | const elementRef = useRef<HTMLHeadingElement>(null); 74 | const [anchorId, setAnchorId] = useState<string | null>(null); 75 | const anchorRef = useRef<HTMLAnchorElement>(null); 76 | 77 | const tableOfContentsContext = useContext(TableOfContentsContext); 78 | const registerHeading = tableOfContentsContext?.registerHeading; 79 | const appContext = useAppContext(); 80 | if (showAnchor === undefined) { 81 | showAnchor = appContext?.appGlobals?.showHeadingAnchors ?? false; 82 | } 83 | 84 | const ref = forwardedRef ? composeRefs(elementRef, forwardedRef) : elementRef; 85 | 86 | const scrollIntoView = useCallback((options?: ScrollIntoViewOptions) => { 87 | if (elementRef.current) { 88 | elementRef.current.scrollIntoView({ 89 | behavior: 'smooth', 90 | block: 'start', 91 | ...options, 92 | }); 93 | } 94 | }, []); 95 | 96 | const hasOverflow = useCallback(() => { 97 | if (elementRef.current) { 98 | const element = elementRef.current; 99 | return element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight; 100 | } 101 | return false; 102 | }, []); 103 | 104 | useEffect(() => { 105 | registerComponentApi?.({ 106 | scrollIntoView, 107 | hasOverflow, 108 | }); 109 | }, [registerComponentApi, scrollIntoView, hasOverflow]); 110 | 111 | useEffect(() => { 112 | if (elementRef.current) { 113 | const newAnchorId = elementRef.current.textContent 114 | ?.trim() 115 | ?.replace(/[^\w\s-]/g, "") 116 | ?.replace(/\s+/g, "-") 117 | ?.toLowerCase(); 118 | setAnchorId(newAnchorId || null); 119 | } 120 | }, []); 121 | 122 | useIsomorphicLayoutEffect(() => { 123 | if (elementRef.current && anchorId && !omitFromToc) { 124 | return registerHeading?.({ 125 | id: anchorId, 126 | level: parseInt(level.replace("h", "")), 127 | text: elementRef.current.textContent!.trim().replace(/#$/, ""), // Remove trailing # 128 | anchor: anchorRef.current, 129 | }); 130 | } 131 | }, [anchorId, registerHeading, level, omitFromToc]); 132 | 133 | return ( 134 | <Element 135 | {...furtherProps} 136 | ref={ref} 137 | id={uid} 138 | title={title} 139 | style={{ ...sx, ...style, ...getMaxLinesStyle(maxLines) }} 140 | className={classnames(styles.heading, styles[Element], className, { 141 | [styles.truncateOverflow]: maxLines > 0, 142 | [styles.preserveLinebreaks]: preserveLinebreaks, 143 | [styles.noEllipsis]: !ellipses, 144 | })} 145 | > 146 | {anchorId && ( 147 | <span ref={anchorRef} id={anchorId} className={styles.anchorRef} data-anchor={true} /> 148 | )} 149 | {children} 150 | {showAnchor && anchorId && ( 151 | <Link 152 | to={`#${anchorId}`} 153 | aria-hidden="true" 154 | onClick={(event) => { 155 | // cmd/ctrl + click - open in new tab, don't prevent that 156 | if (tableOfContentsContext) { 157 | if (!event.ctrlKey && !event.metaKey && !event.metaKey) { 158 | event.preventDefault(); 159 | } 160 | tableOfContentsContext.scrollToAnchor(anchorId, true); 161 | } 162 | }} 163 | > 164 | # 165 | </Link> 166 | )} 167 | </Element> 168 | ); 169 | }); 170 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Splitter/VSplitter.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { getBounds } from "../../testing/component-test-helpers"; 2 | import { expect, test } from "../../testing/fixtures"; 3 | 4 | // ============================================================================= 5 | // BASIC FUNCTIONALITY TESTS 6 | // ============================================================================= 7 | 8 | test.describe("Basic Functionality", () => { 9 | test("renders with basic setup", async ({ initTestBed, page }) => { 10 | await initTestBed(` 11 | <VSplitter height="200px" width="400px" testId="vsplitter"> 12 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/> 13 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/> 14 | </VSplitter> 15 | `); 16 | 17 | await expect(page.getByTestId("vsplitter")).toBeVisible(); 18 | await expect(page.getByTestId("primary")).toBeVisible(); 19 | await expect(page.getByTestId("secondary")).toBeVisible(); 20 | }); 21 | 22 | test("defaults to vertical orientation", async ({ initTestBed, page }) => { 23 | await initTestBed(` 24 | <VSplitter height="200px" width="400px" testId="vsplitter"> 25 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/> 26 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/> 27 | </VSplitter> 28 | `); 29 | 30 | const primary = page.getByTestId("primary"); 31 | const secondary = page.getByTestId("secondary"); 32 | 33 | const primaryBounds = await getBounds(primary); 34 | const secondaryBounds = await getBounds(secondary); 35 | 36 | // In vertical orientation, primary should be above secondary 37 | expect(primaryBounds.bottom).toBeLessThanOrEqual(secondaryBounds.top + 10); // Allow for small overlap due to resizer 38 | }); 39 | 40 | test("ignores orientation property when explicitly set", async ({ initTestBed, page }) => { 41 | await initTestBed(` 42 | <VSplitter height="200px" width="400px" orientation="horizontal" testId="vsplitter"> 43 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/> 44 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/> 45 | </VSplitter> 46 | `); 47 | 48 | const primary = page.getByTestId("primary"); 49 | const secondary = page.getByTestId("secondary"); 50 | 51 | const primaryBounds = await getBounds(primary); 52 | const secondaryBounds = await getBounds(secondary); 53 | 54 | // Even with orientation="horizontal", VSplitter should still be vertical 55 | // Primary should be above secondary, NOT to the left of it 56 | expect(primaryBounds.bottom).toBeLessThanOrEqual(secondaryBounds.top + 10); 57 | }); 58 | 59 | test("works with swapped property", async ({ initTestBed, page }) => { 60 | await initTestBed(` 61 | <VSplitter height="200px" width="400px" swapped="true" testId="vsplitter"> 62 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/> 63 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/> 64 | </VSplitter> 65 | `); 66 | 67 | const primary = page.getByTestId("primary"); 68 | const secondary = page.getByTestId("secondary"); 69 | 70 | const primaryBounds = await getBounds(primary); 71 | const secondaryBounds = await getBounds(secondary); 72 | 73 | // With swapped=true, secondary should be above primary 74 | expect(secondaryBounds.bottom).toBeLessThanOrEqual(primaryBounds.top + 10); 75 | }); 76 | 77 | test("maintains vertical orientation even with invalid orientation values", async ({ initTestBed, page }) => { 78 | await initTestBed(` 79 | <VSplitter height="200px" width="400px" orientation="invalid-value" testId="vsplitter"> 80 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/> 81 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/> 82 | </VSplitter> 83 | `); 84 | 85 | const primary = page.getByTestId("primary"); 86 | const secondary = page.getByTestId("secondary"); 87 | 88 | const primaryBounds = await getBounds(primary); 89 | const secondaryBounds = await getBounds(secondary); 90 | 91 | // Should still be vertical regardless of invalid orientation value 92 | expect(primaryBounds.bottom).toBeLessThanOrEqual(secondaryBounds.top + 10); 93 | }); 94 | }); 95 | 96 | // ============================================================================= 97 | // ACCESSIBILITY TESTS 98 | // ============================================================================= 99 | 100 | test.describe("Accessibility", () => { 101 | test("resizer has vertical cursor style", async ({ initTestBed, page, createSplitterDriver }) => { 102 | await initTestBed(` 103 | <VSplitter height="200px" width="400px" testId="vsplitter"> 104 | <Stack backgroundColor="lightblue" height="100%"/> 105 | <Stack backgroundColor="darksalmon" height="100%"/> 106 | </VSplitter> 107 | `); 108 | 109 | const vsplitter = page.getByTestId("vsplitter"); 110 | const driver = await createSplitterDriver(vsplitter); 111 | const resizer = await driver.getResizer(); 112 | 113 | // VSplitter should always use vertical cursor (ns-resize) 114 | await expect(resizer).toHaveCSS("cursor", "ns-resize"); 115 | }); 116 | }); 117 | ```