This is page 9 of 179. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ ├── cold-items-taste.md │ ├── config.json │ ├── empty-spiders-dress.md │ ├── shy-windows-allow.md │ ├── sour-coins-read.md │ ├── tame-zebras-invite.md │ ├── three-ideas-invent.md │ ├── twenty-jeans-watch.md │ ├── warm-spies-melt.md │ └── whole-ways-cry.md ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── blog-page-component.png │ │ │ │ ├── blog-scrabble.png │ │ │ │ ├── integrated-blog-search.png │ │ │ │ └── lorem-ipsum.png │ │ │ ├── lorem-ipsum.md │ │ │ ├── newest-post.md │ │ │ ├── older-post.md │ │ │ └── welcome-to-the-xmlui-blog.md │ │ ├── mockServiceWorker.js │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── CONTRIBUTING.md ├── docs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── ComponentRefLinks.txt │ ├── content │ │ ├── _meta.json │ │ ├── components │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── APICall.md │ │ │ ├── App.md │ │ │ ├── AppHeader.md │ │ │ ├── AppState.md │ │ │ ├── AutoComplete.md │ │ │ ├── Avatar.md │ │ │ ├── Backdrop.md │ │ │ ├── Badge.md │ │ │ ├── BarChart.md │ │ │ ├── Bookmark.md │ │ │ ├── Breakout.md │ │ │ ├── Button.md │ │ │ ├── Card.md │ │ │ ├── Carousel.md │ │ │ ├── ChangeListener.md │ │ │ ├── Checkbox.md │ │ │ ├── CHStack.md │ │ │ ├── ColorPicker.md │ │ │ ├── Column.md │ │ │ ├── ContentSeparator.md │ │ │ ├── CVStack.md │ │ │ ├── DataSource.md │ │ │ ├── DateInput.md │ │ │ ├── DatePicker.md │ │ │ ├── DonutChart.md │ │ │ ├── DropdownMenu.md │ │ │ ├── EmojiSelector.md │ │ │ ├── ExpandableItem.md │ │ │ ├── FileInput.md │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FlowLayout.md │ │ │ ├── Footer.md │ │ │ ├── Form.md │ │ │ ├── FormItem.md │ │ │ ├── FormSection.md │ │ │ ├── Fragment.md │ │ │ ├── H1.md │ │ │ ├── H2.md │ │ │ ├── H3.md │ │ │ ├── H4.md │ │ │ ├── H5.md │ │ │ ├── H6.md │ │ │ ├── Heading.md │ │ │ ├── HSplitter.md │ │ │ ├── HStack.md │ │ │ ├── Icon.md │ │ │ ├── IFrame.md │ │ │ ├── Image.md │ │ │ ├── Items.md │ │ │ ├── LabelList.md │ │ │ ├── Legend.md │ │ │ ├── LineChart.md │ │ │ ├── Link.md │ │ │ ├── List.md │ │ │ ├── Logo.md │ │ │ ├── Markdown.md │ │ │ ├── MenuItem.md │ │ │ ├── MenuSeparator.md │ │ │ ├── ModalDialog.md │ │ │ ├── NavGroup.md │ │ │ ├── NavLink.md │ │ │ ├── NavPanel.md │ │ │ ├── NoResult.md │ │ │ ├── NumberBox.md │ │ │ ├── Option.md │ │ │ ├── Page.md │ │ │ ├── PageMetaTitle.md │ │ │ ├── Pages.md │ │ │ ├── Pagination.md │ │ │ ├── PasswordInput.md │ │ │ ├── PieChart.md │ │ │ ├── ProgressBar.md │ │ │ ├── Queue.md │ │ │ ├── RadioGroup.md │ │ │ ├── RealTimeAdapter.md │ │ │ ├── Redirect.md │ │ │ ├── Select.md │ │ │ ├── Slider.md │ │ │ ├── Slot.md │ │ │ ├── SpaceFiller.md │ │ │ ├── Spinner.md │ │ │ ├── Splitter.md │ │ │ ├── Stack.md │ │ │ ├── StickyBox.md │ │ │ ├── SubMenuItem.md │ │ │ ├── Switch.md │ │ │ ├── TabItem.md │ │ │ ├── Table.md │ │ │ ├── TableOfContents.md │ │ │ ├── Tabs.md │ │ │ ├── Text.md │ │ │ ├── TextArea.md │ │ │ ├── TextBox.md │ │ │ ├── Theme.md │ │ │ ├── TimeInput.md │ │ │ ├── Timer.md │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneSwitch.md │ │ │ ├── Tooltip.md │ │ │ ├── Tree.md │ │ │ ├── VSplitter.md │ │ │ ├── VStack.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ ├── xmlui-spreadsheet │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Spreadsheet.md │ │ │ └── xmlui-website-blocks │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Carousel.md │ │ │ ├── HelloMd.md │ │ │ ├── HeroSection.md │ │ │ └── ScrollToTop.md │ │ └── extensions │ │ ├── _meta.json │ │ ├── xmlui-animations │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Animation.md │ │ │ ├── FadeAnimation.md │ │ │ ├── FadeInAnimation.md │ │ │ ├── FadeOutAnimation.md │ │ │ ├── ScaleAnimation.md │ │ │ └── SlideInAnimation.md │ │ └── xmlui-website-blocks │ │ ├── _meta.json │ │ ├── _overview.md │ │ ├── Carousel.md │ │ ├── HelloMd.md │ │ ├── HeroSection.md │ │ └── ScrollToTop.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── feed.rss │ │ ├── mockServiceWorker.js │ │ ├── pages │ │ │ ├── _meta.json │ │ │ ├── app-structure.md │ │ │ ├── build-editor-component.md │ │ │ ├── build-hello-world-component.md │ │ │ ├── components-intro.md │ │ │ ├── context-variables.md │ │ │ ├── forms.md │ │ │ ├── globals.md │ │ │ ├── glossary.md │ │ │ ├── helper-tags.md │ │ │ ├── hosted-deployment.md │ │ │ ├── howto │ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md │ │ │ │ ├── chain-a-refetch.md │ │ │ │ ├── debug-a-component.md │ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md │ │ │ │ ├── delegate-a-method.md │ │ │ │ ├── do-custom-form-validation.md │ │ │ │ ├── expose-a-method-from-a-component.md │ │ │ │ ├── filter-and-transform-data-from-an-api.md │ │ │ │ ├── group-items-in-list-by-a-property.md │ │ │ │ ├── handle-background-operations.md │ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md │ │ │ │ ├── make-a-set-of-equal-width-cards.md │ │ │ │ ├── make-a-table-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── actions.md │ ├── AppRoot.md │ ├── component-apis.md │ ├── component-rendering.md │ ├── component-review-checklist.md │ ├── containers.md │ ├── data-sources.md │ ├── e2e-summary.md │ ├── expression-evaluation.md │ ├── glossary.md │ ├── helper-components.md │ ├── index.md │ ├── loaders.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── rendering-fundamentals.md │ ├── reusable-components.md │ ├── standalone-apps.md │ ├── state-management.md │ └── xmlui-extensibility.xlsx ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.tsx │ │ ├── SelectionStore │ │ │ ├── SelectionStore.md │ │ │ ├── SelectionStore.tsx │ │ │ └── SelectionStoreNative.tsx │ │ ├── Slider │ │ │ ├── Slider.md │ │ │ ├── Slider.module.scss │ │ │ ├── Slider.spec.ts │ │ │ ├── Slider.tsx │ │ │ └── SliderNative.tsx │ │ ├── Slot │ │ │ ├── Slot.md │ │ │ ├── Slot.spec.ts │ │ │ └── Slot.ts │ │ ├── SlotItem.tsx │ │ ├── SpaceFiller │ │ │ ├── SpaceFiller.md │ │ │ ├── SpaceFiller.module.scss │ │ │ ├── SpaceFiller.spec.ts │ │ │ ├── SpaceFiller.tsx │ │ │ └── SpaceFillerNative.tsx │ │ ├── Spinner │ │ │ ├── Spinner.md │ │ │ ├── Spinner.module.scss │ │ │ ├── Spinner.spec.ts │ │ │ ├── Spinner.tsx │ │ │ └── SpinnerNative.tsx │ │ ├── Splitter │ │ │ ├── HSplitter.md │ │ │ ├── HSplitter.spec.ts │ │ │ ├── Splitter.md │ │ │ ├── Splitter.module.scss │ │ │ ├── Splitter.spec.ts │ │ │ ├── Splitter.tsx │ │ │ ├── SplitterNative.tsx │ │ │ ├── utils.ts │ │ │ ├── VSplitter.md │ │ │ └── VSplitter.spec.ts │ │ ├── Stack │ │ │ ├── CHStack.md │ │ │ ├── CHStack.spec.ts │ │ │ ├── CVStack.md │ │ │ ├── CVStack.spec.ts │ │ │ ├── HStack.md │ │ │ ├── HStack.spec.ts │ │ │ ├── Stack.md │ │ │ ├── Stack.module.scss │ │ │ ├── Stack.spec.ts │ │ │ ├── Stack.tsx │ │ │ ├── StackNative.tsx │ │ │ ├── VStack.md │ │ │ └── VStack.spec.ts │ │ ├── StickyBox │ │ │ ├── StickyBox.md │ │ │ ├── StickyBox.module.scss │ │ │ ├── StickyBox.tsx │ │ │ └── StickyBoxNative.tsx │ │ ├── Switch │ │ │ ├── Switch.md │ │ │ ├── Switch.spec.ts │ │ │ └── Switch.tsx │ │ ├── Table │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── react-table-config.d.ts │ │ │ ├── Table.md │ │ │ ├── Table.module.scss │ │ │ ├── Table.spec.ts │ │ │ ├── Table.tsx │ │ │ ├── TableNative.tsx │ │ │ └── useRowSelection.tsx │ │ ├── TableOfContents │ │ │ ├── TableOfContents.module.scss │ │ │ ├── TableOfContents.spec.ts │ │ │ ├── TableOfContents.tsx │ │ │ └── TableOfContentsNative.tsx │ │ ├── Tabs │ │ │ ├── TabContext.tsx │ │ │ ├── TabItem.md │ │ │ ├── TabItem.tsx │ │ │ ├── TabItemNative.tsx │ │ │ ├── Tabs.md │ │ │ ├── Tabs.module.scss │ │ │ ├── Tabs.spec.ts │ │ │ ├── Tabs.tsx │ │ │ └── TabsNative.tsx │ │ ├── Text │ │ │ ├── Text.md │ │ │ ├── Text.module.scss │ │ │ ├── Text.spec.ts │ │ │ ├── Text.tsx │ │ │ └── TextNative.tsx │ │ ├── TextArea │ │ │ ├── TextArea.md │ │ │ ├── TextArea.module.scss │ │ │ ├── TextArea.spec.ts │ │ │ ├── TextArea.tsx │ │ │ ├── TextAreaNative.tsx │ │ │ ├── TextAreaResizable.tsx │ │ │ └── useComposedRef.ts │ │ ├── TextBox │ │ │ ├── TextBox.md │ │ │ ├── TextBox.module.scss │ │ │ ├── TextBox.spec.ts │ │ │ ├── TextBox.tsx │ │ │ └── TextBoxNative.tsx │ │ ├── Theme │ │ │ ├── NotificationToast.tsx │ │ │ ├── Theme.md │ │ │ ├── Theme.module.scss │ │ │ ├── Theme.spec.ts │ │ │ ├── Theme.tsx │ │ │ └── ThemeNative.tsx │ │ ├── TimeInput │ │ │ ├── TimeInput.md │ │ │ ├── TimeInput.module.scss │ │ │ ├── TimeInput.spec.ts │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeInputNative.tsx │ │ │ └── utils.ts │ │ ├── Timer │ │ │ ├── Timer.md │ │ │ ├── Timer.spec.ts │ │ │ ├── Timer.tsx │ │ │ └── TimerNative.tsx │ │ ├── Toggle │ │ │ ├── Toggle.module.scss │ │ │ └── Toggle.tsx │ │ ├── ToneChangerButton │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneChangerButton.spec.ts │ │ │ └── ToneChangerButton.tsx │ │ ├── ToneSwitch │ │ │ ├── ToneSwitch.md │ │ │ ├── ToneSwitch.module.scss │ │ │ ├── ToneSwitch.spec.ts │ │ │ ├── ToneSwitch.tsx │ │ │ └── ToneSwitchNative.tsx │ │ ├── Tooltip │ │ │ ├── Tooltip.md │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.spec.ts │ │ │ ├── Tooltip.tsx │ │ │ └── TooltipNative.tsx │ │ ├── Tree │ │ │ ├── testData.ts │ │ │ ├── Tree-dynamic.spec.ts │ │ │ ├── Tree-icons.spec.ts │ │ │ ├── Tree.md │ │ │ ├── Tree.spec.ts │ │ │ ├── TreeComponent.module.scss │ │ │ ├── TreeComponent.tsx │ │ │ └── TreeNative.tsx │ │ ├── TreeDisplay │ │ │ ├── TreeDisplay.md │ │ │ ├── TreeDisplay.module.scss │ │ │ ├── TreeDisplay.tsx │ │ │ └── TreeDisplayNative.tsx │ │ ├── ValidationSummary │ │ │ ├── ValidationSummary.module.scss │ │ │ └── ValidationSummary.tsx │ │ └── VisuallyHidden.tsx │ ├── components-core │ │ ├── abstractions │ │ │ ├── ComponentRenderer.ts │ │ │ ├── LoaderRenderer.ts │ │ │ ├── standalone.ts │ │ │ └── treeAbstractions.ts │ │ ├── action │ │ │ ├── actions.ts │ │ │ ├── APICall.tsx │ │ │ ├── FileDownloadAction.tsx │ │ │ ├── FileUploadAction.tsx │ │ │ ├── NavigateAction.tsx │ │ │ └── TimedAction.tsx │ │ ├── ApiBoundComponent.tsx │ │ ├── appContext │ │ │ ├── date-functions.ts │ │ │ ├── math-function.ts │ │ │ └── misc-utils.ts │ │ ├── AppContext.tsx │ │ ├── behaviors │ │ │ ├── Behavior.tsx │ │ │ ├── BehaviorContext.tsx │ │ │ └── CoreBehaviors.tsx │ │ ├── component-hooks.ts │ │ ├── ComponentDecorator.tsx │ │ ├── ComponentViewer.tsx │ │ ├── CompoundComponent.tsx │ │ ├── constants.ts │ │ ├── DebugViewProvider.tsx │ │ ├── descriptorHelper.ts │ │ ├── devtools │ │ │ ├── InspectorDialog.module.scss │ │ │ ├── InspectorDialog.tsx │ │ │ └── InspectorDialogVisibilityContext.tsx │ │ ├── EngineError.ts │ │ ├── event-handlers.ts │ │ ├── InspectorButton.module.scss │ │ ├── InspectorContext.tsx │ │ ├── interception │ │ │ ├── abstractions.ts │ │ │ ├── ApiInterceptor.ts │ │ │ ├── ApiInterceptorProvider.tsx │ │ │ ├── apiInterceptorWorker.ts │ │ │ ├── Backend.ts │ │ │ ├── Errors.ts │ │ │ ├── IndexedDb.ts │ │ │ ├── initMock.ts │ │ │ ├── InMemoryDb.ts │ │ │ ├── ReadonlyCollection.ts │ │ │ └── useApiInterceptorContext.tsx │ │ ├── loader │ │ │ ├── ApiLoader.tsx │ │ │ ├── DataLoader.tsx │ │ │ ├── ExternalDataLoader.tsx │ │ │ ├── Loader.tsx │ │ │ ├── MockLoaderRenderer.tsx │ │ │ └── PageableLoader.tsx │ │ ├── LoaderComponent.tsx │ │ ├── markup-check.ts │ │ ├── parts.ts │ │ ├── renderers.ts │ │ ├── rendering │ │ │ ├── AppContent.tsx │ │ │ ├── AppRoot.tsx │ │ │ ├── AppWrapper.tsx │ │ │ ├── buildProxy.ts │ │ │ ├── collectFnVarDeps.ts │ │ │ ├── ComponentAdapter.tsx │ │ │ ├── ComponentWrapper.tsx │ │ │ ├── Container.tsx │ │ │ ├── containers.ts │ │ │ ├── ContainerWrapper.tsx │ │ │ ├── ErrorBoundary.module.scss │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── InvalidComponent.module.scss │ │ │ ├── InvalidComponent.tsx │ │ │ ├── nodeUtils.ts │ │ │ ├── reducer.ts │ │ │ ├── renderChild.tsx │ │ │ ├── StandaloneComponent.tsx │ │ │ ├── StateContainer.tsx │ │ │ ├── UnknownComponent.module.scss │ │ │ ├── UnknownComponent.tsx │ │ │ └── valueExtractor.ts │ │ ├── reportEngineError.ts │ │ ├── RestApiProxy.ts │ │ ├── script-runner │ │ │ ├── asyncProxy.ts │ │ │ ├── AttributeValueParser.ts │ │ │ ├── bannedFunctions.ts │ │ │ ├── BindingTreeEvaluationContext.ts │ │ │ ├── eval-tree-async.ts │ │ │ ├── eval-tree-common.ts │ │ │ ├── eval-tree-sync.ts │ │ │ ├── ParameterParser.ts │ │ │ ├── process-statement-async.ts │ │ │ ├── process-statement-common.ts │ │ │ ├── process-statement-sync.ts │ │ │ ├── ScriptingSourceTree.ts │ │ │ ├── simplify-expression.ts │ │ │ ├── statement-queue.ts │ │ │ └── visitors.ts │ │ ├── StandaloneApp.tsx │ │ ├── StandaloneExtensionManager.ts │ │ ├── TableOfContentsContext.tsx │ │ ├── theming │ │ │ ├── _themes.scss │ │ │ ├── component-layout-resolver.ts │ │ │ ├── extendThemeUtils.ts │ │ │ ├── hvar.ts │ │ │ ├── layout-resolver.ts │ │ │ ├── parse-layout-props.ts │ │ │ ├── StyleContext.tsx │ │ │ ├── StyleRegistry.ts │ │ │ ├── ThemeContext.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ ├── themes │ │ │ │ ├── base-utils.ts │ │ │ │ ├── palette.ts │ │ │ │ ├── root.ts │ │ │ │ ├── solid.ts │ │ │ │ ├── theme-colors.ts │ │ │ │ └── xmlui.ts │ │ │ ├── themeVars.module.scss │ │ │ ├── themeVars.ts │ │ │ ├── transformThemeVars.ts │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── actionUtils.ts │ │ │ ├── audio-utils.ts │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /packages/xmlui-playground/src/providers/Toast.module.scss: -------------------------------------------------------------------------------- ```scss 1 | .ToastViewport { 2 | --viewport-padding: 25px; 3 | position: fixed; 4 | bottom: 0; 5 | right: 0; 6 | display: flex; 7 | flex-direction: column; 8 | padding: 2rem; 9 | gap: 10px; 10 | width: 390px; 11 | max-width: 100vw; 12 | margin: 0; 13 | list-style: none; 14 | z-index: 2147483647; 15 | outline: none; 16 | } 17 | 18 | .ToastClose { 19 | color: #fff; 20 | justify-self: flex-end; 21 | } 22 | 23 | .ToastRoot { 24 | background-color: rgb(23, 162, 184); 25 | border-radius: 6px; 26 | box-shadow: 0px 0.8px 2px rgba(0, 0, 0, 0.032), 0px 2.7px 6.7px rgba(0, 0, 0, 0.048), 27 | 0px 12px 30px rgba(0, 0, 0, 0.08); 28 | padding: 15px; 29 | display: grid; 30 | grid-template-areas: "title action" "description action"; 31 | grid-template-columns: auto max-content; 32 | align-items: center; 33 | } 34 | 35 | .error { 36 | background-color: rgb(220, 53, 69); 37 | } 38 | 39 | .warning { 40 | background-color: rgb(255, 193, 7); 41 | } 42 | 43 | .info { 44 | background-color: rgb(23, 162, 184); 45 | } 46 | 47 | .success { 48 | background-color: rgb(40, 167, 69); 49 | } 50 | 51 | .ToastRoot[data-state="open"] { 52 | animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1); 53 | } 54 | .ToastRoot[data-state="closed"] { 55 | animation: hide 100ms ease-in; 56 | } 57 | .ToastRoot[data-swipe="cancel"] { 58 | transform: translateX(0); 59 | transition: transform 200ms ease-out; 60 | } 61 | 62 | @keyframes hide { 63 | from { 64 | opacity: 1; 65 | } 66 | to { 67 | opacity: 0; 68 | } 69 | } 70 | 71 | @keyframes slideIn { 72 | from { 73 | transform: translateX(calc(100% + 2rem)); 74 | } 75 | to { 76 | transform: translateX(0); 77 | } 78 | } 79 | 80 | .ToastTitle { 81 | color: #fff; 82 | grid-area: title; 83 | margin-bottom: 5px; 84 | font-weight: 500; 85 | font-size: 15px; 86 | } 87 | 88 | .ToastDescription { 89 | color: #fff; 90 | grid-area: description; 91 | margin: 0; 92 | font-size: 13px; 93 | line-height: 1.3; 94 | } 95 | 96 | .ToastAction { 97 | grid-area: action; 98 | } 99 | ``` -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "xmlui-docs", 3 | "private": true, 4 | "version": "0.0.10", 5 | "scripts": { 6 | "start": "echo '====================================================================\nExecuting \"npm run watch-docs-content\" in the project root,\nyou get automatic content generation based on xmlui metadata!\n====================================================================\n' && xmlui start", 7 | "preview": "xmlui preview", 8 | "gen:releases": "node scripts/get-releases.js --output 'public/resources/files/releases.json'", 9 | "gen:download-latest-xmlui-release": "node scripts/download-latest-xmlui.js", 10 | "gen:rss": "node scripts/generate-rss.js", 11 | "build:docs": "xmlui build --buildMode=INLINE_ALL --withMock && npm run gen:download-latest-xmlui-release && npm run gen:rss", 12 | "build-optimized": "npm run gen:releases && npm run gen:download-latest-xmlui-release && npm run gen:rss && npx xmlui-optimizer", 13 | "release-ci-optimized": "npm run build-optimized && xmlui zip-dist --source=xmlui-optimized-output --target=ui-optimized.zip", 14 | "preview-optimized": "npx serve@latest ./xmlui-optimized-output" 15 | }, 16 | "dependencies": { 17 | "@shikijs/langs": "3.4.2", 18 | "shiki": "^3.3.0", 19 | "xmlui": "*", 20 | "xmlui-playground": "*", 21 | "xmlui-search": "*", 22 | "xmlui-hello-world": "*" 23 | }, 24 | "msw": { 25 | "workerDirectory": [ 26 | "public" 27 | ] 28 | }, 29 | "devDependencies": { 30 | "xmlui-website-blocks": "*", 31 | "xmlui-animations": "*", 32 | "@emotion/is-prop-valid": "^1.3.1", 33 | "@octokit/rest": "^22.0.0", 34 | "remark-parse": "11.0.0", 35 | "remark-stringify": "11.0.0", 36 | "strip-markdown": "6.0.0", 37 | "unified": "11.0.5" 38 | } 39 | } 40 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Footer/Footer.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./Footer.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { Footer } from "./FooterNative"; 6 | import { createMetadata } from "../metadata-helpers"; 7 | import classnames from "classnames"; 8 | 9 | const COMP = "Footer"; 10 | 11 | export const FooterMd = createMetadata({ 12 | status: "stable", 13 | description: 14 | "`Footer` provides a designated area at the bottom of your application for " + 15 | "footer content such as branding, copyright notices, or utility controls like " + 16 | "theme toggles.", 17 | themeVars: parseScssVar(styles.themeVars), 18 | limitThemeVarsToComponent: true, 19 | defaultThemeVars: { 20 | [`backgroundColor-${COMP}`]: "$backgroundColor-AppHeader", 21 | [`verticalAlignment-${COMP}`]: "center", 22 | [`fontSize-${COMP}`]: "$fontSize-sm", 23 | [`textColor-${COMP}`]: "$textColor-secondary", 24 | [`maxWidth-content-${COMP}`]: "$maxWidth-content", 25 | [`borderTop-${COMP}`]: `1px solid $borderColor`, 26 | [`padding-${COMP}`]: "$space-2 $space-4", 27 | [`gap-${COMP}`]: "$space-normal", 28 | [`margin-${COMP}`]: `0 auto`, 29 | light: { 30 | // --- No light-specific theme vars 31 | }, 32 | dark: { 33 | // --- No dark-specific theme vars 34 | }, 35 | }, 36 | }); 37 | 38 | export const footerRenderer = createComponentRenderer( 39 | COMP, 40 | FooterMd, 41 | ({ node, renderChild, className, layoutContext }) => { 42 | return ( 43 | <Footer className={classnames(layoutContext?.themeClassName, className)}> 44 | {renderChild(node.children, { 45 | type: "Stack", 46 | orientation: "horizontal", 47 | })} 48 | </Footer> 49 | ); 50 | }, 51 | ); 52 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Spinner/Spinner.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $size-Spinner: createThemeVar("size-Spinner"); 11 | $thickness-Spinner: createThemeVar("thickness-Spinner"); 12 | $borderColor-Spinner: createThemeVar("borderColor-Spinner"); 13 | 14 | @layer components { 15 | .lds-ring { 16 | display: inline-block; 17 | position: relative; 18 | width: $size-Spinner; 19 | height: $size-Spinner; 20 | } 21 | 22 | .lds-ring div { 23 | box-sizing: border-box; 24 | display: block; 25 | position: absolute; 26 | width: 80%; 27 | height: 80%; 28 | margin: 10%; 29 | border-width: $thickness-Spinner; 30 | border-style: solid; 31 | border-radius: 50%; 32 | animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 33 | border-color: $borderColor-Spinner transparent transparent transparent; 34 | } 35 | 36 | .lds-ring div:nth-child(1) { 37 | animation-delay: -0.45s; 38 | } 39 | 40 | .lds-ring div:nth-child(2) { 41 | animation-delay: -0.3s; 42 | } 43 | 44 | .lds-ring div:nth-child(3) { 45 | animation-delay: -0.15s; 46 | } 47 | 48 | @keyframes lds-ring { 49 | 0% { 50 | transform: rotate(0deg); 51 | } 52 | 100% { 53 | transform: rotate(360deg); 54 | } 55 | } 56 | 57 | .fullScreenSpinnerWrapper { 58 | width: 100%; 59 | height: 100%; 60 | position: absolute; 61 | inset: 0; 62 | display: flex; 63 | align-items: center; 64 | justify-content: center; 65 | } 66 | } 67 | 68 | // --- We export the theme variables to add them to the component renderer 69 | :export { 70 | themeVars: t.json-stringify($themeVars); 71 | } 72 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ChangeListener/ChangeListener.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { createMetadata, dDidChange } from "../metadata-helpers"; 3 | import { ChangeListener, defaultProps } from "./ChangeListenerNative"; 4 | 5 | const COMP = "ChangeListener"; 6 | 7 | export const ChangeListenerMd = createMetadata({ 8 | status: "stable", 9 | description: 10 | "`ChangeListener` is an invisible component that watches for changes in values " + 11 | "and triggers actions in response. It's essential for creating reactive behavior " + 12 | "when you need to respond to data changes, state updates, or component property " + 13 | "modifications outside of normal event handlers.", 14 | props: { 15 | listenTo: { 16 | description: 17 | "Value to the changes of which this component listens. If this property is not set, " + 18 | "the `ChangeListener` is inactive.", 19 | valueType: "any", 20 | }, 21 | throttleWaitInMs: { 22 | description: 23 | `This variable sets a throttling time (in milliseconds) to apply when executing the \`didChange\` ` + 24 | `event handler. All changes within that throttling time will only fire the \`didChange\` event once.`, 25 | valueType: "number", 26 | defaultValue: defaultProps.throttleWaitInMs, 27 | }, 28 | }, 29 | events: { 30 | didChange: dDidChange(COMP), 31 | }, 32 | }); 33 | 34 | export const changeListenerComponentRenderer = createComponentRenderer( 35 | COMP, 36 | ChangeListenerMd, 37 | ({ node, lookupEventHandler, extractValue }) => { 38 | return ( 39 | <ChangeListener 40 | listenTo={extractValue(node.props.listenTo)} 41 | throttleWaitInMs={extractValue(node.props.throttleWaitInMs)} 42 | onChange={lookupEventHandler("didChange")} 43 | /> 44 | ); 45 | }, 46 | ); 47 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Badge/Badge.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $component: "Badge"; 11 | $themeVars: t.composeBorderVars($themeVars, $component); 12 | $themeVars: t.composePaddingVars($themeVars, $component); 13 | $themeVars: t.composeTextVars($themeVars, $component); 14 | $themeVars: t.composeBorderVars($themeVars, "#{$component}-pill"); 15 | $themeVars: t.composePaddingVars($themeVars, "#{$component}-pill"); 16 | $themeVars: t.composeTextVars($themeVars, "#{$component}-pill"); 17 | 18 | @layer components { 19 | .badge { 20 | @include t.paddingVars($themeVars, $component); 21 | @include t.borderVars($themeVars, $component); 22 | @include t.textVars($themeVars, $component); 23 | display: inline-block; 24 | width: fit-content; 25 | height: fit-content; 26 | vertical-align: top; 27 | min-width: fit-content; 28 | overflow: hidden; 29 | text-overflow: ellipsis; 30 | text-transform: uppercase; 31 | } 32 | 33 | .pill { 34 | @include t.paddingVars($themeVars, "#{$component}-pill"); 35 | @include t.borderVars($themeVars, "#{$component}-pill"); 36 | @include t.textVars($themeVars, "#{$component}-pill"); 37 | display: inline-block; 38 | width: fit-content; 39 | height: fit-content; 40 | vertical-align: top; 41 | min-width: fit-content; 42 | overflow: hidden; 43 | text-overflow: ellipsis; 44 | border-radius: 9999px; 45 | } 46 | } 47 | 48 | // --- We export the theme variables to add them to the component renderer 49 | :export { 50 | themeVars: t.json-stringify($themeVars); 51 | } 52 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Link/Link.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | ## Using Link 4 | 5 | ### `Link` Appearance 6 | 7 | You can use the `label` and `icon` properties of a `Link` to set its text and icon to display. If you want a custom appearance, you can nest your visual representation into `Link`: 8 | 9 | ```xmlui-pg copy {3-6} display name="Example: custom Link content" 10 | <App> 11 | <Link to="https://docs.xmlui.org/" target="_blank"> 12 | <HStack verticalAlignment="center"> 13 | <Stack width="16px" height="16px" backgroundColor="purple" /> 14 | XMLUI introduction 15 | </HStack> 16 | </Link> 17 | </App> 18 | ``` 19 | 20 | %-DESC-END 21 | 22 | %-PROP-START active 23 | 24 | ```xmlui-pg copy display name="Example: active" /active/ 25 | <App> 26 | <Link>I'm an inactive link (by default)</Link> 27 | <Link active="true">I'm an active link</Link> 28 | <Link active="false">I'm an inactive link (explicit setting)</Link> 29 | </App> 30 | ``` 31 | 32 | %-PROP-END 33 | 34 | %-PROP-START enabled 35 | 36 | ```xmlui-pg copy display name="Example: enabled" /enabled/ 37 | <App> 38 | <Link>I'm an enabled link (by default)</Link> 39 | <Link enabled="false">I'm a disabled link</Link> 40 | <Link enabled="true">I'm an enabled link (explicit setting)</Link> 41 | </App> 42 | ``` 43 | 44 | %-PROP-END 45 | 46 | %-PROP-START icon 47 | 48 | ```xmlui-pg copy display name="Example: icon" 49 | <App> 50 | <Link icon="home" label="Home" /> 51 | <Link icon="drive">Drives</Link> 52 | </App> 53 | ``` 54 | 55 | >[!INFO] 56 | > If you want to specify paddings and gaps or put the icon to the right of the link text, use your custom link template (nest it into `Link`). 57 | 58 | %-PROP-END 59 | 60 | %-PROP-START target 61 | 62 | The following sample opens its link in a new tab: 63 | 64 | ```xmlui-pg copy display name="Example: target" 65 | <App> 66 | <Link to="https://docs.xmlui.org/" target="_blank"> 67 | Open XMLUI overview in a new tab 68 | </Link> 69 | </App> 70 | ``` 71 | 72 | %-PROP-END 73 | ``` -------------------------------------------------------------------------------- /docs/content/components/xmlui-website-blocks/ScrollToTop.md: -------------------------------------------------------------------------------- ```markdown 1 | # ScrollToTop [#scrolltotop] 2 | 3 | A floating button that scrolls the page to the top when clicked 4 | 5 | ## Properties 6 | 7 | ### `behavior` (default: "smooth") 8 | 9 | Scroll behavior when scrolling to top 10 | 11 | ### `icon` (default: "chevronup") 12 | 13 | Name of the icon to display in the button 14 | 15 | ### `position` (default: "end") 16 | 17 | Horizontal position of the button at the bottom of the screen 18 | 19 | ### `threshold` (default: 300) 20 | 21 | Scroll position threshold (in pixels) after which the button becomes visible 22 | 23 | ### `visible` (default: true) 24 | 25 | Whether the button is visible 26 | 27 | ## Events 28 | 29 | ### `click` 30 | 31 | Triggered when the scroll to top button is clicked 32 | 33 | ## Exposed Methods 34 | 35 | This component does not expose any methods. 36 | 37 | ## Parts 38 | 39 | The component has some parts that can be styled through layout properties and theme variables separately: 40 | 41 | - **`icon`**: The icon displayed inside the scroll to top button 42 | 43 | ## Styling 44 | 45 | ### Theme Variables 46 | 47 | | Variable | Default Value (Light) | Default Value (Dark) | 48 | | --- | --- | --- | 49 | | [backgroundColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary | $color-primary | 50 | | [borderColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary-dark | $color-primary-dark | 51 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-ScrollToTop | $space-24 | $space-24 | 52 | | bottom-ScrollToTop | $space-16 | $space-16 | 53 | | [color](../styles-and-themes/common-units/#color)-ScrollToTop | $color-surface-0 | $color-surface-0 | 54 | | horizontalSpacing-ScrollToTop | $space-16 | $space-16 | 55 | | shadow-ScrollToTop | $shadow-lg | $shadow-lg | 56 | | [size](../styles-and-themes/common-units/#size)-ScrollToTop | 48px | 48px | 57 | | zIndex-ScrollToTop | 1000 | 1000 | 58 | ``` -------------------------------------------------------------------------------- /docs/content/extensions/xmlui-website-blocks/ScrollToTop.md: -------------------------------------------------------------------------------- ```markdown 1 | # ScrollToTop [#scrolltotop] 2 | 3 | A floating button that scrolls the page to the top when clicked 4 | 5 | ## Properties 6 | 7 | ### `behavior` (default: "smooth") 8 | 9 | Scroll behavior when scrolling to top 10 | 11 | ### `icon` (default: "chevronup") 12 | 13 | Name of the icon to display in the button 14 | 15 | ### `position` (default: "end") 16 | 17 | Horizontal position of the button at the bottom of the screen 18 | 19 | ### `threshold` (default: 300) 20 | 21 | Scroll position threshold (in pixels) after which the button becomes visible 22 | 23 | ### `visible` (default: true) 24 | 25 | Whether the button is visible 26 | 27 | ## Events 28 | 29 | ### `click` 30 | 31 | Triggered when the scroll to top button is clicked 32 | 33 | ## Exposed Methods 34 | 35 | This component does not expose any methods. 36 | 37 | ## Parts 38 | 39 | The component has some parts that can be styled through layout properties and theme variables separately: 40 | 41 | - **`icon`**: The icon displayed inside the scroll to top button 42 | 43 | ## Styling 44 | 45 | ### Theme Variables 46 | 47 | | Variable | Default Value (Light) | Default Value (Dark) | 48 | | --- | --- | --- | 49 | | [backgroundColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary | $color-primary | 50 | | [borderColor](../styles-and-themes/common-units/#color)-ScrollToTop | $color-primary-dark | $color-primary-dark | 51 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-ScrollToTop | $space-24 | $space-24 | 52 | | bottom-ScrollToTop | $space-16 | $space-16 | 53 | | [color](../styles-and-themes/common-units/#color)-ScrollToTop | $color-surface-0 | $color-surface-0 | 54 | | horizontalSpacing-ScrollToTop | $space-16 | $space-16 | 55 | | shadow-ScrollToTop | $shadow-lg | $shadow-lg | 56 | | [size](../styles-and-themes/common-units/#size)-ScrollToTop | 48px | 48px | 57 | | zIndex-ScrollToTop | 1000 | 1000 | 58 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/InspectButton/InspectButton.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { Button } from "../Button/ButtonNative"; 3 | import { useInspectMode } from "../../components-core/InspectorContext"; 4 | import styles from "./InspectButton.module.scss"; 5 | import { parseScssVar } from "../../components-core/theming/themeVars"; 6 | import type { CSSProperties, ReactNode } from "react"; 7 | import { PiFileCode } from "react-icons/pi"; 8 | import { createMetadata } from "../metadata-helpers"; 9 | import classnames from "classnames"; 10 | 11 | const COMP = "InspectButton"; 12 | export const InspectButtonMd = createMetadata({ 13 | status: "experimental", 14 | description: 15 | "This component displays a button that can turn the inspection " + 16 | "mode of a running XMLUI app on or off.", 17 | props: {}, 18 | themeVars: parseScssVar(styles.themeVars), 19 | }); 20 | 21 | function InspectButton({ children, style, className }: { children: ReactNode; style?: CSSProperties, className?: string }) { 22 | const { setInspectMode, inspectMode } = useInspectMode(); 23 | 24 | return ( 25 | <Button 26 | style={style} 27 | className={classnames(styles.inspectButton, className)} 28 | themeColor={inspectMode ? "primary" : "secondary"} 29 | variant={inspectMode ? "solid" : "outlined"} 30 | onClick={() => { 31 | setInspectMode((prev: any) => !prev); 32 | }} 33 | > 34 | <PiFileCode className={styles.icon}/> 35 | {children} 36 | </Button> 37 | ); 38 | } 39 | 40 | /** 41 | * Define the renderer for the Button component 42 | */ 43 | export const inspectButtonComponentRenderer = createComponentRenderer( 44 | COMP, 45 | InspectButtonMd, 46 | ({ renderChild, node, className }) => { 47 | return <InspectButton className={className}>{renderChild(node.children)}</InspectButton>; 48 | }, 49 | ); 50 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/NoResult/NoResult.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./NoResult.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { createMetadata, dLabel } from "../metadata-helpers"; 6 | import { NoResult, defaultProps } from "./NoResultNative"; 7 | 8 | const COMP = "NoResult"; 9 | 10 | export const NoResultMd = createMetadata({ 11 | status: "stable", 12 | description: "`NoResult` displays a visual indication that a query or search returned nothing.", 13 | props: { 14 | label: dLabel(), 15 | icon: { 16 | description: `This property defines the icon to display with the component.`, 17 | valueType: "string", 18 | defaultValue: defaultProps.icon, 19 | }, 20 | hideIcon: { 21 | description: `This boolean property indicates if the icon should be hidden.`, 22 | valueType: "boolean", 23 | defaultValue: defaultProps.hideIcon, 24 | }, 25 | }, 26 | themeVars: parseScssVar(styles.themeVars), 27 | defaultThemeVars: { 28 | [`border-${COMP}`]: "0px solid $borderColor", 29 | [`paddingVertical-${COMP}`]: "$space-2", 30 | [`gap-icon-${COMP}`]: "$space-2", 31 | [`size-icon-${COMP}`]: "$space-8", 32 | light: { 33 | // --- No light-specific theme vars 34 | }, 35 | dark: { 36 | // --- No dark-specific theme vars 37 | }, 38 | }, 39 | }); 40 | 41 | export const noResultComponentRenderer = createComponentRenderer( 42 | COMP, 43 | NoResultMd, 44 | ({ node, extractValue, className }) => { 45 | return ( 46 | <NoResult 47 | label={extractValue.asDisplayText(node.props.label || node.children || "No results found")} 48 | icon={node.props.icon} 49 | hideIcon={extractValue.asOptionalBoolean(node.props.hideIcon)} 50 | className={className} 51 | /> 52 | ); 53 | }, 54 | ); 55 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/unlink.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M10 12.4503C10.3236 12.894 10.7365 13.2612 11.2106 13.5269C11.6847 13.7926 12.209 13.9505 12.7479 13.9901C13.2868 14.0297 13.8277 13.95 14.3339 13.7563C14.8401 13.5626 15.2998 13.2596 15.6817 12.8677L17.9424 10.549C18.6287 9.82019 19.0084 8.84404 18.9999 7.83081C18.9913 6.81758 18.595 5.84834 17.8965 5.13185C17.1979 4.41536 16.2529 4.00895 15.265 4.00015C14.2771 3.99134 13.3254 4.38085 12.6148 5.08478L11.3187 6.4064" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M13 10.5497C12.6764 10.106 12.2635 9.73881 11.7894 9.47313C11.3153 9.20745 10.791 9.04946 10.2521 9.00987C9.7132 8.97029 9.17231 9.05004 8.66611 9.24371C8.15991 9.43738 7.70024 9.74045 7.31828 10.1323L5.05764 12.451C4.37132 13.1798 3.99156 14.156 4.00014 15.1692C4.00873 16.1824 4.40497 17.1517 5.10354 17.8681C5.80211 18.5846 6.7471 18.991 7.73498 18.9999C8.72287 19.0087 9.6746 18.6192 10.3852 17.9152L11.6738 16.5936" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8.1582 2.67383L9.06952 5.30645" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M18.4316 17.667L16.8262 15.3902" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M5 5.28906L7.23775 6.94848" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M20.7402 14.2783L18.1301 13.3044" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/doc.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="22" height="28" viewBox="0 0 22 28" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <g clip-path="url(#clip0_1811_15061)"> 3 | <path d="M22.2271 7.38281L16.304 5.74219L14.6117 0H2.76551C1.36355 0 0.227051 1.10179 0.227051 2.46094V25.5391C0.227051 26.8982 1.36355 28 2.76551 28H19.6886C21.0906 28 22.2271 26.8982 22.2271 25.5391V7.38281Z" fill="currentColor"/> 4 | <path d="M22.227 7.38281H16.3039C15.3731 7.38281 14.6116 6.64453 14.6116 5.74219V0C14.8316 0 15.0516 0.0820312 15.2038 0.246148L21.9731 6.80865C22.1423 6.95625 22.227 7.16953 22.227 7.38281Z" fill="currentColor"/> 5 | <path d="M16.304 13.1797H6.15011C5.68241 13.1797 5.30396 12.8128 5.30396 12.3594C5.30396 11.906 5.68241 11.5391 6.15011 11.5391H16.304C16.7717 11.5391 17.1501 11.906 17.1501 12.3594C17.1501 12.8128 16.7717 13.1797 16.304 13.1797Z" fill="white"/> 6 | <path d="M16.304 16.4609H6.15011C5.68241 16.4609 5.30396 16.094 5.30396 15.6406C5.30396 15.1872 5.68241 14.8203 6.15011 14.8203H16.304C16.7717 14.8203 17.1501 15.1872 17.1501 15.6406C17.1501 16.094 16.7717 16.4609 16.304 16.4609Z" fill="white"/> 7 | <path d="M16.304 19.7422H6.15011C5.68241 19.7422 5.30396 19.3753 5.30396 18.9219C5.30396 18.4685 5.68241 18.1016 6.15011 18.1016H16.304C16.7717 18.1016 17.1501 18.4685 17.1501 18.9219C17.1501 19.3753 16.7717 19.7422 16.304 19.7422Z" fill="white"/> 8 | <path d="M12.9193 23.0234H6.15011C5.68241 23.0234 5.30396 22.6565 5.30396 22.2031C5.30396 21.7497 5.68241 21.3828 6.15011 21.3828H12.9193C13.387 21.3828 13.7655 21.7497 13.7655 22.2031C13.7655 22.6565 13.387 23.0234 12.9193 23.0234Z" fill="white"/> 9 | </g> 10 | <defs> 11 | <clipPath id="clip0_1811_15061"> 12 | <rect width="22" height="28" fill="white"/> 13 | </clipPath> 14 | </defs> 15 | </svg> 16 | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/style-parser/parseHVar.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {describe, expect, test} from "vitest"; 2 | import { parseHVar } from "../../../src/components-core/theming/hvar"; 3 | 4 | describe("Parse HVar", () => { 5 | 6 | const cases = [ 7 | { 8 | src: 'backgroundColor-Button-primary-solid--active--hover', 9 | attribute: "backgroundColor", 10 | component: "Button", 11 | traits: ["primary", "solid"], 12 | states: ["active", "hover"], 13 | }, 14 | { 15 | src: 'backgroundColor-Button--active--hover', 16 | attribute: "backgroundColor", 17 | component: "Button", 18 | states: ["active", "hover"], 19 | }, 20 | { 21 | src: "backgroundColor-Button-primary-solid", 22 | attribute: "backgroundColor", 23 | component: "Button", 24 | traits: ["primary", "solid"], 25 | }, 26 | { 27 | src: "backgroundColor-Button-primary--active", 28 | attribute: "backgroundColor", 29 | component: "Button", 30 | traits: ["primary"], 31 | states: ["active"], 32 | }, 33 | { 34 | src: "backgroundColor-Button", 35 | attribute: "backgroundColor", 36 | component: "Button", 37 | }, 38 | { 39 | src: "Input:backgroundColor-TextBox", 40 | classes: ['Input'], 41 | attribute: "backgroundColor", 42 | component: "TextBox", 43 | }, 44 | { 45 | src: "Control:Input:backgroundColor-TextBox", 46 | classes: ['Control', 'Input'], 47 | attribute: "backgroundColor", 48 | component: "TextBox", 49 | } 50 | ]; 51 | 52 | test.each(cases)('parse hvar: $src', ({src, states, traits, component, attribute, classes})=>{ 53 | const parsed = parseHVar(src)!; 54 | expect(parsed.classes).eqls(classes || []); 55 | expect(parsed.attribute).equal(attribute); 56 | expect(parsed.component).equal(component); 57 | expect(parsed.traits).eqls(traits || []); 58 | expect(parsed.states).eqls(states || []); 59 | }); 60 | 61 | }); 62 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Avatar/Avatar.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $component: "Avatar"; 11 | $themeVars: t.composeBorderVars($themeVars, $component); 12 | $backgroundColor-Avatar: createThemeVar("backgroundColor-#{$component}"); 13 | $boxShadow-Avatar: createThemeVar("boxShadow-#{$component}"); 14 | $textColor-Avatar: createThemeVar("textColor-#{$component}"); 15 | $fontWeight-Avatar: createThemeVar("fontWeight-#{$component}"); 16 | 17 | @layer components { 18 | .container { 19 | @include t.borderVars($themeVars, $component); 20 | aspect-ratio: 1 / 1; 21 | background-color: $backgroundColor-Avatar; 22 | color: $textColor-Avatar; 23 | flex-shrink: 0; 24 | object-fit: cover; 25 | display: flex; 26 | align-items: center; 27 | justify-content: center; 28 | font-weight: $fontWeight-Avatar; 29 | white-space: nowrap; 30 | user-select: none; 31 | font-size: 12px; 32 | box-shadow: $boxShadow-Avatar; 33 | 34 | &.xs { 35 | width: t.$space-8; 36 | height: t.$space-8; 37 | font-size: t.$space-3; 38 | } 39 | 40 | &.sm { 41 | width: t.$space-12; 42 | height: t.$space-12; 43 | font-size: t.$space-4; 44 | } 45 | 46 | &.md { 47 | width: t.$space-16; 48 | height: t.$space-16; 49 | font-size: t.$space-5; 50 | } 51 | 52 | &.lg { 53 | width: t.$space-24; 54 | height: t.$space-24; 55 | font-size: t.$space-8; 56 | } 57 | 58 | &.clickable { 59 | cursor: pointer; 60 | } 61 | } 62 | } 63 | 64 | 65 | // --- We export the theme variables to add them to the component renderer 66 | :export { 67 | themeVars: t.json-stringify($themeVars); 68 | } 69 | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/process-statement-asgn.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it, assert } from "vitest"; 2 | 3 | import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; 4 | import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; 5 | import { createEvalContext, parseStatements } from "./test-helpers"; 6 | 7 | describe("Process statements - assignments", () => { 8 | const asgnOps = [ 9 | "=", 10 | "+=", 11 | "-=", 12 | "**=", 13 | "*=", 14 | "/=", 15 | "%=", 16 | "<<=", 17 | ">>=", 18 | ">>>=", 19 | "&=", 20 | "^=", 21 | "|=", 22 | "&&=", 23 | "||=", 24 | "??=", 25 | ]; 26 | 27 | asgnOps.forEach((c) => { 28 | it(`cannot assign to non-defined variable (${c}) - sync`, () => { 29 | // --- Arrange 30 | const source = `dummy ${c} "do not allow this";`; 31 | const evalContext = createEvalContext({ localContext: {} }); 32 | const statements = parseStatements(source); 33 | 34 | // --- Act/Assert 35 | try { 36 | processStatementQueue(statements, evalContext); 37 | } catch (err: any) { 38 | expect(err.toString()).toContain("not found"); 39 | return; 40 | } 41 | assert.fail("Exception expected"); 42 | }); 43 | }); 44 | 45 | asgnOps.forEach((c) => { 46 | it(`cannot assign to non-defined variable (${c}) - async`, async () => { 47 | // --- Arrange 48 | const source = `dummy ${c} "do not allow this";`; 49 | const evalContext = createEvalContext({ localContext: {} }); 50 | const statements = parseStatements(source); 51 | 52 | // --- Act/Assert 53 | try { 54 | await processStatementQueueAsync(statements, evalContext); 55 | } catch (err: any) { 56 | expect(err.toString()).toContain("not found"); 57 | return; 58 | } 59 | assert.fail("Exception expected"); 60 | }); 61 | }); 62 | }); 63 | ``` -------------------------------------------------------------------------------- /xmlui/src/testing/infrastructure/public/resources/doc.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="22" height="28" viewBox="0 0 22 28" fill="none" xmlns="http://www.w3.org/2000/svg" data-testid="doc-svg"> 2 | <g clip-path="url(#clip0_1811_15061)"> 3 | <path d="M22.2271 7.38281L16.304 5.74219L14.6117 0H2.76551C1.36355 0 0.227051 1.10179 0.227051 2.46094V25.5391C0.227051 26.8982 1.36355 28 2.76551 28H19.6886C21.0906 28 22.2271 26.8982 22.2271 25.5391V7.38281Z" fill="currentColor"/> 4 | <path d="M22.227 7.38281H16.3039C15.3731 7.38281 14.6116 6.64453 14.6116 5.74219V0C14.8316 0 15.0516 0.0820312 15.2038 0.246148L21.9731 6.80865C22.1423 6.95625 22.227 7.16953 22.227 7.38281Z" fill="currentColor"/> 5 | <path d="M16.304 13.1797H6.15011C5.68241 13.1797 5.30396 12.8128 5.30396 12.3594C5.30396 11.906 5.68241 11.5391 6.15011 11.5391H16.304C16.7717 11.5391 17.1501 11.906 17.1501 12.3594C17.1501 12.8128 16.7717 13.1797 16.304 13.1797Z" fill="white"/> 6 | <path d="M16.304 16.4609H6.15011C5.68241 16.4609 5.30396 16.094 5.30396 15.6406C5.30396 15.1872 5.68241 14.8203 6.15011 14.8203H16.304C16.7717 14.8203 17.1501 15.1872 17.1501 15.6406C17.1501 16.094 16.7717 16.4609 16.304 16.4609Z" fill="white"/> 7 | <path d="M16.304 19.7422H6.15011C5.68241 19.7422 5.30396 19.3753 5.30396 18.9219C5.30396 18.4685 5.68241 18.1016 6.15011 18.1016H16.304C16.7717 18.1016 17.1501 18.4685 17.1501 18.9219C17.1501 19.3753 16.7717 19.7422 16.304 19.7422Z" fill="white"/> 8 | <path d="M12.9193 23.0234H6.15011C5.68241 23.0234 5.30396 22.6565 5.30396 22.2031C5.30396 21.7497 5.68241 21.3828 6.15011 21.3828H12.9193C13.387 21.3828 13.7655 21.7497 13.7655 22.2031C13.7655 22.6565 13.387 23.0234 12.9193 23.0234Z" fill="white"/> 9 | </g> 10 | <defs> 11 | <clipPath id="clip0_1811_15061"> 12 | <rect width="22" height="28" fill="white"/> 13 | </clipPath> 14 | </defs> 15 | </svg> 16 | ``` -------------------------------------------------------------------------------- /packages/xmlui-search/src/index.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer, createMetadata, parseScssVar } from "xmlui"; 2 | import { Search, defaultProps } from "./Search"; 3 | import styles from "./Search.module.scss"; 4 | 5 | const COMP = "Search"; 6 | const COMP_PANEL = `${COMP}Panel`; 7 | const COMP_ITEM = `${COMP}Item`; 8 | 9 | export const SearchMd = createMetadata({ 10 | description: `The \`${COMP}\` component provides a search component.`, 11 | status: "experimental", 12 | props: { 13 | data: { 14 | description: ``, 15 | }, 16 | limit: { 17 | description: ``, 18 | valueType: "number", 19 | defaultValue: defaultProps.limit, 20 | }, 21 | }, 22 | themeVars: parseScssVar(styles.themeVars), 23 | defaultThemeVars: { 24 | [`backgroundColor-${COMP_PANEL}`]: "$color-surface-0", 25 | [`borderRadius-${COMP_PANEL}`]: "8px", 26 | [`borderWidth-${COMP_PANEL}`]: "1px", 27 | [`borderStyle-${COMP_PANEL}`]: "solid", 28 | [`borderColor-${COMP_PANEL}`]: "$color-surface-200", 29 | [`boxShadow-${COMP_PANEL}`]: "$boxShadow-xl", 30 | 31 | [`backgroundColor-${COMP_ITEM}--hover`]: "$color-primary-50", 32 | [`borderColor-${COMP_ITEM}--focus`]: "$color-primary-400", 33 | [`borderRadius-${COMP_ITEM}`]: "4px", 34 | 35 | dark: { 36 | [`backgroundColor-${COMP_PANEL}`]: "$color-surface-100", 37 | [`borderColor-${COMP_PANEL}`]: "$color-surface-300", 38 | [`backgroundColor-${COMP_ITEM}--hover`]: "rgb(from $color-primary-200 r g b / 0.4)", 39 | } 40 | }, 41 | }); 42 | 43 | const searchComponentRenderer = createComponentRenderer(COMP, SearchMd, ({ node, extractValue }) => { 44 | return ( 45 | <Search 46 | data={extractValue(node.props?.data)} 47 | limit={extractValue.asOptionalNumber(node.props?.limit, defaultProps.limit)} 48 | /> 49 | ); 50 | }); 51 | 52 | export default { 53 | namespace: "XMLUIExtensions", 54 | components: [searchComponentRenderer], 55 | }; 56 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/scripts-runner/process-statement-asgn.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it, assert } from "vitest"; 2 | 3 | import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; 4 | import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; 5 | import { createEvalContext, parseStatements } from "./test-helpers"; 6 | 7 | describe("Process statements - assignments (exp)", () => { 8 | const asgnOps = [ 9 | "=", 10 | "+=", 11 | "-=", 12 | "**=", 13 | "*=", 14 | "/=", 15 | "%=", 16 | "<<=", 17 | ">>=", 18 | ">>>=", 19 | "&=", 20 | "^=", 21 | "|=", 22 | "&&=", 23 | "||=", 24 | "??=", 25 | ]; 26 | 27 | asgnOps.forEach((c) => { 28 | it(`cannot assign to non-defined variable (${c}) - sync`, () => { 29 | // --- Arrange 30 | const source = `dummy ${c} "do not allow this";`; 31 | const evalContext = createEvalContext({ localContext: {} }); 32 | const statements = parseStatements(source); 33 | 34 | // --- Act/Assert 35 | try { 36 | processStatementQueue(statements, evalContext); 37 | } catch (err: any) { 38 | expect(err.toString()).toContain("not found"); 39 | return; 40 | } 41 | assert.fail("Exception expected"); 42 | }); 43 | }); 44 | 45 | asgnOps.forEach((c) => { 46 | it(`cannot assign to non-defined variable (${c}) - async`, async () => { 47 | // --- Arrange 48 | const source = `dummy ${c} "do not allow this";`; 49 | const evalContext = createEvalContext({ localContext: {} }); 50 | const statements = parseStatements(source); 51 | 52 | // --- Act/Assert 53 | try { 54 | await processStatementQueueAsync(statements, evalContext); 55 | } catch (err: any) { 56 | expect(err.toString()).toContain("not found"); 57 | return; 58 | } 59 | assert.fail("Exception expected"); 60 | }); 61 | }); 62 | }); 63 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Column/ColumnNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { useCallback, useId, useLayoutEffect, useMemo } from "react"; 2 | 3 | import type { ComponentDef } from "../../abstractions/ComponentDefs"; 4 | import type { RenderChildFn } from "../../abstractions/RendererDefs"; 5 | import { MemoizedItem } from "../../components/container-helpers"; 6 | import { useTableContext } from "./TableContext"; 7 | import type { OurColumnMetadata } from "./TableContext"; 8 | 9 | type Props = OurColumnMetadata & { 10 | nodeChildren?: ComponentDef[]; 11 | renderChild: RenderChildFn; 12 | }; 13 | 14 | export const defaultProps: Pick<Props, "canSort" | "canResize"> = { 15 | canSort: true, 16 | canResize: true, 17 | }; 18 | 19 | export function Column({ nodeChildren, renderChild, ...columnMetadata }: Props) { 20 | const id = useId(); 21 | const { registerColumn, unRegisterColumn } = useTableContext(); 22 | 23 | const cellRenderer = useCallback( 24 | (row: any, rowIndex: number, colIndex: number, value: any) => { 25 | return ( 26 | <MemoizedItem 27 | node={nodeChildren!} 28 | item={row} 29 | contextVars={{ 30 | $rowIndex: rowIndex, 31 | $colIndex: colIndex, 32 | $row: row, 33 | $itemIndex: rowIndex, 34 | $cell: value, 35 | }} 36 | renderChild={renderChild} 37 | /> 38 | ); 39 | }, 40 | [nodeChildren, renderChild], 41 | ); 42 | 43 | const safeCellRenderer = useMemo(() => { 44 | return nodeChildren ? cellRenderer : undefined; 45 | }, [cellRenderer, nodeChildren]); 46 | 47 | useLayoutEffect(() => { 48 | registerColumn( 49 | { 50 | ...columnMetadata, 51 | cellRenderer: safeCellRenderer, 52 | }, 53 | id, 54 | ); 55 | }, [columnMetadata, id, registerColumn, safeCellRenderer]); 56 | 57 | useLayoutEffect(() => { 58 | return () => { 59 | unRegisterColumn(id); 60 | }; 61 | }, [id, unRegisterColumn]); 62 | return null; 63 | } 64 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/test-metadata-handler.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ComponentMetadata } from "../../src/abstractions/ComponentDefs"; 2 | import { MetadataHandler, PropDescriptorHash } from "../../src/components-core/markup-check"; 3 | 4 | export function createTestMetadataHandler( 5 | desc: Record<string, ComponentMetadata>, 6 | ): MetadataHandler { 7 | return { 8 | componentRegistered: (type: string) => { 9 | return !!desc[type]; 10 | }, 11 | getComponentProps: (type: string) => { 12 | const compDesc = desc[type]; 13 | if (!compDesc) { 14 | return {}; 15 | } 16 | const propsToMap = compDesc.props ?? {}; 17 | const mappedProps: PropDescriptorHash = {}; 18 | Object.keys(propsToMap).forEach((key) => { 19 | const prop = propsToMap[key]!; 20 | mappedProps[key] = { 21 | type: prop.valueType, 22 | availableValues: prop.availableValues, 23 | defaultValue: prop.defaultValue, 24 | }; 25 | }); 26 | return mappedProps; 27 | }, 28 | getComponentEvents(componentName) { 29 | const compDesc = desc[componentName]; 30 | if (!compDesc) { 31 | return {}; 32 | } 33 | const eventsToMap = compDesc.events ?? {}; 34 | const mappedEvents: Record<string, any> = {}; 35 | Object.keys(eventsToMap).forEach((key) => { 36 | mappedEvents[key] = eventsToMap[key]; 37 | }); 38 | return mappedEvents; 39 | }, 40 | acceptArbitraryProps: (type: string) => { 41 | const compDesc = desc[type]; 42 | return compDesc?.allowArbitraryProps ?? false; 43 | }, 44 | getComponentValidator: (type: string) => { 45 | if (type === "Button") { 46 | return (instance, devMode) => { 47 | if (devMode && (instance.props as any)?.label?.startsWith("q")) { 48 | return "Label should not start with 'q'"; 49 | } 50 | return null; 51 | }; 52 | } 53 | }, 54 | }; 55 | } 56 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/StandaloneExtensionManager.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { Extension } from "../abstractions/ExtensionDefs"; 2 | 3 | type ExtensionRegisteredCallbackFn = (extension: Extension) => void; 4 | 5 | /** 6 | * This class allows external component libraries to add their components to 7 | * the xmlui component registry. The framework resolves the components used 8 | * in an application markup with this registry. 9 | */ 10 | export default class StandaloneExtensionManager { 11 | subscriptions: Set<ExtensionRegisteredCallbackFn> = new Set(); 12 | registeredExtensions: Array<Extension> = []; 13 | 14 | constructor() {} 15 | 16 | /** 17 | * You can add a callback function invoked whenever a new component is added 18 | * to the registry. When you register a new callback function, the component 19 | * manager automatically invokes it for all components already in the 20 | * registry. 21 | * @param cb Function to call when a new component is registered 22 | */ 23 | subscribeToRegistrations(cb: ExtensionRegisteredCallbackFn) { 24 | this.subscriptions.add(cb); 25 | this.registeredExtensions.forEach((component) => { 26 | cb(component); 27 | }); 28 | } 29 | 30 | /** 31 | * You can remove a function added by `subscribeToRegistrations`. After 32 | * calling this method, the particular callback function won't be invoked 33 | * for a new component registration. 34 | * @param cb Function to call when a new component is registered 35 | */ 36 | unSubscribeFromRegistrations(cb: ExtensionRegisteredCallbackFn) { 37 | this.subscriptions.delete(cb); 38 | } 39 | 40 | registerExtension(component: Extension | Extension[]) { 41 | (Array.isArray(component) ? component : [component]).forEach((component) => { 42 | this.registeredExtensions.push(component); 43 | this.subscriptions.forEach((cb: ExtensionRegisteredCallbackFn) => { 44 | cb(component); 45 | }); 46 | }); 47 | } 48 | } 49 | ``` -------------------------------------------------------------------------------- /docs/public/resources/files/howto/component-icons/up-arrow.svg: -------------------------------------------------------------------------------- ``` 1 | <?xml version="1.0" encoding="iso-8859-1"?> 2 | <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> 3 | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 4 | <svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 5 | width="800px" height="800px" viewBox="0 0 429.658 429.658" 6 | xml:space="preserve"> 7 | <g> 8 | <g> 9 | <path d="M235.252,13.406l-0.447-0.998c-3.417-7.622-11.603-12.854-19.677-12.375l-0.3,0.016l-0.302-0.016 10 | C214.194,0.011,213.856,0,213.524,0c-7.706,0-15.386,5.104-18.674,12.413l-0.452,0.998L13.662,176.079 11 | c-6.871,6.183-6.495,12.657-4.971,16.999c2.661,7.559,10.361,13.373,18.313,13.82l1.592,0.297c0.68,0.168,1.356,0.348,2.095,0.427 12 | c23.036,2.381,45.519,2.876,64.472,3.042l5.154,0.048V407.93c0,11.023,7.221,15.152,11.522,16.635l0.967,0.33l0.77,0.671 13 | c3.105,2.717,7.02,4.093,11.644,4.093h179.215c4.626,0,8.541-1.376,11.639-4.093l0.771-0.671l0.965-0.33 14 | c4.307-1.482,11.532-5.611,11.532-16.635V210.706l5.149-0.048c18.961-0.17,41.446-0.666,64.475-3.042 15 | c0.731-0.079,1.407-0.254,2.082-0.422l1.604-0.302c7.952-0.447,15.65-6.262,18.312-13.82c1.528-4.336,1.899-10.811-4.972-16.998 16 | L235.252,13.406z M344.114,173.365c-11.105,0.18-22.216,0.254-33.337,0.254c-5.153,0-9.363,1.607-12.507,4.768 17 | c-3.372,3.4-5.296,8.48-5.266,13.932l0.005,0.65l-0.157,0.629c-0.437,1.767-0.64,3.336-0.64,4.928v194.001H137.458V198.526 18 | c0-1.597-0.201-3.161-0.638-4.928l-0.157-0.629l0.005-0.65c0.031-5.456-1.892-10.537-5.271-13.937 19 | c-3.141-3.161-7.353-4.763-12.507-4.768c-11.124,0-22.224-0.074-33.337-0.254l-13.223-0.218L214.834,44.897l142.503,128.249 20 | L344.114,173.365z"/> 21 | </g> 22 | </g> 23 | </svg> ``` -------------------------------------------------------------------------------- /xmlui/src/components/SlotItem.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { memo, useMemo } from "react"; 2 | 3 | import type { ComponentDef } from "../abstractions/ComponentDefs"; 4 | import type { ContainerWrapperDef } from "../components-core/rendering/ContainerWrapper"; 5 | import { useShallowCompareMemoize } from "../components-core/utils/hooks"; 6 | import { EMPTY_OBJECT } from "../components-core/constants"; 7 | import type { LayoutContext, RenderChildFn } from "../abstractions/RendererDefs"; 8 | 9 | type SlotItemProps = { 10 | node: ComponentDef | Array<ComponentDef>; 11 | slotProps?: any; 12 | renderChild: RenderChildFn; 13 | layoutContext?: LayoutContext; 14 | }; 15 | 16 | /** 17 | * This React component wraps the slot content defined in a parent component into a container. 18 | * This container may contain context values pushed from a compound component back to 19 | * the parent. 20 | */ 21 | export const SlotItem = memo( 22 | ({ node, renderChild, layoutContext, slotProps = EMPTY_OBJECT }: SlotItemProps) => { 23 | const shallowMemoedSlotProps = useShallowCompareMemoize(slotProps); 24 | 25 | // --- Transform all Slot properties into context values so that they can be 26 | // --- used in the slot content (in the parent component) 27 | const nodeWithItem = useMemo(() => { 28 | const templateProps = {}; 29 | Object.entries(shallowMemoedSlotProps).forEach(([key, value]) => { 30 | templateProps["$" + key] = value; 31 | }); 32 | 33 | // --- Create a container for the slot content with the cotext values 34 | return { 35 | type: "Container", 36 | contextVars: templateProps, 37 | children: Array.isArray(node) ? node : [node], 38 | } as ContainerWrapperDef; 39 | }, [node, shallowMemoedSlotProps]); 40 | 41 | // --- Render the slot content 42 | return <>{renderChild(nodeWithItem, layoutContext)}</>; 43 | }, 44 | ); 45 | SlotItem.displayName = "SlotItem"; 46 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileUploadDropZone/FileUploadDropZone.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | ## Using `FileUploadDropZone` 4 | 5 | The component provides a surface on which you can drag files or paste files from the clipboard. The following example demonstrates how to use the component. 6 | 7 | ```xmlui-pg copy display name="Example: using FileUploadDropZone" height="200px" 8 | ---app copy display 9 | <App> 10 | <H3>The cyan area below is a FileUploadDropZone</H3> 11 | <FileUploadDropZone backgroundColor="cyan" height="100px" 12 | onUpload=" 13 | (files) => { 14 | console.log(files); 15 | files.map(file => toast('file ' + file.path + ' uploaded'))}" /> 16 | </App> 17 | ---desc 18 | You can try it by dragging one or more files to the cyan surface. When you drop the file(s), the app triggers the `upload` event and displays a dialog for each file. 19 | 20 | You can also paste files from the clipboard: click the drop zone (cyan area) and then use the keyboard shortcut set on your OS. 21 | ``` 22 | 23 | %-DESC-END 24 | 25 | %-PROP-START allowPaste 26 | 27 | This property indicates if the drop zone accepts files pasted from the clipboard (`true`) or only dragged files (`false`). 28 | 29 | The following example sets this property to `false` and, thus, it turns off pasting files: 30 | 31 | ```xmlui-pg copy display name="Example: allowPaste" height="200px" 32 | ---app copy display 33 | <App> 34 | <H3>You cannot paste files from the clipboard</H3> 35 | <FileUploadDropZone backgroundColor="cyan" height="100px" 36 | allowPaste="false" 37 | onUpload="(files) => files.map(file => toast('file ' + file.path + ' uploaded'))" /> 38 | </App> 39 | ---desc 40 | Try it! When you copy a file to a clipboard, you cannot paste it with the keyboard shortcut of your OS. 41 | ``` 42 | 43 | %-PROP-END 44 | 45 | %-EVENT-START upload 46 | 47 | Each item passed in the event argument is an instance of [File](https://developer.mozilla.org/en-US/docs/Web/API/File). 48 | 49 | %-EVENT-END 50 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Backdrop/Backdrop.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./Backdrop.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { createMetadata, dComponent } from "../../components/metadata-helpers"; 6 | import { Backdrop } from "./BackdropNative"; 7 | 8 | const COMP = "Backdrop"; 9 | 10 | // See reference implementation here: https://getbootstrap.com/docs/5.3/components/alerts/ 11 | 12 | export const BackdropMd = createMetadata({ 13 | status: "stable", 14 | description: 15 | `The \`${COMP}\` component is a semi-transparent overlay that appears on ` + 16 | `top of its child component to obscure or highlight the content behind it.`, 17 | props: { 18 | overlayTemplate: dComponent( 19 | "This property defines the component template for an optional overlay to display " + 20 | "over the component.", 21 | ), 22 | backgroundColor: { 23 | description: "The background color of the backdrop.", 24 | valueType: "string", 25 | }, 26 | opacity: { 27 | description: "The opacity of the backdrop.", 28 | valueType: "string", 29 | }, 30 | }, 31 | themeVars: parseScssVar(styles.themeVars), 32 | defaultThemeVars: { 33 | [`backgroundColor-${COMP}`]: "black", 34 | [`opacity-${COMP}`]: "0.1", 35 | } 36 | }); 37 | 38 | export const backdropComponentRenderer = createComponentRenderer( 39 | COMP, 40 | BackdropMd, 41 | ({ node, extractValue, renderChild, className }) => { 42 | return ( 43 | <Backdrop 44 | className={className} 45 | overlayTemplate={renderChild(node.props?.overlayTemplate)} 46 | backgroundColor={extractValue.asOptionalString(node.props.backgroundColor, undefined)} 47 | opacity={extractValue.asOptionalString(node.props.opacity, undefined)} 48 | > 49 | {renderChild(node.children)} 50 | </Backdrop> 51 | ); 52 | }, 53 | ); 54 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/FormSection/FormSection.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createUserDefinedComponentRenderer } from "../../components-core/renderers"; 2 | import { createMetadata } from "../metadata-helpers"; 3 | import componentSource from "./FormSection.xmlui"; 4 | 5 | const COMP = "FormSection"; 6 | 7 | export const FormSectionMd = createMetadata({ 8 | status: "experimental", 9 | description: 10 | "`FormSection` groups elements within a `Form`. Child components are placed in " + 11 | "a [FlowLayout](/components/FlowLayout).", 12 | props: { 13 | heading: { 14 | description: "The heading text to be displayed at the top of the form section.", 15 | type: "string", 16 | }, 17 | headingLevel: { 18 | description: "The semantic and visual level of the heading.", 19 | availableValues: ["h1", "h2", "h3", "h4", "h5", "h6"], 20 | defaultValue: "h3", 21 | }, 22 | headingWeight: { 23 | description: "The font weight of the heading.", 24 | type: "string", 25 | defaultValue: "bold", 26 | }, 27 | info: { 28 | description: "Informational text displayed below the heading.", 29 | type: "string", 30 | }, 31 | infoFontSize: { 32 | description: "The font size of the informational text.", 33 | type: "string", 34 | defaultValue: "0.8rem", 35 | }, 36 | paddingTop: { 37 | description: "The top padding of the FlowLayout where the section's children are placed.", 38 | type: "string", 39 | defaultValue: "$space-normal", 40 | }, 41 | columnGap: { 42 | description: "The gap between columns of items within the section.", 43 | type: "string", 44 | defaultValue: "3rem", 45 | }, 46 | rowGap: { 47 | description: "The gap between rows of items within the section.", 48 | type: "string", 49 | defaultValue: "$space-normal", 50 | }, 51 | }, 52 | }); 53 | 54 | export const formSectionRenderer = createUserDefinedComponentRenderer( 55 | FormSectionMd, 56 | componentSource, 57 | ); 58 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/abstractions/LoaderRenderer.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { ReactNode } from "react"; 2 | 3 | import type { LookupAsyncFn, LookupSyncFn } from "../../abstractions/ActionDefs"; 4 | import type { ComponentDef, ComponentMetadata } from "../../abstractions/ComponentDefs"; 5 | import type { RegisterComponentApiFn, ValueExtractor } from "../../abstractions/RendererDefs"; 6 | import type { ContainerState } from "../rendering/ContainerWrapper"; 7 | import type { ContainerDispatcher } from "./ComponentRenderer"; 8 | 9 | // This function renders a loader definition into a React component 10 | export type LoaderRenderer<TMd extends ComponentMetadata> = ( 11 | context: RendererContext<TMd>, 12 | ) => ReactNode; 13 | 14 | // Defines the traits of a loader renderer 15 | export interface LoaderRendererDef { 16 | // The loader's type identifier 17 | type: string; 18 | 19 | // The renderer function of the loader 20 | renderer: LoaderRenderer<any>; 21 | 22 | // Loader descriptor 23 | hints?: ComponentMetadata; 24 | } 25 | 26 | export type LoaderInProgressChangedFn = (isInProgress: boolean) => void; 27 | export type LoaderLoadedFn = (data: any, pageInfo?: any) => void; 28 | export type LoaderErrorFn = (error: any) => void; 29 | export type TransformResultFn = (data: any) => any; 30 | 31 | // The context in which a particular component is rendered 32 | type RendererContext<TMd extends ComponentMetadata> = { 33 | // The definition of the loader 34 | loader: ComponentDef<TMd>; 35 | 36 | // Loader state 37 | state: ContainerState; 38 | 39 | // Dispatcher function to change the state of the component 40 | dispatch: ContainerDispatcher; 41 | 42 | registerComponentApi: RegisterComponentApiFn; 43 | extractValue: ValueExtractor; 44 | lookupAction: LookupAsyncFn; 45 | lookupSyncCallback: LookupSyncFn; 46 | 47 | loaderInProgressChanged: LoaderInProgressChangedFn; 48 | loaderIsRefetchingChanged: LoaderInProgressChangedFn; 49 | loaderLoaded: LoaderLoadedFn; 50 | loaderError: LoaderErrorFn; 51 | }; 52 | ``` -------------------------------------------------------------------------------- /packages/xmlui-playground/src/playground/Playground.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer, createMetadata, parseScssVar } from "xmlui"; 2 | import { Playground } from "./PlaygroundNative"; 3 | 4 | const COMP = "Playground"; 5 | 6 | export const PlaygroundMd = createMetadata({ 7 | description: "XMLUI Playground component.", 8 | status: "experimental", 9 | props: { 10 | app: { 11 | description: "", 12 | }, 13 | name: { 14 | description: "The name of the component to be rendered.", 15 | }, 16 | api: { 17 | description: "", 18 | }, 19 | description: { 20 | description: "The description of the component to be rendered.", 21 | }, 22 | previewOnly: { 23 | description: "If true, the component will be rendered in preview mode only.", 24 | }, 25 | }, 26 | themeVars: parseScssVar({}), 27 | defaultThemeVars: {}, 28 | }); 29 | 30 | export const playgroundComponentRenderer = createComponentRenderer( 31 | COMP, 32 | PlaygroundMd, 33 | ({ extractValue, node }: any) => { 34 | return ( 35 | <Playground 36 | height={extractValue(node.props.height)} 37 | initialEditorHeight={extractValue.asOptionalString(node.props.initialEditorHeight)} 38 | swapped={extractValue.asOptionalBoolean(node.props.swapped)} 39 | horizontal={extractValue.asOptionalBoolean(node.props.horizontal)} 40 | allowStandalone={extractValue.asOptionalBoolean(node.props.allowStandalone)} 41 | fixedTheme={extractValue.asOptionalBoolean(node.props.fixedTheme)} 42 | themes={extractValue(node.props.themes)} 43 | previewOnly={extractValue.asBoolean(node.props.previewOnly)} 44 | components={extractValue(node.props.components)} 45 | app={extractValue.asOptionalString(node.props.app)} 46 | name={extractValue.asOptionalString(node.props.name)} 47 | api={extractValue(node.props.api)} 48 | description={extractValue.asOptionalString(node.props.description)} 49 | /> 50 | ); 51 | }, 52 | ); 53 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ValidationSummary/ValidationSummary.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | @mixin validationDisplayVariant($variantName) { 11 | &.#{$variantName} { 12 | background-color: createThemeVar("backgroundColor-ValidationDisplay-#{$variantName}"); 13 | 14 | &::before { 15 | background-color: createThemeVar("color-accent-ValidationDisplay-#{$variantName}"); 16 | } 17 | 18 | .heading { 19 | color: createThemeVar("textColor-ValidationDisplay-#{$variantName}"); 20 | font-weight: bold; 21 | } 22 | } 23 | } 24 | 25 | @layer components { 26 | .summaryContainer { 27 | display: flex; 28 | flex-direction: column; 29 | gap: 0.5rem; 30 | 31 | &:empty{ 32 | display: none; 33 | } 34 | } 35 | 36 | .validationContainer { 37 | position: relative; 38 | padding-top: 0; 39 | padding-bottom: 0.5rem; 40 | padding-left: 1rem; 41 | 42 | &::before { 43 | position: absolute; 44 | top: 0; 45 | left: 0; 46 | display: block; 47 | content: ""; 48 | height: 100%; 49 | width: 2px; 50 | } 51 | 52 | ul { 53 | list-style-position: inside; 54 | padding-left: 0; 55 | } 56 | 57 | li { 58 | padding-left: 2rem; 59 | 60 | &::marker { 61 | font-size: 0.625em; 62 | } 63 | } 64 | .noMarker { 65 | list-style: none; 66 | padding-left: 1.6rem; 67 | line-height: 1.6rem; 68 | } 69 | 70 | @include validationDisplayVariant("error"); 71 | @include validationDisplayVariant("warning"); 72 | @include validationDisplayVariant("info"); 73 | @include validationDisplayVariant("valid"); 74 | } 75 | } 76 | 77 | // --- We export the theme variables to add them to the component renderer 78 | :export { 79 | themeVars: t.json-stringify($themeVars); 80 | } 81 | ``` -------------------------------------------------------------------------------- /docs/public/pages/howto/delay-a-datasource-until-another-datasource-is-ready.md: -------------------------------------------------------------------------------- ```markdown 1 | # Delay a DataSource until another DataSource is ready 2 | 3 | ```xmlui-pg noHeader 4 | ---app 5 | <App> 6 | <Test /> 7 | </App> 8 | ---api 9 | { 10 | "apiUrl": "/api", 11 | "initialize": "$state.users_for_ds_dependency = 12 | [ 13 | { id: 1, name: 'Alice', departmentId: 1 }, 14 | { id: 2, name: 'Bob', departmentId: 2 } 15 | ]; 16 | $state.departments_with_ds_dependency = [ 17 | { id: 1, name: 'Engineering' }, 18 | { id: 2, name: 'Marketing' } 19 | ]", 20 | "operations": { 21 | "get_users_for_ds_dependency": { 22 | "url": "/users_for_ds_dependency", 23 | "method": "get", 24 | "handler": "delay(1000); return $state.users_for_ds_dependency" 25 | }, 26 | "get_departments_with_ds_dependency": { 27 | "url": "/departments_with_ds_dependency", 28 | "method": "get", 29 | "handler": "delay(1000); return $state.departments_with_ds_dependency" 30 | } 31 | } 32 | } 33 | ---comp display 34 | <Component name="Test" var.selectedId="" var.nonce="{0}"> 35 | 36 | <DataSource 37 | id="users_for_ds_dependency" 38 | url="/api/users_for_ds_dependency?nonce" 39 | inProgressNotificationMessage="Loading users..." 40 | when="{ nonce > 0 }" 41 | /> 42 | 43 | <DataSource 44 | id="departments_with_ds_dependency" 45 | url="/api/departments_with_ds_dependency" 46 | when="{ users_for_ds_dependency.loaded }" 47 | inProgressNotificationMessage="Loading departments..." 48 | /> 49 | 50 | <Select 51 | id="usersForDsDepencency" 52 | data="{users_for_ds_dependency}" 53 | when="{departments_with_ds_dependency.loaded}" 54 | onDidChange="(newVal) => selectedId = newVal" 55 | > 56 | <Items data="{users_for_ds_dependency}"> 57 | <Option 58 | value="{$item.id}" 59 | label="{$item.name} ({departments_with_ds_dependency.value.find(d => d.id === $item.departmentId)?.name})" 60 | /> 61 | </Items> 62 | </Select> 63 | 64 | <Button 65 | label="Run" 66 | onClick="{nonce++}" 67 | /> 68 | 69 | 70 | </Component> 71 | ``` 72 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Icon/svg/inspect.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <g filter="url(#filter0_d_8732_12671)"> 3 | <rect x="10" y="10" width="36" height="36" rx="6" fill="#143566" shape-rendering="crispEdges"/> 4 | <path d="M28.834 18.6667H23.0007C22.5586 18.6667 22.1347 18.8423 21.8221 19.1549C21.5096 19.4675 21.334 19.8914 21.334 20.3334V33.6667C21.334 34.1088 21.5096 34.5327 21.8221 34.8453C22.1347 35.1578 22.5586 35.3334 23.0007 35.3334H33.0007C33.4427 35.3334 33.8666 35.1578 34.1792 34.8453C34.4917 34.5327 34.6673 34.1088 34.6673 33.6667V24.5001L28.834 18.6667Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 5 | <path d="M28.834 18.6667V24.5001H34.6673" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 6 | <path d="M29.666 30.3333L31.3327 28.6667L29.666 27" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 7 | <path d="M26.334 26.9999L24.6673 28.6666L26.334 30.3333" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 8 | </g> 9 | <defs> 10 | <filter id="filter0_d_8732_12671" x="0" y="0" width="56" height="56" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> 11 | <feFlood flood-opacity="0" result="BackgroundImageFix"/> 12 | <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> 13 | <feOffset/> 14 | <feGaussianBlur stdDeviation="5"/> 15 | <feComposite in2="hardAlpha" operator="out"/> 16 | <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/> 17 | <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_8732_12671"/> 18 | <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_8732_12671" result="shape"/> 19 | </filter> 20 | </defs> 21 | </svg> 22 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/container-helpers.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { memo, useMemo } from "react"; 2 | 3 | import type { ComponentDef } from "../abstractions/ComponentDefs"; 4 | import type { LayoutContext, RenderChildFn } from "../abstractions/RendererDefs"; 5 | import type { ContainerWrapperDef } from "../components-core/rendering/ContainerWrapper"; 6 | import { EMPTY_OBJECT } from "../components-core/constants"; 7 | import { useShallowCompareMemoize } from "../components-core/utils/hooks"; 8 | import { rest } from "lodash-es"; 9 | 10 | type MemoizedItemProps = { 11 | node: ComponentDef | Array<ComponentDef>; 12 | item?: any; 13 | context?: any; 14 | renderChild: RenderChildFn; 15 | layoutContext?: LayoutContext; 16 | contextVars?: Record<string, any>; 17 | itemKey?: string; 18 | contextKey?: string; 19 | }; 20 | 21 | export const MemoizedItem = memo( 22 | ({ 23 | node, 24 | item, 25 | context, 26 | renderChild, 27 | layoutContext, 28 | contextVars = EMPTY_OBJECT, 29 | itemKey = "$item", 30 | contextKey = "$context", 31 | }: MemoizedItemProps) => { 32 | const shallowMemoedContextVars = useShallowCompareMemoize(contextVars); 33 | const nodeWithItem = useMemo(() => { 34 | if (itemKey === contextKey) { 35 | return { 36 | type: "Container", 37 | contextVars: { 38 | [itemKey]: { ...item, ...context }, 39 | ...shallowMemoedContextVars, 40 | }, 41 | children: Array.isArray(node) ? node : [node], 42 | } as ContainerWrapperDef; 43 | } 44 | return { 45 | type: "Container", 46 | contextVars: { 47 | [itemKey]: item, 48 | [contextKey]: context, 49 | ...shallowMemoedContextVars, 50 | }, 51 | children: Array.isArray(node) ? node : [node], 52 | } as ContainerWrapperDef; 53 | }, [context, item, node, shallowMemoedContextVars, itemKey, contextKey]); 54 | 55 | return <>{renderChild(nodeWithItem, layoutContext)}</>; 56 | }, 57 | ); 58 | MemoizedItem.displayName = "MemoizedItem"; 59 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ColorPicker/ColorPicker.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | ## Using `ColorPicker` 4 | 5 | This component allows you to edit or select a color using RGB, HSL, or CSS HEX notation. It displays a popup over the UI and lets you use the mouse or keyboard to edit or select a color. 6 | 7 | ```xmlui-pg copy display name="Example: using ColorPicker" 8 | <App> 9 | <ColorPicker id="colorPicker" label="Select your favorite color" /> 10 | <Text>Selected color: {colorPicker.value}</Text> 11 | </App> 12 | ``` 13 | 14 | %-DESC-END 15 | 16 | %-PROP-START initialValue 17 | 18 | ```xmlui-pg copy display name="Example: using ColorPicker" 19 | <App> 20 | <ColorPicker 21 | id="colorPicker" 22 | label="Select your favorite color" 23 | initialValue="#ff0080" 24 | /> 25 | <Text>Selected color: {colorPicker.value}</Text> 26 | </App> 27 | ``` 28 | 29 | %-PROP-END 30 | 31 | %-PROP-START validationStatus 32 | 33 | ```xmlui-pg copy display name="Example: validationStatus" 34 | <App> 35 | <ColorPicker initialValue="#c0c0ff" label="Valid" validationStatus="valid" /> 36 | <ColorPicker initialValue="#c0c0ff" label="Warning" validationStatus="warning" /> 37 | <ColorPicker initialValue="#c0c0ff" label="Error" validationStatus="error" /> 38 | </App> 39 | ``` 40 | 41 | %-PROP-END 42 | 43 | %-PROP-START readOnly 44 | 45 | ```xmlui-pg copy display name="Example: readOnly" 46 | <App> 47 | <ColorPicker initialValue="#ffff00" label="Cannot be edited" readOnly /> 48 | </App> 49 | ``` 50 | 51 | %-PROP-END 52 | 53 | %-API-START setValue 54 | 55 | ```xmlui-pg copy display name="Example: setValue" 56 | <App> 57 | <App> 58 | <ColorPicker 59 | id="colorPicker" 60 | label="Select your favorite color" 61 | initialValue="#808080" /> 62 | <HStack> 63 | <Button 64 | label="Set to red" 65 | onClick="colorPicker.setValue('#ff0000')" /> 66 | <Button 67 | label="Set to green" 68 | onClick="colorPicker.setValue('#00c000')" /> 69 | <Button 70 | label="Set to blue" 71 | onClick="colorPicker.setValue('#0000ff')" /> 72 | </HStack> 73 | </App> 74 | </App> 75 | ``` 76 | 77 | %-API-END 78 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/FileInput/FileInput.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | // --- The FileInput component uses a TextBox and a Button. We do not allow separately customize the 11 | // --- FileInput theme, only through the TextBox and Button themes. 12 | 13 | @layer components { 14 | .container { 15 | gap: t.$space-2; 16 | width: 100%; 17 | display: flex; 18 | flex-direction: row; 19 | align-items: center; 20 | justify-content: flex-start; 21 | } 22 | 23 | .buttonStart { 24 | flex-direction: row-reverse; 25 | } 26 | 27 | .buttonEnd { 28 | flex-direction: row; 29 | } 30 | 31 | .textBoxWrapper { 32 | width: 100%; 33 | border-radius: t.$borderRadius; 34 | min-height: fit-content; 35 | 36 | &:focus-within { 37 | border-radius: createThemeVar("Input:borderRadius-FileInput--focus"); 38 | border-color: createThemeVar("Input:borderColor-FileInput--focus"); 39 | background-color: createThemeVar("Input:backgroundColor-FileInput--focus"); 40 | box-shadow: createThemeVar("Input:boxShadow-FileInput--focus"); 41 | color: createThemeVar("Input:textColor-FileInput--focus"); 42 | } 43 | 44 | &:has(.input:focus-visible) { 45 | outline-width: createThemeVar("Input:outlineWidth-FileInput--focus"); 46 | outline-color: createThemeVar("Input:outlineColor-FileInput--focus"); 47 | outline-style: createThemeVar("Input:outlineStyle-FileInput--focus"); 48 | outline-offset: createThemeVar("Input:outlineOffset-FileInput--focus"); 49 | } 50 | } 51 | 52 | .button { 53 | flex-shrink: 0; 54 | } 55 | } 56 | 57 | // --- We export the theme variables to add them to the component renderer 58 | :export { 59 | themeVars: t.json-stringify($themeVars); 60 | } 61 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Footer/Footer.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | // --- Theme vars for paddings 11 | $component: "Footer"; 12 | $themeVars: t.composePaddingVars($themeVars, $component); 13 | $themeVars: t.composeBorderVars($themeVars, $component) !global; 14 | 15 | // --- Other theme vars 16 | $backgroundColor-Footer: createThemeVar("backgroundColor-#{$component}"); 17 | $textColor-Footer: createThemeVar("textColor-#{$component}"); 18 | $height-Footer: createThemeVar("height-#{$component}"); 19 | $fontSize-Footer: createThemeVar("fontSize-#{$component}"); 20 | $verticalAlignment-Footer: createThemeVar("verticalAlignment-#{$component}"); 21 | $maxWidth-content-Footer: createThemeVar("maxWidth-content-#{$component}"); 22 | $gap-Footer: createThemeVar("gap-#{$component}"); 23 | $maxWidth-App: createThemeVar("maxWidth-App"); 24 | $margin-Footer: createThemeVar("margin-#{$component}"); 25 | 26 | 27 | @layer components { 28 | .outerWrapper{ 29 | @include t.borderVars($themeVars, $component); 30 | background-color: $backgroundColor-Footer; 31 | color: $textColor-Footer; 32 | height: $height-Footer; 33 | } 34 | 35 | .wrapper { 36 | width: 100%; 37 | display: flex; 38 | min-height: 0; 39 | height: 100%; 40 | flex-direction: row; 41 | gap: $gap-Footer; 42 | @include t.paddingVars($themeVars, $component); 43 | font-size: $fontSize-Footer; 44 | align-items: $verticalAlignment-Footer; 45 | max-width: $maxWidth-content-Footer; 46 | margin: $margin-Footer; 47 | 48 | &.full{ 49 | max-width: $maxWidth-App; 50 | } 51 | } 52 | } 53 | 54 | // --- We export the theme variables to add them to the component renderer 55 | :export{ 56 | themeVars: t.json-stringify($themeVars) 57 | } 58 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/RadioGroup/RadioItemNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type React from "react"; 2 | import { useCallback, useId } from "react"; 3 | import styles from "./RadioGroup.module.scss"; 4 | import * as InnerRadioGroup from "@radix-ui/react-radio-group"; 5 | import { noop } from "../../components-core/constants"; 6 | 7 | import classnames from "classnames"; 8 | import { convertOptionValue } from "../Option/OptionNative"; 9 | 10 | export const defaultProps = { 11 | checked: false, 12 | value: "", 13 | }; 14 | 15 | type RadioItemProps = { 16 | checked: boolean; 17 | style?: React.CSSProperties; 18 | value?: string; 19 | onDidChange?: (value: string) => void; 20 | }; 21 | 22 | export const RadioItem = ({ 23 | checked = defaultProps.checked, 24 | style, 25 | value = defaultProps.value, 26 | onDidChange = noop, 27 | }: RadioItemProps) => { 28 | const id = useId(); 29 | return ( 30 | <div key={id} className={styles.radioOptionContainer} style={style}> 31 | <UnwrappedRadioItem id={id} checked={checked} value={value} onDidChange={onDidChange} /> 32 | </div> 33 | ); 34 | }; 35 | 36 | type UnwrappedRadioItemProps = Omit<RadioItemProps, "style"> & { 37 | id: string; 38 | statusStyles?: Record<string, boolean>; 39 | disabled?: boolean; 40 | onDidChange?: (value: string) => void; 41 | }; 42 | 43 | export const UnwrappedRadioItem = ({ 44 | id, 45 | checked = defaultProps.checked, 46 | value = defaultProps.value, 47 | statusStyles, 48 | disabled, 49 | onDidChange = noop, 50 | }: UnwrappedRadioItemProps) => { 51 | const onInputChange = useCallback( 52 | (_: React.MouseEvent<HTMLButtonElement>) => { 53 | onDidChange(value); 54 | }, 55 | [onDidChange, value], 56 | ); 57 | 58 | return ( 59 | <InnerRadioGroup.Item 60 | className={classnames(styles.radioOption, statusStyles)} 61 | id={id} 62 | value={convertOptionValue(value)} 63 | checked={checked} 64 | disabled={disabled} 65 | onClick={onInputChange} 66 | > 67 | <InnerRadioGroup.Indicator className={classnames(styles.indicator, statusStyles)} /> 68 | </InnerRadioGroup.Item> 69 | ); 70 | }; 71 | ```