This is page 10 of 137. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?page={x} to view the full context. # Directory Structure ``` ├── .changeset │ ├── cold-items-taste.md │ ├── config.json │ ├── empty-spiders-dress.md │ ├── shy-windows-allow.md │ ├── sour-coins-read.md │ ├── tame-zebras-invite.md │ ├── three-ideas-invent.md │ ├── twenty-jeans-watch.md │ ├── warm-spies-melt.md │ └── whole-ways-cry.md ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── blog-page-component.png │ │ │ │ ├── blog-scrabble.png │ │ │ │ ├── integrated-blog-search.png │ │ │ │ └── lorem-ipsum.png │ │ │ ├── lorem-ipsum.md │ │ │ ├── newest-post.md │ │ │ ├── older-post.md │ │ │ └── welcome-to-the-xmlui-blog.md │ │ ├── mockServiceWorker.js │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── CONTRIBUTING.md ├── docs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── ComponentRefLinks.txt │ ├── content │ │ ├── _meta.json │ │ ├── components │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── APICall.md │ │ │ ├── App.md │ │ │ ├── AppHeader.md │ │ │ ├── AppState.md │ │ │ ├── AutoComplete.md │ │ │ ├── Avatar.md │ │ │ ├── Backdrop.md │ │ │ ├── Badge.md │ │ │ ├── BarChart.md │ │ │ ├── Bookmark.md │ │ │ ├── Breakout.md │ │ │ ├── Button.md │ │ │ ├── Card.md │ │ │ ├── Carousel.md │ │ │ ├── ChangeListener.md │ │ │ ├── Checkbox.md │ │ │ ├── CHStack.md │ │ │ ├── ColorPicker.md │ │ │ ├── Column.md │ │ │ ├── ContentSeparator.md │ │ │ ├── CVStack.md │ │ │ ├── DataSource.md │ │ │ ├── DateInput.md │ │ │ ├── DatePicker.md │ │ │ ├── DonutChart.md │ │ │ ├── DropdownMenu.md │ │ │ ├── EmojiSelector.md │ │ │ ├── ExpandableItem.md │ │ │ ├── FileInput.md │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FlowLayout.md │ │ │ ├── Footer.md │ │ │ ├── Form.md │ │ │ ├── FormItem.md │ │ │ ├── FormSection.md │ │ │ ├── Fragment.md │ │ │ ├── H1.md │ │ │ ├── H2.md │ │ │ ├── H3.md │ │ │ ├── H4.md │ │ │ ├── H5.md │ │ │ ├── H6.md │ │ │ ├── Heading.md │ │ │ ├── HSplitter.md │ │ │ ├── HStack.md │ │ │ ├── Icon.md │ │ │ ├── IFrame.md │ │ │ ├── Image.md │ │ │ ├── Items.md │ │ │ ├── LabelList.md │ │ │ ├── Legend.md │ │ │ ├── LineChart.md │ │ │ ├── Link.md │ │ │ ├── List.md │ │ │ ├── Logo.md │ │ │ ├── Markdown.md │ │ │ ├── MenuItem.md │ │ │ ├── MenuSeparator.md │ │ │ ├── ModalDialog.md │ │ │ ├── NavGroup.md │ │ │ ├── NavLink.md │ │ │ ├── NavPanel.md │ │ │ ├── NoResult.md │ │ │ ├── NumberBox.md │ │ │ ├── Option.md │ │ │ ├── Page.md │ │ │ ├── PageMetaTitle.md │ │ │ ├── Pages.md │ │ │ ├── Pagination.md │ │ │ ├── PasswordInput.md │ │ │ ├── PieChart.md │ │ │ ├── ProgressBar.md │ │ │ ├── Queue.md │ │ │ ├── RadioGroup.md │ │ │ ├── RealTimeAdapter.md │ │ │ ├── Redirect.md │ │ │ ├── Select.md │ │ │ ├── Slider.md │ │ │ ├── Slot.md │ │ │ ├── SpaceFiller.md │ │ │ ├── Spinner.md │ │ │ ├── Splitter.md │ │ │ ├── Stack.md │ │ │ ├── StickyBox.md │ │ │ ├── SubMenuItem.md │ │ │ ├── Switch.md │ │ │ ├── TabItem.md │ │ │ ├── Table.md │ │ │ ├── TableOfContents.md │ │ │ ├── Tabs.md │ │ │ ├── Text.md │ │ │ ├── TextArea.md │ │ │ ├── TextBox.md │ │ │ ├── Theme.md │ │ │ ├── TimeInput.md │ │ │ ├── Timer.md │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneSwitch.md │ │ │ ├── Tooltip.md │ │ │ ├── Tree.md │ │ │ ├── VSplitter.md │ │ │ ├── VStack.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ ├── xmlui-spreadsheet │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Spreadsheet.md │ │ │ └── xmlui-website-blocks │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Carousel.md │ │ │ ├── HelloMd.md │ │ │ ├── HeroSection.md │ │ │ └── ScrollToTop.md │ │ └── extensions │ │ ├── _meta.json │ │ ├── xmlui-animations │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Animation.md │ │ │ ├── FadeAnimation.md │ │ │ ├── FadeInAnimation.md │ │ │ ├── FadeOutAnimation.md │ │ │ ├── ScaleAnimation.md │ │ │ └── SlideInAnimation.md │ │ └── xmlui-website-blocks │ │ ├── _meta.json │ │ ├── _overview.md │ │ ├── Carousel.md │ │ ├── HelloMd.md │ │ ├── HeroSection.md │ │ └── ScrollToTop.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── feed.rss │ │ ├── mockServiceWorker.js │ │ ├── pages │ │ │ ├── _meta.json │ │ │ ├── app-structure.md │ │ │ ├── build-editor-component.md │ │ │ ├── build-hello-world-component.md │ │ │ ├── components-intro.md │ │ │ ├── context-variables.md │ │ │ ├── forms.md │ │ │ ├── globals.md │ │ │ ├── glossary.md │ │ │ ├── helper-tags.md │ │ │ ├── hosted-deployment.md │ │ │ ├── howto │ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md │ │ │ │ ├── chain-a-refetch.md │ │ │ │ ├── debug-a-component.md │ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md │ │ │ │ ├── delegate-a-method.md │ │ │ │ ├── do-custom-form-validation.md │ │ │ │ ├── expose-a-method-from-a-component.md │ │ │ │ ├── filter-and-transform-data-from-an-api.md │ │ │ │ ├── group-items-in-list-by-a-property.md │ │ │ │ ├── handle-background-operations.md │ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md │ │ │ │ ├── make-a-set-of-equal-width-cards.md │ │ │ │ ├── make-a-table-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── actions.md │ ├── AppRoot.md │ ├── component-apis.md │ ├── component-rendering.md │ ├── component-review-checklist.md │ ├── containers.md │ ├── data-sources.md │ ├── e2e-summary.md │ ├── expression-evaluation.md │ ├── glossary.md │ ├── helper-components.md │ ├── index.md │ ├── loaders.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── rendering-fundamentals.md │ ├── reusable-components.md │ ├── standalone-apps.md │ ├── state-management.md │ └── xmlui-extensibility.xlsx ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.tsx │ │ ├── SelectionStore │ │ │ ├── SelectionStore.md │ │ │ ├── SelectionStore.tsx │ │ │ └── SelectionStoreNative.tsx │ │ ├── Slider │ │ │ ├── Slider.md │ │ │ ├── Slider.module.scss │ │ │ ├── Slider.spec.ts │ │ │ ├── Slider.tsx │ │ │ └── SliderNative.tsx │ │ ├── Slot │ │ │ ├── Slot.md │ │ │ ├── Slot.spec.ts │ │ │ └── Slot.ts │ │ ├── SlotItem.tsx │ │ ├── SpaceFiller │ │ │ ├── SpaceFiller.md │ │ │ ├── SpaceFiller.module.scss │ │ │ ├── SpaceFiller.spec.ts │ │ │ ├── SpaceFiller.tsx │ │ │ └── SpaceFillerNative.tsx │ │ ├── Spinner │ │ │ ├── Spinner.md │ │ │ ├── Spinner.module.scss │ │ │ ├── Spinner.spec.ts │ │ │ ├── Spinner.tsx │ │ │ └── SpinnerNative.tsx │ │ ├── Splitter │ │ │ ├── HSplitter.md │ │ │ ├── HSplitter.spec.ts │ │ │ ├── Splitter.md │ │ │ ├── Splitter.module.scss │ │ │ ├── Splitter.spec.ts │ │ │ ├── Splitter.tsx │ │ │ ├── SplitterNative.tsx │ │ │ ├── utils.ts │ │ │ ├── VSplitter.md │ │ │ └── VSplitter.spec.ts │ │ ├── Stack │ │ │ ├── CHStack.md │ │ │ ├── CHStack.spec.ts │ │ │ ├── CVStack.md │ │ │ ├── CVStack.spec.ts │ │ │ ├── HStack.md │ │ │ ├── HStack.spec.ts │ │ │ ├── Stack.md │ │ │ ├── Stack.module.scss │ │ │ ├── Stack.spec.ts │ │ │ ├── Stack.tsx │ │ │ ├── StackNative.tsx │ │ │ ├── VStack.md │ │ │ └── VStack.spec.ts │ │ ├── StickyBox │ │ │ ├── StickyBox.md │ │ │ ├── StickyBox.module.scss │ │ │ ├── StickyBox.tsx │ │ │ └── StickyBoxNative.tsx │ │ ├── Switch │ │ │ ├── Switch.md │ │ │ ├── Switch.spec.ts │ │ │ └── Switch.tsx │ │ ├── Table │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── react-table-config.d.ts │ │ │ ├── Table.md │ │ │ ├── Table.module.scss │ │ │ ├── Table.spec.ts │ │ │ ├── Table.tsx │ │ │ ├── TableNative.tsx │ │ │ └── useRowSelection.tsx │ │ ├── TableOfContents │ │ │ ├── TableOfContents.module.scss │ │ │ ├── TableOfContents.spec.ts │ │ │ ├── TableOfContents.tsx │ │ │ └── TableOfContentsNative.tsx │ │ ├── Tabs │ │ │ ├── TabContext.tsx │ │ │ ├── TabItem.md │ │ │ ├── TabItem.tsx │ │ │ ├── TabItemNative.tsx │ │ │ ├── Tabs.md │ │ │ ├── Tabs.module.scss │ │ │ ├── Tabs.spec.ts │ │ │ ├── Tabs.tsx │ │ │ └── TabsNative.tsx │ │ ├── Text │ │ │ ├── Text.md │ │ │ ├── Text.module.scss │ │ │ ├── Text.spec.ts │ │ │ ├── Text.tsx │ │ │ └── TextNative.tsx │ │ ├── TextArea │ │ │ ├── TextArea.md │ │ │ ├── TextArea.module.scss │ │ │ ├── TextArea.spec.ts │ │ │ ├── TextArea.tsx │ │ │ ├── TextAreaNative.tsx │ │ │ ├── TextAreaResizable.tsx │ │ │ └── useComposedRef.ts │ │ ├── TextBox │ │ │ ├── TextBox.md │ │ │ ├── TextBox.module.scss │ │ │ ├── TextBox.spec.ts │ │ │ ├── TextBox.tsx │ │ │ └── TextBoxNative.tsx │ │ ├── Theme │ │ │ ├── NotificationToast.tsx │ │ │ ├── Theme.md │ │ │ ├── Theme.module.scss │ │ │ ├── Theme.spec.ts │ │ │ ├── Theme.tsx │ │ │ └── ThemeNative.tsx │ │ ├── TimeInput │ │ │ ├── TimeInput.md │ │ │ ├── TimeInput.module.scss │ │ │ ├── TimeInput.spec.ts │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeInputNative.tsx │ │ │ └── utils.ts │ │ ├── Timer │ │ │ ├── Timer.md │ │ │ ├── Timer.spec.ts │ │ │ ├── Timer.tsx │ │ │ └── TimerNative.tsx │ │ ├── Toggle │ │ │ ├── Toggle.module.scss │ │ │ └── Toggle.tsx │ │ ├── ToneChangerButton │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneChangerButton.spec.ts │ │ │ └── ToneChangerButton.tsx │ │ ├── ToneSwitch │ │ │ ├── ToneSwitch.md │ │ │ ├── ToneSwitch.module.scss │ │ │ ├── ToneSwitch.spec.ts │ │ │ ├── ToneSwitch.tsx │ │ │ └── ToneSwitchNative.tsx │ │ ├── Tooltip │ │ │ ├── Tooltip.md │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.spec.ts │ │ │ ├── Tooltip.tsx │ │ │ └── TooltipNative.tsx │ │ ├── Tree │ │ │ ├── testData.ts │ │ │ ├── Tree-dynamic.spec.ts │ │ │ ├── Tree-icons.spec.ts │ │ │ ├── Tree.md │ │ │ ├── Tree.spec.ts │ │ │ ├── TreeComponent.module.scss │ │ │ ├── TreeComponent.tsx │ │ │ └── TreeNative.tsx │ │ ├── TreeDisplay │ │ │ ├── TreeDisplay.md │ │ │ ├── TreeDisplay.module.scss │ │ │ ├── TreeDisplay.tsx │ │ │ └── TreeDisplayNative.tsx │ │ ├── ValidationSummary │ │ │ ├── ValidationSummary.module.scss │ │ │ └── ValidationSummary.tsx │ │ └── VisuallyHidden.tsx │ ├── components-core │ │ ├── abstractions │ │ │ ├── ComponentRenderer.ts │ │ │ ├── LoaderRenderer.ts │ │ │ ├── standalone.ts │ │ │ └── treeAbstractions.ts │ │ ├── action │ │ │ ├── actions.ts │ │ │ ├── APICall.tsx │ │ │ ├── FileDownloadAction.tsx │ │ │ ├── FileUploadAction.tsx │ │ │ ├── NavigateAction.tsx │ │ │ └── TimedAction.tsx │ │ ├── ApiBoundComponent.tsx │ │ ├── appContext │ │ │ ├── date-functions.ts │ │ │ ├── math-function.ts │ │ │ └── misc-utils.ts │ │ ├── AppContext.tsx │ │ ├── behaviors │ │ │ ├── Behavior.tsx │ │ │ ├── BehaviorContext.tsx │ │ │ └── CoreBehaviors.tsx │ │ ├── component-hooks.ts │ │ ├── ComponentDecorator.tsx │ │ ├── ComponentViewer.tsx │ │ ├── CompoundComponent.tsx │ │ ├── constants.ts │ │ ├── DebugViewProvider.tsx │ │ ├── descriptorHelper.ts │ │ ├── devtools │ │ │ ├── InspectorDialog.module.scss │ │ │ ├── InspectorDialog.tsx │ │ │ └── InspectorDialogVisibilityContext.tsx │ │ ├── EngineError.ts │ │ ├── event-handlers.ts │ │ ├── InspectorButton.module.scss │ │ ├── InspectorContext.tsx │ │ ├── interception │ │ │ ├── abstractions.ts │ │ │ ├── ApiInterceptor.ts │ │ │ ├── ApiInterceptorProvider.tsx │ │ │ ├── apiInterceptorWorker.ts │ │ │ ├── Backend.ts │ │ │ ├── Errors.ts │ │ │ ├── IndexedDb.ts │ │ │ ├── initMock.ts │ │ │ ├── InMemoryDb.ts │ │ │ ├── ReadonlyCollection.ts │ │ │ └── useApiInterceptorContext.tsx │ │ ├── loader │ │ │ ├── ApiLoader.tsx │ │ │ ├── DataLoader.tsx │ │ │ ├── ExternalDataLoader.tsx │ │ │ ├── Loader.tsx │ │ │ ├── MockLoaderRenderer.tsx │ │ │ └── PageableLoader.tsx │ │ ├── LoaderComponent.tsx │ │ ├── markup-check.ts │ │ ├── parts.ts │ │ ├── renderers.ts │ │ ├── rendering │ │ │ ├── AppContent.tsx │ │ │ ├── AppRoot.tsx │ │ │ ├── AppWrapper.tsx │ │ │ ├── buildProxy.ts │ │ │ ├── collectFnVarDeps.ts │ │ │ ├── ComponentAdapter.tsx │ │ │ ├── ComponentWrapper.tsx │ │ │ ├── Container.tsx │ │ │ ├── containers.ts │ │ │ ├── ContainerWrapper.tsx │ │ │ ├── ErrorBoundary.module.scss │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── InvalidComponent.module.scss │ │ │ ├── InvalidComponent.tsx │ │ │ ├── nodeUtils.ts │ │ │ ├── reducer.ts │ │ │ ├── renderChild.tsx │ │ │ ├── StandaloneComponent.tsx │ │ │ ├── StateContainer.tsx │ │ │ ├── UnknownComponent.module.scss │ │ │ ├── UnknownComponent.tsx │ │ │ └── valueExtractor.ts │ │ ├── reportEngineError.ts │ │ ├── RestApiProxy.ts │ │ ├── script-runner │ │ │ ├── asyncProxy.ts │ │ │ ├── AttributeValueParser.ts │ │ │ ├── bannedFunctions.ts │ │ │ ├── BindingTreeEvaluationContext.ts │ │ │ ├── eval-tree-async.ts │ │ │ ├── eval-tree-common.ts │ │ │ ├── eval-tree-sync.ts │ │ │ ├── ParameterParser.ts │ │ │ ├── process-statement-async.ts │ │ │ ├── process-statement-common.ts │ │ │ ├── process-statement-sync.ts │ │ │ ├── ScriptingSourceTree.ts │ │ │ ├── simplify-expression.ts │ │ │ ├── statement-queue.ts │ │ │ └── visitors.ts │ │ ├── StandaloneApp.tsx │ │ ├── StandaloneExtensionManager.ts │ │ ├── TableOfContentsContext.tsx │ │ ├── theming │ │ │ ├── _themes.scss │ │ │ ├── component-layout-resolver.ts │ │ │ ├── extendThemeUtils.ts │ │ │ ├── hvar.ts │ │ │ ├── layout-resolver.ts │ │ │ ├── parse-layout-props.ts │ │ │ ├── StyleContext.tsx │ │ │ ├── StyleRegistry.ts │ │ │ ├── ThemeContext.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ ├── themes │ │ │ │ ├── base-utils.ts │ │ │ │ ├── palette.ts │ │ │ │ ├── root.ts │ │ │ │ ├── solid.ts │ │ │ │ ├── theme-colors.ts │ │ │ │ └── xmlui.ts │ │ │ ├── themeVars.module.scss │ │ │ ├── themeVars.ts │ │ │ ├── transformThemeVars.ts │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── actionUtils.ts │ │ │ ├── audio-utils.ts │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /xmlui/src/components/DropdownMenu/MenuItem.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Action handling**: Support both navigation (`to` property) and custom click handlers - **Visual feedback**: Built-in active, hover, and disabled states for clear user interaction - **Icon support**: Optional icons with flexible positioning (start or end) - **Menu integration**: Designed to work seamlessly within `DropdownMenu` and `SubMenuItem` hierarchies **Usage pattern:** Always used within menu containers like `DropdownMenu`. Use `to` for navigation or `onClick` for custom actions. For complex menu structures, combine with `MenuSeparator` and `SubMenuItem` components. %-DESC-END %-PROP-START icon ```xmlui-pg copy display name="Example: icon" height="200px" <App> <DropdownMenu label="DropdownMenu"> <MenuItem icon="drive">Item 1</MenuItem> <MenuItem icon="trash">Item 2</MenuItem> <MenuItem icon="email">Item 3</MenuItem> </DropdownMenu> </App> ``` %-PROP-END %-PROP-START iconPosition ```xmlui-pg copy display name="Example: iconPosition" height="200px" <App> <DropdownMenu label="DropdownMenu"> <MenuItem icon="drive" iconPosition="start">Item 1</MenuItem> <MenuItem icon="trash" iconPosition="end">Item 2</MenuItem> <MenuItem icon="email">Item 3</MenuItem> </DropdownMenu> </App> ``` %-PROP-END %-PROP-START active ```xmlui-pg copy display name="Example: active" height="200px" <App> <DropdownMenu label="DropdownMenu"> <MenuItem icon="drive" active="true">Item 1</MenuItem> <MenuItem icon="trash">Item 2</MenuItem> <MenuItem icon="email">Item 3</MenuItem> </DropdownMenu> </App> ``` %-PROP-END %-EVENT-START click This event is fired when the user clicks the menu item. With an event handler, you can define how to respond to the user's click. If this event does not have an associated event handler but the `to` property has a value, clicking the component navigates the URL set in `to`. If both properties are defined, `click` takes precedence. ```xmlui-pg copy display name="Example: click" height="200px" <DropdownMenu label="DropdownMenu"> <MenuItem onClick="toast('Item 1 clicked')">Item 1</MenuItem> <MenuItem onClick="toast('Item 2 clicked')">Item 2</MenuItem> <MenuItem onClick="toast('Item 3 clicked')">Item 3</MenuItem> </DropdownMenu> ``` %-EVENT-END ``` -------------------------------------------------------------------------------- /packages/xmlui-os-frames/src/WindowsAppFrame.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $backgroundColor-content-WindowsAppFrame: createThemeVar("backgroundColor-content-WindowsAppFrame"); .toolbar { display: flex; justify-content: space-between; align-items: center; height: 28px; } .topInfo { display: flex; flex-grow: 1; align-items: center; } .uicon { height: 100%; display: flex; align-items: center; } .appFullName { font-size: 0.75rem; } .actbtns { display: flex; align-items: center; height: 100%; .uicon { height: 100%; padding: 0 18px; transition: all ease-in-out 60ms; img { transition: all ease-in-out 60ms; } &:hover { background: rgba(136, 136, 136, 0.2); } &.closeBtn:hover { background: rgba(255, 0, 0, 0.8); img { filter: invert(1); } } } } .prtclk { cursor: pointer; } .closeBtn { cursor: pointer; margin-right: 0; } .notepad { resize: both; overflow: auto; display: flex; flex-direction: column; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); border-radius: 0.375rem; width: 55rem; height: 32rem; min-width: 400px; min-height: 300px; .windowScreen { display: flex; flex-direction: column; flex: 1; min-height: 0; } .topBar { display: flex; font-size: 0.75rem; padding-top: 0.5rem; padding-bottom: 0.5rem; } .topBarItem { margin-left: 0.5rem; margin-right: 0.5rem; } .topBarItem + .topBarItem { margin-left: 1rem; margin-right: 1rem; } .restWindow { min-height: 0; flex-grow: 1; background-color: $backgroundColor-content-WindowsAppFrame; overflow: auto; } .noteText { min-height: 0; scrollbar-width: thin; scrollbar-color: #c0c0c0 #f9f9f9; padding: t.$space-4; gap: t.$space-4; display: flex; flex-direction: column; } } // --- We export the theme variables to add them to the component renderer :export{ themeVars: t.json-stringify($themeVars) } ``` -------------------------------------------------------------------------------- /xmlui/src/abstractions/scripting/Token.ts: -------------------------------------------------------------------------------- ```typescript // Represents a generic token export type Token = { // The raw text of the token readonly text: string; // The type of the token readonly type: TokenType; // The location of the token readonly location: TokenLocation; }; // Represents the location of a token export interface TokenLocation { // Start position in the source stream readonly startPosition: number; // End position (exclusive) in the source stream readonly endPosition: number; // Start line number readonly startLine: number; // End line number of the token readonly endLine: number; // Start column number of the token readonly startColumn: number; // End column number of the token readonly endColumn: number; } // Token types available for parsing // Using declare enum to make this a type-only declaration export declare enum TokenType { Eof = -1, Ws = -2, BlockComment = -3, EolComment = -4, Unknown = 0, // --- Binding Expression specific tokens LParent, RParent, Identifier, Exponent, Divide, Multiply, Remainder, Plus, Minus, BitwiseXor, BitwiseOr, LogicalOr, BitwiseAnd, LogicalAnd, IncOp, DecOp, Assignment, AddAssignment, SubtractAssignment, ExponentAssignment, MultiplyAssignment, DivideAssignment, RemainderAssignment, ShiftLeftAssignment, ShiftRightAssignment, SignedShiftRightAssignment, BitwiseAndAssignment, BitwiseXorAssignment, BitwiseOrAssignment, LogicalAndAssignment, LogicalOrAssignment, NullCoalesceAssignment, Semicolon, Comma, Colon, LSquare, RSquare, QuestionMark, NullCoalesce, OptionalChaining, BinaryNot, LBrace, RBrace, Equal, StrictEqual, LogicalNot, NotEqual, StrictNotEqual, LessThan, LessThanOrEqual, ShiftLeft, GreaterThan, GreaterThanOrEqual, ShiftRight, SignedShiftRight, Dot, Spread, Global, Backtick, DollarLBrace, Arrow, DecimalLiteral, HexadecimalLiteral, BinaryLiteral, RealLiteral, StringLiteral, Infinity, NaN, True, False, Typeof, Null, Undefined, In, Let, Const, Var, If, Else, Return, Break, Continue, Do, While, For, Of, Try, Catch, Finally, Throw, Switch, Case, Default, Delete, Function, Export, Import, As, From, } ``` -------------------------------------------------------------------------------- /packages/xmlui-os-frames/src/MacOSAppFrame.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } .app { --elevated-shadow: 0px 8.5px 10px #0000001d, 0px 68px 80px #0000003b; will-change: width, height; box-shadow: var(--elevated-shadow); cursor: var(--system-cursor-default), auto; border-radius: .75rem; grid-template-rows: 1fr; display: grid; width: 50rem; height: 30rem; z-index: 14; touch-action: none; position: relative; resize: both; overflow: auto; } .appButtons { position: absolute; top: 1rem; left: 1rem; box-shadow: none !important; z-index: var(--system-z-index-window-traffic-lights); } .appButtonsInner { --button-size: .8rem; grid-template-columns: repeat(3, var(--button-size)); align-items: center; gap: .6rem; height: 100%; display: grid; svg{ opacity: 0; } &:hover{ svg{ opacity: 1; } } } .button{ height: var(--button-size); width: var(--button-size); background-color: var(--bgcolor); box-shadow: 0 0 0 .5px var(--border-color); border-radius: 50%; transition: transform .1s ease-in; display: flex; justify-content: center; align-items: center; } .close{ --bgcolor: #ff5f56; --border-color: #e0443e; } .minimize{ --bgcolor: #ffbd2e; --border-color: #dea123; } .stretch{ --bgcolor: #27c93f; --border-color: #1aab29; } .container { background-color: t.$backgroundColor; border-radius: inherit; grid-template-rows: auto 1fr; min-height: auto; max-height: 100%; display: grid; overflow-y: hidden; height: 100% !important; } .titleBar { border-bottom: solid .9px hsla(var(--system-color-dark-hsl), .3); justify-content: center; width: 100%; padding: .9rem 1rem; display: flex; } .mainArea { color: var(--system-color-light-contrast); flex-direction: column; width: 100%; height: 100%; font-size: 1rem; display: flex; overflow-y: auto; padding: t.$space-4; gap: t.$space-4; } // --- We export the theme variables to add them to the component renderer :export{ themeVars: t.json-stringify($themeVars) } ``` -------------------------------------------------------------------------------- /xmlui/src/components/AutoComplete/AutoComplete.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Type-ahead filtering**: Users can type to narrow down options in real-time - **Multi-select support**: Set `multi="true"` to allow selecting multiple items - **Custom option creation**: Enable `creatable="true"` to let users add new options - **Rich customization**: Use `optionTemplate` to create complex option layouts ## Using AutoComplete ```xmlui-pg copy display height="200px" name="Example: Using AutoComplete" <App> <AutoComplete> <Option value="1" label="Bruce Wayne" /> <Option value="2" label="Clark Kent" enabled="false" /> <Option value="3" label="Diana Prince" /> </AutoComplete> </App> ``` %-DESC-END %-PROP-START emptyListTemplate ```xmlui-pg copy display height="200px" name="Example: emptyListTemplate" <App> <AutoComplete> <property name="emptyListTemplate"> <Text>No options found</Text> </property> </AutoComplete> </App> ``` %-PROP-END %-PROP-START multi ```xmlui-pg copy display height="300px" name="Example: multi" <App> <AutoComplete multi="true"> <Option value="1" label="Bruce Wayne" /> <Option value="2" label="Clark Kent" /> <Option value="3" label="Diana Prince" /> <Option value="4" label="Barry Allen" /> <Option value="5" label="Hal Jordan" /> </AutoComplete> </App> ``` %-PROP-END %-PROP-START optionTemplate ```xmlui-pg copy display height="300px" name="Example: optionTemplate" <App> <AutoComplete multi="true"> <property name="optionTemplate"> <Text textAlign="center" color="red">{$item.label}</Text> </property> <Option value="1" label="Bruce Wayne" /> <Option value="2" label="Clark Kent" /> <Option value="3" label="Diana Prince" /> </AutoComplete> </App> ``` %-PROP-END %-EVENT-START itemCreated Add a few new items not in the options list. The following markup will display them: ```xmlui-pg copy display height="300px" name="Example: itemCreated" <App var.newItems="{[]}"> <AutoComplete id="autoComplete" creatable="true" onItemCreated="item => newItems.push(item)"> <Option value="1" label="Bruce Wayne" /> <Option value="2" label="Clark Kent" /> </AutoComplete> <Text testId="text"> New items: {newItems.join(", ")} </Text> </App> ``` %-EVENT-END ``` -------------------------------------------------------------------------------- /xmlui/src/abstractions/_conventions.md: -------------------------------------------------------------------------------- ```markdown # TypeScript Abstractions Conventions This document outlines best practices and conventions for working with TypeScript abstractions in the XMLUI project. Following these conventions ensures that files in the abstractions folder only contain type definitions and don't generate JavaScript code when compiled. ## Core Principles The abstractions folder is intended to contain **type definitions only** with no runtime JavaScript code generation. ## Conventions for Type Definitions ### 1. Type-Only Constructs Use these TypeScript constructs in abstractions files: - `interface` for defining object shapes - `type` for type aliases and unions - `declare` keyword for values that must exist at runtime Avoid these constructs that generate JavaScript: - `class` declarations - `function` declarations - `const`, `let`, or `var` declarations without `declare` - Regular `enum` declarations ### 2. Implementation Patterns #### Pattern A: Type-Only Declarations Use the `declare` keyword to indicate that a value exists at runtime but its implementation is elsewhere: ```typescript export declare const MY_CONSTANT: 42; export declare enum MyEnum { Value1, Value2 } export declare function myFunction(): void; ``` #### Pattern B: Implementation Separation Place implementation files outside the abstractions folder: ```typescript // In abstractions/MyTypes.ts export type MyType = { prop: string }; // In implementation/MyImplementation.ts import type { MyType } from "../abstractions/MyTypes"; export const createMyType = (): MyType => ({ prop: "value" }); ``` ### 3. Import Patterns - Always use `import type` for type imports in abstractions: ```typescript import type { SomeType } from "./OtherFile"; ``` ## Common Scenarios 1. **Constants**: Use `declare const` in abstractions 2. **Enums**: Use `declare enum` or type unions 3. **Functions**: Use `declare function` or move implementation outside 4. **Classes**: Use interfaces instead or move implementation outside ## Benefits - Clear separation between types and implementation - Improved maintainability and refactoring - No runtime overhead from abstraction files - Better TypeScript compilation performance By following these conventions, you help maintain a clean architecture with proper separation between type definitions and runtime implementation. ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/abstractions/ComponentRenderer.ts: -------------------------------------------------------------------------------- ```typescript import type { Dispatch, MutableRefObject, RefObject } from "react"; import type { AppContextObject } from "../../abstractions/AppContextDefs"; import type { LookupAsyncFnInner, LookupSyncFnInner } from "../../abstractions/ActionDefs"; import type { CodeDeclaration } from "../script-runner/ScriptingSourceTree"; import type { ComponentMetadata, ParentRenderContext } from "../../abstractions/ComponentDefs"; import type { ComponentRendererContextBase } from "../../abstractions/RendererDefs"; import type { ComponentApi, ContainerState, RegisterComponentApiFnInner, } from "../rendering/ContainerWrapper"; import type { ContainerAction } from "../rendering/containers"; // This interface defines the renderer context for the XMLUI core framework // components. Its implementations are used only within the component core. export interface InnerRendererContext<T extends ComponentMetadata = ComponentMetadata> extends ComponentRendererContextBase<T> { // The dispatcher function to change the state of the component dispatch: ContainerDispatcher; // The function to register a component API registerComponentApi: RegisterComponentApiFnInner; // The function to obtain a synchronous action handler lookupSyncCallback: LookupSyncFnInner; // The function to obtain an async action handler lookupAction: LookupAsyncFnInner; // The memoized variables (with their values) used in the component memoedVarsRef: MutableRefObject<MemoedVars>; parentRenderContext?: ParentRenderContext; uidInfoRef?: RefObject<Record<string, any>>; } // This property is a redux-style dispatcher that manages state changes in a container. export type ContainerDispatcher = Dispatch<ContainerAction>; // This type represents a map of objects providing access to memoed variables within the // container of a particular component. The key is an expression; the value is an // accessor object. export type MemoedVars = Map< any, { getDependencies: ( value: string | CodeDeclaration, referenceTrackedApi: Record<string, ComponentApi>, ) => Array<string>; obtainValue: ( expression: any, state: ContainerState, appContext: AppContextObject | undefined, strict: boolean | undefined, stateDeps: Record<string, any>, appContextDeps: Record<string, any>, ) => any; } >; ``` -------------------------------------------------------------------------------- /xmlui/src/components/Avatar/AvatarNative.tsx: -------------------------------------------------------------------------------- ```typescript import type { CSSProperties, Ref } from "react"; import { forwardRef, memo, useMemo } from "react"; import classnames from "classnames"; import styles from "./Avatar.module.scss"; type Props = { size?: string; url?: string; name?: string; style?: CSSProperties; className?: string; } & Pick<React.HTMLAttributes<HTMLDivElement>, "onClick">; export const defaultProps: Pick<Props, "size"> = { size: "sm", }; export const Avatar = memo(forwardRef(function Avatar( { size = defaultProps.size, url, name, style, className, onClick, ...rest }: Props, ref: Ref<any>, ) { // Memoize the abbreviated name calculation to avoid recalculation on every render const abbreviatedName = useMemo(() => abbrevName(name ?? null), [name]); // Handle keyboard events for accessibility const handleKeyDown = (event: React.KeyboardEvent) => { if (onClick && (event.key === 'Enter' || event.key === ' ')) { event.preventDefault(); onClick(event as any); } }; // Simplified className generation by directly mapping size to styles const commonClassNames = classnames( className, styles.container, styles[size as keyof typeof styles] || styles.sm, // Fallback to sm if size not found { [styles.clickable]: !!onClick } ); const altTxt = !!name ? `Avatar of ${name}` : "Avatar"; if (url) { return ( <img {...rest} ref={ref} src={url} alt={altTxt} className={commonClassNames} style={style} onClick={onClick} onKeyDown={handleKeyDown} tabIndex={onClick ? 0 : undefined} /> ); } else return ( <div {...rest} ref={ref} className={commonClassNames} style={style} onClick={onClick} onKeyDown={handleKeyDown} role="img" aria-label={altTxt} tabIndex={onClick ? 0 : undefined} > {abbreviatedName || <span aria-hidden="true"></span>} {/* Display initials or an empty decorative span */} </div> ); })); function abbrevName(name: string | null): string | null { if (!!name) { const abbrev = name .trim() .split(" ") .filter((word) => !!word.trim().length) .map((word) => word[0].toUpperCase()) .slice(0, 3) .join(""); return abbrev; } return null; } ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/theming/themes/palette.ts: -------------------------------------------------------------------------------- ```typescript export const palette = { $colorSurface0: "$color-surface-0", $colorSurface50: "$color-surface-50", $colorSurface100: "$color-surface-100", $colorSurface200: "$color-surface-200", $colorSurface200A70: "rgb(from $color-surface-200 r g b / 0.7)", $colorSurface200A80: "rgb(from $color-surface-200 r g b / 0.8)", $colorSurface300: "$color-surface-300", $colorSurface400A80: "rgb(from $color-surface-400 r g b / 0.8)", $colorSurface400: "$color-surface-400", $colorSurface500: "$color-surface-500", $colorSurface500A80: "rgba($color-surface-500, .8)", $colorSurface500A60: "rgba($color-surface-500, .6)", $colorSurface600: "$color-surface-600", $colorSurface700: "$color-surface-700", $colorSurface700A30: "rgb(from $color-surface-700 r g b / 0.3)", $colorSurface700A90: "rgb(from $color-surface-700 r g b / 0.9)", $colorSurface800: "$color-surface-800", $colorSurface900: "$color-surface-900", $colorSurface950: "$color-surface-950", $colorPrimary50: "$color-primary-50", $colorPrimary100: "$color-primary-100", $colorPrimary200: "$color-primary-200", $colorPrimary300: "$color-primary-300", $colorPrimary400: "$color-primary-400", $colorPrimary500: "$color-primary-500", $colorPrimary600: "$color-primary-600", $colorPrimary700: "$color-primary-700", $colorPrimary800: "$color-primary-800", $colorPrimary900: "$color-primary-900", $colorPrimary950: "$color-primary-950", $colorSecondary50: "$color-secondary-50", $colorSecondary100: "$color-secondary-100", $colorSecondary200: "$color-secondary-200", $colorSecondary400: "$color-secondary-400", $colorSecondary500: "$color-secondary-500", $colorSecondary600: "$color-secondary-600", $colorSecondary900: "$color-secondary-900", $colorSecondary950: "$color-secondary-950", $colorWarn700: "$color-warn-700", $colorWarn950: "$color-warn-950", $colorDanger50: "$color-danger-50", $colorDanger100: "$color-danger-100", $colorDanger400: "$color-danger-400", $colorDanger500: "$color-danger-500", $colorDanger600: "$color-danger-600", $colorDanger700: "$color-danger-700", $colorDanger900: "$color-danger-900", $colorDanger950: "$color-danger-950", $colorSuccess600: "$color-success-600", $colorSuccess950: "$color-success-950", $colorInfo600: "$color-info-600", $colorInfo800: "$color-info-700", }; ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/scripts-runner/process-implicit-context.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect, it } from "vitest"; import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; import { createEvalContext, parseStatements } from "./test-helpers"; import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; describe("Process implicit context (exp)", () => { it("Implicit context #1", async () => { // --- Arrange const source = "x = Impl.myCalc(23);"; const Impl: any = { _SUPPORT_IMPLICIT_CONTEXT: true, _GET_CONTEXT: () => 100, myCalc: (context: any, arg: any) => context + arg }; const evalContext = createEvalContext({ localContext: { x: 1 }, appContext: { Impl }, implicitContextGetter: (obj: any) => obj._GET_CONTEXT() }); const statements = parseStatements(source); // --- Act const diag = await processStatementQueueAsync(statements, evalContext); // --- Assert expect(evalContext.mainThread!.blocks!.length).equal(1); expect(evalContext.localContext.x).equal(123); expect(diag.processedStatements).equal(1); expect(diag.maxLoops).equal(0); expect(diag.maxBlocks).equal(1); expect(diag.maxQueueLength).equal(1); expect(diag.clearToLabels).equal(0); expect(diag.unshiftedItems).equal(0); }); it("Implicit context (sync) # ", () => { // --- Arrange const source = "x = Impl.myCalc(23);"; const Impl: any = { _SUPPORT_IMPLICIT_CONTEXT: true, _GET_CONTEXT: () => 100, myCalc: (context: any, arg: any) => context + arg }; const evalContext = createEvalContext({ localContext: { x: 1 }, appContext: { Impl }, implicitContextGetter: (obj: any) => obj._GET_CONTEXT() }); const statements = parseStatements(source); // --- Act const diag = processStatementQueue(statements, evalContext); // --- Assert expect(evalContext.mainThread!.blocks!.length).equal(1); expect(evalContext.localContext.x).equal(123); expect(diag.processedStatements).equal(1); expect(diag.maxLoops).equal(0); expect(diag.maxBlocks).equal(1); expect(diag.maxQueueLength).equal(1); expect(diag.clearToLabels).equal(0); expect(diag.unshiftedItems).equal(0); }); }); ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/process-implicit-context.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect, it } from "vitest"; import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; import { createEvalContext, parseStatements } from "./test-helpers"; import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; describe("Process implicit context", () => { it("Implicit context #1", async () => { // --- Arrange const source = "x = Impl.myCalc(23);"; const Impl: any = { _SUPPORT_IMPLICIT_CONTEXT: true, _GET_CONTEXT: () => 100, myCalc: (context: any, arg: any) => context + arg, }; const evalContext = createEvalContext({ localContext: { x: 1, }, appContext: { Impl, }, implicitContextGetter: (obj: any) => obj._GET_CONTEXT(), }); const statements = parseStatements(source); // --- Act const diag = await processStatementQueueAsync(statements, evalContext); // --- Assert expect(evalContext.mainThread!.blocks!.length).equal(1); expect(evalContext.localContext.x).equal(123); expect(diag.processedStatements).equal(1); expect(diag.maxLoops).equal(0); expect(diag.maxBlocks).equal(1); expect(diag.maxQueueLength).equal(1); expect(diag.clearToLabels).equal(0); expect(diag.unshiftedItems).equal(0); }); it("Implicit context (sync) # ", () => { // --- Arrange const source = "x = Impl.myCalc(23);"; const Impl: any = { _SUPPORT_IMPLICIT_CONTEXT: true, _GET_CONTEXT: () => 100, myCalc: (context: any, arg: any) => context + arg, }; const evalContext = createEvalContext({ localContext: { x: 1, }, appContext: { Impl, }, implicitContextGetter: (obj: any) => obj._GET_CONTEXT(), }); const statements = parseStatements(source); // --- Act const diag = processStatementQueue(statements, evalContext); // --- Assert expect(evalContext.mainThread!.blocks!.length).equal(1); expect(evalContext.localContext.x).equal(123); expect(diag.processedStatements).equal(1); expect(diag.maxLoops).equal(0); expect(diag.maxBlocks).equal(1); expect(diag.maxQueueLength).equal(1); expect(diag.clearToLabels).equal(0); expect(diag.unshiftedItems).equal(0); }); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/FormSection/FormSection.md: -------------------------------------------------------------------------------- ```markdown %-PROP-START columnGap ```xmlui-pg copy display name="Example: columnGap" <Form padding="1rem"> <FormSection columnGap="1rem"> <FormItem width="50%" label="Name" bindTo="" /> <FormItem width="50%" label="Occupation" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-STARt rowGap ```xmlui-pg copy display name="Example: rowGap" <Form padding="1rem"> <FormSection rowGap="2rem"> <FormItem label="Name" bindTo="" /> <FormItem label="Occupation" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-START info ```xmlui-pg copy display name="Example: info" <Form padding="1rem"> <FormSection info="This is some information about a particular section."> <FormItem label="Input Field" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-START infoFontSize ```xmlui-pg copy {4} display name="Example: infoFontSize" <Form padding="1rem"> <FormSection info="This is some information about a particular section." infoFontSize="18px" > <FormItem label="Input Field" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-START heading ```xmlui-pg copy display name="Example: heading" <Form padding="1rem"> <FormSection heading="Basic Heading"> <FormItem label="Input Field" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-START headingLevel ```xmlui-pg copy display name="Example: headingLevel" <Form padding="1rem"> <FormSection heading="Basic Heading" headingLevel="h1"> <FormItem label="Input Field" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-START headingWeight The default weight is `bold`. ```xmlui-pg copy display name="Example: headingWeight" <Form padding="1rem"> <FormSection heading="Basic Heading" headingWeight="normal"> <FormItem label="Input Field" bindTo="" /> </FormSection> </Form> ``` %-PROP-END %-PROP-START paddingBottom ```xmlui-pg copy display name="Example: paddingBottom" <Form padding="1rem"> <FormSection paddingBottom="3rem" heading="Basic Info"> <FormItem width="50%" label="First Name" bindTo="" /> <FormItem width="50%" label="Last Name" bindTo="" /> </FormSection> <FormSection paddingBottom="0" heading="Job Related"> <FormItem width="50%" label="Occupation" bindTo="" /> <FormItem width="50%" label="Job Description" bindTo="" /> </FormSection> </Form> ``` %-PROP-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/Avatar/Avatar.tsx: -------------------------------------------------------------------------------- ```typescript import styles from "./Avatar.module.scss"; import { createComponentRenderer } from "../../components-core/renderers"; import { parseScssVar } from "../../components-core/theming/themeVars"; import { sizeMd } from "../../components/abstractions"; import { Avatar, defaultProps } from "./AvatarNative"; import { createMetadata, d } from "../metadata-helpers"; const COMP = "Avatar"; export const AvatarMd = createMetadata({ status: "stable", description: "`Avatar` displays a user or entity's profile picture as a circular image, " + "with automatic fallback to initials when no image is provided. It's commonly " + "used in headers, user lists, comments, and anywhere you need to represent a " + "person or organization.", props: { size: { description: `This property defines the display size of the ${COMP}.`, availableValues: sizeMd, valueType: "string", defaultValue: defaultProps.size, }, name: { description: `This property sets the name value the ${COMP} uses to display initials. If neither ` + "this property nor \`url\` is defined, an empty avatar is displayed.", valueType: "string", }, url: { description: `This property specifies the URL of the image to display in the ${COMP}. ` + "If neither this property nor \`name\` is defined, an empty avatar is displayed.", valueType: "string", }, }, events: { click: d("This event is triggered when the avatar is clicked."), }, themeVars: parseScssVar(styles.themeVars), defaultThemeVars: { [`borderRadius-${COMP}`]: "4px", [`boxShadow-${COMP}`]: "inset 0 0 0 1px rgba(4,32,69,0.1)", [`textColor-${COMP}`]: "$textColor-secondary", [`fontWeight-${COMP}`]: "$fontWeight-bold", [`border-${COMP}`]: "0px solid $color-surface-400A80", [`backgroundColor-${COMP}`]: "$color-surface-100", }, }); export const avatarComponentRenderer = createComponentRenderer( COMP, AvatarMd, ({ node, extractValue, lookupEventHandler, className, extractResourceUrl }) => { return ( <Avatar className={className} size={node.props?.size} url={node.props.url ? extractResourceUrl(node.props.url) : undefined} name={extractValue(node.props.name)} onClick={lookupEventHandler("click")} /> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/ChangeListener/ChangeListener.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Value monitoring**: Watches any expression, variable, or component property for changes - **Throttling support**: Prevents excessive triggering with `throttleWaitInMs` for rapid changes - **Previous/new values**: Access both old and new values in the change handler - **Reactive patterns**: Coordinates between components or triggers side effects %-DESC-END %-PROP-START listenTo The following sample demonstrates using this property. Every time the user clicks the button, a counter is incremented. The `ChangeListener` component watches the counter's value. Whenever it changes, the component fires the `didChange` event, which stores whether the new counter value is even into the `isEven` variable. ```xmlui-pg copy display name="Example: listenTo" <App var.counter="{0}" var.isEven="{false}"> <Button label="Increment counter" onClick="{counter++}" /> <ChangeListener listenTo="{counter}" onDidChange="isEven = counter % 2 == 0" /> <Text>Counter is {counter} which {isEven? "is": "isn't"} even.</Text> </App> ``` %-PROP-END %-PROP-START throttleWaitInMs The following example works like the previous one (in the `listen` prop's description). However, the user can reset or set the throttling time to 3 seconds. You can observe that while the throttling time is 3 seconds, the counter increments on every click, but `isEven` only refreshes once within 3 seconds. ```xmlui-pg copy display name="Example: throttleWaitInMs" <App var.counter="{0}" var.isEven="{false}" var.throttle="{0}"> <HStack> <Button label="Increment counter" onClick="{counter++}" /> <Button label="Set 3 sec throttling" onClick="throttle = 3000" /> <Button label="Reset throttling" onClick="throttle = 0" /> </HStack> <ChangeListener listenTo="{counter}" throttleWaitInMs="{throttle}" onDidChange="isEven = counter % 2 == 0" /> <Text>Counter is {counter} which {isEven? "is": "isn't"} even.</Text> </App> ``` %-PROP-END %-EVENT-START didChange This event is fired when the component observes a value change (within the specified throttling interval). Define the event handler that responds to that change (as the previous samples demonstrate). The event argument is an object with `prevValue` and `newValue` properties that (as their name suggests) contain the previous and the new values. %-EVENT-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/AppState/AppStateNative.tsx: -------------------------------------------------------------------------------- ```typescript import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs"; import type { AsyncFunction } from "../../abstractions/FunctionDefs"; import { useIsomorphicLayoutEffect } from "../../components-core/utils/hooks"; import { useAppStateContextPart } from "../../components/App/AppStateContext"; export const defaultProps: Pick<{ bucket?: string }, "bucket"> = { bucket: "default", }; type Props = { bucket?: string; initialValue: Record<string, any>; updateState: UpdateStateFn; registerComponentApi: RegisterComponentApiFn; onDidUpdate?: AsyncFunction; }; export function AppState({ bucket = defaultProps.bucket, updateState, initialValue, registerComponentApi, onDidUpdate, }: Props) { const update = useAppStateContextPart((value) => value.update); const value = useAppStateContextPart((value) => value?.appState?.[bucket]); useIsomorphicLayoutEffect(() => { if (initialValue !== undefined) { update(bucket, initialValue); } }, [bucket, initialValue]); useIsomorphicLayoutEffect(() => { updateState({ value }); // Fire the didUpdate event when value changes if (onDidUpdate) { onDidUpdate({ bucket, value, previousValue: undefined }); // Note: previousValue tracking could be added if needed } }, [updateState, value, onDidUpdate, bucket]); useIsomorphicLayoutEffect(() => { registerComponentApi({ update: (patch) => update(bucket, patch), appendToList: (key: string, id: any) => { const currentState = value || {}; const currentArray = currentState[key] || []; // Only add if the id doesn't already exist in the array if (!currentArray.includes(id)) { const newArray = [...currentArray, id]; update(bucket, { [key]: newArray }); } }, removeFromList: (key: string, id: any) => { const currentState = value || {}; const currentArray = currentState[key] || []; const newArray = currentArray.filter((item: any) => item !== id); update(bucket, { [key]: newArray }); }, listIncludes: (key: string, id: any) => { const currentState = value || {}; const currentArray = currentState[key] || []; return currentArray.includes(id); }, }); }, [bucket, registerComponentApi, update, value]); return null; } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Link/LinkNative.tsx: -------------------------------------------------------------------------------- ```typescript import { type CSSProperties, type ForwardedRef, forwardRef, type HTMLAttributeReferrerPolicy, type ReactNode, useMemo, } from "react"; import { Link } from "@remix-run/react"; import classnames from "classnames"; import styles from "./Link.module.scss"; import type { LinkTarget } from "../abstractions"; import { createUrlWithQueryParams } from "../component-utils"; import { Icon } from "../Icon/IconNative"; import type { To } from "react-router-dom"; // ===================================================================================================================== // React Link component implementation type Props = { to: string | { pathname: string; queryParams?: Record<string, any> }; children: ReactNode; icon?: string; active?: boolean; disabled?: boolean; onClick?: () => void; style?: CSSProperties; className?: string; } & Partial< Pick< HTMLAnchorElement, "hreflang" | "rel" | "download" | "target" | "referrerPolicy" | "ping" | "type" > >; export const defaultProps: Pick<Props, "active" | "disabled"> = { active: false, disabled: false, }; export const LinkNative = forwardRef(function LinkNative( props: Props, forwardedRef: ForwardedRef<HTMLDivElement>, ) { const { to, children, icon, active = defaultProps.active, onClick, target, disabled = defaultProps.disabled, style, className, ...anchorProps } = specifyTypes(props); const iconLink = !!icon && !children; const smartTo = useMemo(() => { return createUrlWithQueryParams(to); }, [to]) as To; const Node = to ? Link : "div"; return ( <Node {...anchorProps} ref={forwardedRef as any} to={smartTo} style={style} target={target} onClick={onClick} className={classnames(className, styles.container, { [styles.iconLink]: iconLink, [styles.active]: active, [styles.disabled]: disabled, })} > {icon && ( <div className={styles.iconWrapper}> <Icon name={icon} /> </div> )} {children} </Node> ); }); /** * Converts generic types to more specific ones. */ function specifyTypes(props: Props) { const { target, referrerPolicy } = props; return { ...props, target: target as LinkTarget, referrerPolicy: referrerPolicy as HTMLAttributeReferrerPolicy, }; } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Card/CardNative.tsx: -------------------------------------------------------------------------------- ```typescript import { type CSSProperties, type ForwardedRef, type ReactNode, forwardRef } from "react"; import classnames from "classnames"; import styles from "./Card.module.scss"; import { Avatar } from "../Avatar/AvatarNative"; import { LinkNative } from "../Link/LinkNative"; import type { HeadingProps } from "../Heading/HeadingNative"; import { Heading } from "../Heading/HeadingNative"; import { Text } from "../Text/TextNative"; type Props = { style?: CSSProperties; className?: string; children?: ReactNode; title?: string; subtitle?: string; linkTo?: string; avatarUrl?: string; showAvatar?: boolean; avatarSize?: string; orientation?: string; onClick?: any; }; export const defaultProps: Pick<Props, "orientation" | "showAvatar"> = { orientation: "vertical", showAvatar: false, }; export const Card = forwardRef(function Card( { children, orientation = defaultProps.orientation, style, className, title, subtitle, linkTo, avatarUrl, showAvatar = !!avatarUrl || defaultProps.showAvatar, avatarSize, onClick, ...rest }: Props, forwardedRef: ForwardedRef<HTMLDivElement>, ) { const titleProps: Partial<HeadingProps> = { level: "h2", maxLines: 1, }; return ( <div {...rest} ref={forwardedRef} className={classnames( styles.wrapper, { [styles.isClickable]: !!onClick, [styles.vertical]: orientation === "vertical", [styles.horizontal]: orientation === "horizontal", }, className, )} style={style} onClick={onClick} > {[title, subtitle, avatarUrl, showAvatar].some(Boolean) && ( <div className={styles.avatarWrapper}> {showAvatar && <Avatar url={avatarUrl} name={title} size={avatarSize} />} <div className={styles.titleWrapper}> {linkTo ? ( title ? ( <LinkNative to={linkTo + ""}> <Heading {...titleProps}>{title}</Heading> </LinkNative> ) : null ) : title ? ( <Heading {...titleProps}>{title}</Heading> ) : null} {subtitle !== undefined && ( <> <Text variant="secondary">{subtitle}</Text> </> )} </div> </div> )} {children} </div> ); }); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Badge/Badge.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Dynamic color mapping**: Automatically applies colors based on the badge value (e.g., status states) - **Two shape variants**: Choose between `badge` (rounded corners) or `pill` (fully rounded) - **Flexible color control**: Set just background color or customize both background and text colors %-DESC-END %-PROP-START value ```xmlui-pg copy name="Example: value" <App> <Badge value="Example value" /> <Badge value="Example badge"> Example Child </Badge> <Badge /> </App> ``` %-PROP-END %-PROP-START variant ```xmlui-pg copy display name="Example: variant" <App> <Badge value="Example badge" variant="badge" /> <Badge value="Example pill" variant="pill" /> </App> ``` %-PROP-END %-PROP-START colorMap Provide the component with a list or key-value pairs in two ways: 1. Only change the background color ```xmlui-pg copy {2} name="Example: only background color" <App var.simpleColorMap="{{ important: 'red', regular: 'blue', unimportant: 'black' }}"> <Badge value="important" colorMap="{simpleColorMap}" /> </App> ``` 2. Change the background and label color ```xmlui-pg copy display {2-5} name="Example: background and label color" <App var.simpleColorMap="{{ important: { label: 'red', background: 'pink' }, unimportant: { label: 'black', background: 'gray' } }}"> <Badge value="important" colorMap="{simpleColorMap}" /> <Badge value="unimportant" colorMap="{simpleColorMap}" /> <Badge value="other" colorMap="{simpleColorMap}" /> </App> ``` %-PROP-END %-PROP-START indicatorPosition The value of this optional property sets the string to provide a color scheme for the Alert. | Value | Description | | :------------- | :------------------------------------------------------------------- | | `start` | The indicator is displayed within the badge before its text | | `end` | The indicator is displayed within the badge after its text | | `top-start` | The indicator is displayed over the badge in the top-start corner | | `top-end` | The indicator is displayed over the badge in the top-end corner | | `bottom-start` | The indicator is displayed over the badge in the bottom-start corner | | `bottom-end` | The indicator is displayed over the badge in the bottom-end corner | %-PROP-END ``` -------------------------------------------------------------------------------- /packages/xmlui-animations/src/FadeInAnimation.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer, createMetadata } from "xmlui"; import { Animation, defaultProps } from "./AnimationNative"; const COMP = "FadeInAnimation"; const defaultAnimationValues = { from: 0, to: 1, }; export const FadeInAnimationMd = createMetadata({ description: `The \`${COMP}\` component represents an animation that fades in the content.`, status: "experimental", docFolder: "src", props: { animateWhenInView: { description: `Indicates whether the animation should start when the component is in view`, valueType: "boolean", }, duration: { description: `The duration of the animation in milliseconds`, valueType: "number", }, reverse: { description: `Indicates whether the animation should run in reverse`, defaultValue: defaultProps.reverse, valueType: "boolean", }, loop: { description: `Indicates whether the animation should loop`, defaultValue: defaultProps.loop, valueType: "boolean", }, delay: { description: `The delay before the animation starts in milliseconds`, defaultValue: defaultProps.delay, valueType: "number", }, }, events: { started: { description: `Event fired when the animation starts` }, stopped: { description: `Event fired when the animation stops` }, }, apis: { start: { description: `Starts the animation` }, stop: { description: `Stops the animation` }, }, }); export const fadeInAnimationRenderer = createComponentRenderer( "FadeInAnimation", FadeInAnimationMd, ({ node, renderChild, extractValue, registerComponentApi, lookupEventHandler }) => { return ( <Animation registerComponentApi={registerComponentApi} animation={{ from: { opacity: defaultAnimationValues.from }, to: { opacity: defaultAnimationValues.to }, }} duration={extractValue.asOptionalNumber(node.props.duration)} onStop={lookupEventHandler("stopped")} onStart={lookupEventHandler("started")} animateWhenInView={extractValue.asOptionalBoolean(node.props.animateWhenInView)} reverse={extractValue.asOptionalBoolean(node.props.reverse)} loop={extractValue.asOptionalBoolean(node.props.loop)} delay={extractValue.asOptionalNumber(node.props.delay)} > {renderChild(node.children)} </Animation> ); }, ); ``` -------------------------------------------------------------------------------- /docs/public/resources/files/tutorials/p2do/todo-logo.svg: -------------------------------------------------------------------------------- ``` <svg width="63" height="23" viewBox="0 0 63 23" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M21.5 17.75C24.9518 17.75 27.75 14.9518 27.75 11.5C27.75 8.04822 24.9518 5.25 21.5 5.25C18.0482 5.25 15.25 8.04822 15.25 11.5C15.25 14.9518 18.0482 17.75 21.5 17.75Z" stroke="#106CBD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M21.5 7.75V11.5L24 12.75" stroke="#106CBD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12.7129 5.96875H8.68945V18H6.36523V5.96875H2.35156V3.99609H12.7129V5.96875Z" fill="#106CBD"/> <path d="M30.7285 18V3.99609H34.7715C39.9342 3.99609 42.5156 6.27148 42.5156 10.8223C42.5156 12.9837 41.7995 14.722 40.3672 16.0371C38.9349 17.3457 37.0143 18 34.6055 18H30.7285ZM33.043 5.96875V16.0371H34.8984C36.5326 16.0371 37.8021 15.5879 38.707 14.6895C39.6185 13.791 40.0742 12.5215 40.0742 10.8809C40.0742 7.60612 38.3783 5.96875 34.9863 5.96875H33.043Z" fill="#106CBD"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M53.6368 6.70093C52.6026 6.24016 51.4473 6.12601 50.343 6.3755C49.2387 6.625 48.2447 7.22478 47.5091 8.08539C46.7735 8.946 46.3359 10.0213 46.2614 11.151C46.1869 12.2807 46.4796 13.4041 47.0958 14.3539C47.7119 15.3036 48.6186 16.0287 49.6806 16.4211C50.7426 16.8134 51.9029 16.852 52.9886 16.531C54.0742 16.21 55.027 15.5466 55.7049 14.6399C56.3828 13.7331 56.7494 12.6316 56.75 11.4994V10.925C56.75 10.3727 57.1977 9.925 57.75 9.925C58.3023 9.925 58.75 10.3727 58.75 10.925V11.5C58.7491 13.0634 58.2429 14.5852 57.3068 15.8374C56.3707 17.0896 55.0549 18.0056 53.5556 18.4489C52.0564 18.8922 50.454 18.839 48.9875 18.2971C47.521 17.7553 46.2689 16.754 45.4179 15.4424C44.567 14.1309 44.1628 12.5794 44.2657 11.0194C44.3686 9.45938 44.973 7.97441 45.9888 6.78595C47.0045 5.59749 48.3773 4.76922 49.9022 4.42468C51.4272 4.08013 53.0227 4.23777 54.4508 4.87407C54.9552 5.09885 55.182 5.69003 54.9572 6.1945C54.7324 6.69897 54.1412 6.92571 53.6368 6.70093Z" fill="#106CBD"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M58.2801 5.9694C58.5731 6.26215 58.5733 6.73703 58.2806 7.03007L52.0306 13.2863C51.89 13.4271 51.6992 13.5062 51.5002 13.5062C51.3012 13.5063 51.1104 13.4273 50.9697 13.2866L49.0947 11.4116C48.8018 11.1187 48.8018 10.6438 49.0947 10.3509C49.3876 10.058 49.8624 10.058 50.1553 10.3509L51.4997 11.6953L57.2194 5.96993C57.5122 5.6769 57.987 5.67666 58.2801 5.9694Z" fill="#106CBD"/> </svg> ``` -------------------------------------------------------------------------------- /docs/public/pages/tutorial-05.md: -------------------------------------------------------------------------------- ```markdown # Charts The `Dashboard` page continues with a donut chart that visualizes some of the same facts reported using `InfoCard`. We define it as a `Statuses` component whose width and title are defined by its containing `Dashboard` component. Here we'll use it standalone. ```xmlui-pg noHeader ---app display <App> <Statuses /> </App> ---desc `Statuses` uses three critical properties of `DonutChart`. - `data`: The ubiquitous attribute that refers to data that may be defined literally or, as in this case, via an API call backed by a database query. - `dataKey`: The object key that holds data. - `nameKey`: The object key whose value is the data label. ---comp display copy /dataKey/ /nameKey/ <Component name="Statuses"> <DataSource id="dashboardStats" url="/resources/files/dashboard-stats.json" method="GET" /> <Card title="Statuses" height="400px" width="{$props.width}"> <DonutChart data="{ [ { name: 'sent', value: dashboardStats.value[0].sent_invoices }, { name: 'draft', value: dashboardStats.value[0].draft_invoices }, { name: 'paid', value: dashboardStats.value[0].paid_invoices }, ] }" dataKey="value" nameKey="name" /> </Card> </Component> ``` ## Multiseries charts [PieChart](/components/PieChart) and [DonutChart](/components/DonutChart) work with a single series of data and use `dataKey`. [BarChart](/components/BarChart) and [LineChart](/components/LineChart) can display multiple series denoted by `yKeys`. We see that in the `MonthlyStatus` chart. ```xmlui-pg /data/ noHeader ---app display /data/ /xKey/ /yKeys/ <App> <MonthlyStatus /> </App> ---comp display /data/ /yKeys/ <Component name="MonthlyStatus"> <DataSource id="monthlyStatus" url="/resources/files/monthly-status.json" method="GET" /> <VStack width="{$props.width}"> <H1>{$props.title}</H1> <Card height="400px"> <BarChart orientation="horizontal" data="{ monthlyStatus }" xKey="month" yKeys="{['paid_revenue', 'sent_revenue']}" stacked="true" showLegend="true" tickFormatter="{(value) => { return window.formatMonth(value); }}" /> </Card> </VStack> </Component> ``` ``` -------------------------------------------------------------------------------- /packages/xmlui-animations/src/FadeOutAnimation.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer, createMetadata } from "xmlui"; import { Animation, defaultProps } from "./AnimationNative"; const COMP = "FadeOutAnimation"; const defaultAnimationValues = { from: 1, to: 0, }; export const FadeOutAnimationMd = createMetadata({ description: `The \`${COMP}\` component represents an animation that fades out the content.`, status: "experimental", docFolder: "src", props: { animateWhenInView: { description: `Indicates whether the animation should start when the component is in view`, valueType: "boolean", }, duration: { description: `The duration of the animation in milliseconds`, valueType: "number", }, reverse: { description: `Indicates whether the animation should run in reverse`, defaultValue: defaultProps.reverse, valueType: "boolean", }, loop: { description: `Indicates whether the animation should loop`, defaultValue: defaultProps.loop, valueType: "boolean", }, delay: { description: `The delay before the animation starts in milliseconds`, defaultValue: defaultProps.delay, valueType: "number", }, }, events: { started: { description: `Event fired when the animation starts` }, stopped: { description: `Event fired when the animation stops` }, }, apis: { start: { description: `Starts the animation` }, stop: { description: `Stops the animation` }, }, }); export const fadeOutAnimationRenderer = createComponentRenderer( "FadeOutAnimation", FadeOutAnimationMd, ({ node, renderChild, extractValue, registerComponentApi, lookupEventHandler }) => { return ( <Animation registerComponentApi={registerComponentApi} animation={{ from: { opacity: defaultAnimationValues.from }, to: { opacity: defaultAnimationValues.to }, }} duration={extractValue.asOptionalNumber(node.props.duration)} onStop={lookupEventHandler("stopped")} onStart={lookupEventHandler("started")} animateWhenInView={extractValue.asOptionalBoolean(node.props.animateWhenInView)} reverse={extractValue.asOptionalBoolean(node.props.reverse)} loop={extractValue.asOptionalBoolean(node.props.loop)} delay={extractValue.asOptionalNumber(node.props.delay)} > {renderChild(node.children)} </Animation> ); }, ); ``` -------------------------------------------------------------------------------- /docs/content/components/PieChart.md: -------------------------------------------------------------------------------- ```markdown # PieChart [#piechart] `PieChart` visualizes proportional data as circular segments; each slice represents a percentage of the whole. Note that the height of the component or its parent needs to be set explicitly. **Key features:** - **Proportional visualization**: Displays data segments as slices of a complete circle - **Flexible labeling**: Configurable label positions both inside and outside chart segments - **Data binding**: Connects to array data with specified keys for values and labels - **Label list display**: Optional legend-style list showing all segments and values - **Customizable sizing**: Configurable dimensions and outer radius for different layouts For a variation with a hollow center, see [DonutChart](/components/DonutChart). ## Properties [#properties] ### `data` [#data] The data to be displayed in the chart. Needs to be an array of objects. ### `dataKey` [#datakey] This property specifies the key in the data objects that will be used to render the chart. ### `labelListPosition` (default: "inside") [#labellistposition-default-inside] The position of the label list. Available values: `top`, `left`, `right`, `bottom`, `inside` **(default)**, `outside`, `insideLeft`, `insideRight`, `insideTop`, `insideBottom`, `insideTopLeft`, `insideBottomLeft`, `insideTopRight`, `insideBottomRight`, `insideStart`, `insideEnd`, `end`, `center`, `centerTop`, `centerBottom`, `middle` ### `nameKey` [#namekey] Specifies the key in the data objects that will be used to label the different data series. ### `outerRadius` [#outerradius] The outer radius of the pie chart, can be a number or a string (e.g., '100%'). ### `showLabel` (default: true) [#showlabel-default-true] Toggles whether to show labels (`true`) or not (`false`). ### `showLabelList` (default: false) [#showlabellist-default-false] Whether to show labels in a list (`true`) or not (`false`). ### `showLegend` (default: false) [#showlegend-default-false] Toggles whether to show legend (`true`) or not (`false`). ## Events [#events] This component does not have any events. ## Exposed Methods [#exposed-methods] This component does not expose any methods. ## Styling [#styling] ### Theme Variables [#theme-variables] | Variable | Default Value (Light) | Default Value (Dark) | | --- | --- | --- | | [textColor](../styles-and-themes/common-units/#color)-labelList-PieChart | $textColor-primary | $textColor-primary | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Pages/Pages.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer } from "../../components-core/renderers"; import { TableOfContentsProvider } from "../../components-core/TableOfContentsContext"; import { createMetadata, d, dInternal } from "../metadata-helpers"; import { Pages, RouteWrapper, defaultProps } from "./PagesNative"; import { extractPaddings } from "../../components-core/utils/css-utils"; const PAGE = "Page"; export const PageMd = createMetadata({ status: "stable", docFolder: "Pages", description: "`Page` defines route endpoints within an application, mapping specific URL " + "patterns to content that displays when users navigate to those routes. Each " + "Page represents a distinct view or screen in your single-page application's " + "routing system.", props: { //TODO illesg rename to path url: d( `The URL of the route associated with the content. If not set, the page is not available.`, ), navLabel: dInternal( "The label of the page that is displayed in the navigation panel. If provided, the " + "a new entry will be added to the navigation panel.", ), }, }); export const pageRenderer = createComponentRenderer( PAGE, PageMd, ({ node, extractValue, renderChild, className }) => { const paddings = extractPaddings(extractValue, node.props); return ( <TableOfContentsProvider> <RouteWrapper childRoute={node.children} uid={node.uid} renderChild={renderChild} key={extractValue(node.props.url)} className={className} {...paddings} /> </TableOfContentsProvider> ); }, ); const COMP = "Pages"; export const PagesMd = createMetadata({ status: "stable", description: "`Pages` serves as the routing coordinator within an [App](/components/App), " + "managing which [Page](/components/Page) displays based on the current URL.", props: { fallbackPath: { description: `The fallback path when the current URL does not match any of the paths of the pages.`, defaultValue: defaultProps.fallbackPath, }, }, }); export const pagesRenderer = createComponentRenderer( COMP, PagesMd, ({ node, extractValue, renderChild }) => { return ( <Pages fallbackPath={extractValue(node.props.fallbackPath)} node={node} renderChild={renderChild} extractValue={extractValue} /> ); }, ); ``` -------------------------------------------------------------------------------- /packages/xmlui-animations/src/Animation.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer, createMetadata } from "xmlui"; import { Animation, defaultProps } from "./AnimationNative"; const COMP = "Animation"; export const AnimationMd = createMetadata({ status: "experimental", description: `The \`${COMP}\` component represents a generic animation wrapper that can apply various animations to its children.`, docFolder: "src", props: { animation: { description: `The animation object to be applied to the component`, }, animateWhenInView: { description: `Indicates whether the animation should start when the component is in view`, }, duration: { description: `The duration of the animation in milliseconds`, }, once: { description: `Indicates whether the animation should only run once`, defaultValue: defaultProps.once, }, reverse: { description: `Indicates whether the animation should run in reverse`, defaultValue: defaultProps.reverse, }, loop: { description: `Indicates whether the animation should loop`, defaultValue: defaultProps.loop, }, delay: { description: `The delay before the animation starts in milliseconds`, defaultValue: defaultProps.delay, }, }, events: { started: { description: `Event fired when the animation starts` }, stopped: { description: `Event fired when the animation stops` }, }, apis: { start: { description: `Starts the animation` }, stop: { description: `Stops the animation` }, }, }); export const animationComponentRenderer = createComponentRenderer( COMP, AnimationMd, ({ registerComponentApi, renderChild, node, extractValue, lookupEventHandler }) => { return ( <Animation registerComponentApi={registerComponentApi} animation={extractValue(node.props.animation)} onStop={lookupEventHandler("stopped")} onStart={lookupEventHandler("started")} duration={extractValue.asOptionalNumber(node.props.duration)} animateWhenInView={extractValue.asOptionalBoolean(node.props.animateWhenInView)} once={extractValue.asOptionalBoolean(node.props.once)} reverse={extractValue.asOptionalBoolean(node.props.reverse)} loop={extractValue.asOptionalBoolean(node.props.loop)} delay={extractValue.asOptionalNumber(node.props.delay)} > {renderChild(node.children)} </Animation> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/ResponsiveBar/ResponsiveBar.module.scss: -------------------------------------------------------------------------------- ```scss @use "../../components-core/theming/themes" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } // Define theme variables $backgroundColor-ResponsiveBar: createThemeVar("backgroundColor-ResponsiveBar"); $padding-ResponsiveBar: createThemeVar("padding-ResponsiveBar"); $margin-ResponsiveBar: createThemeVar("margin-ResponsiveBar"); @layer components { // --- This part defines the CSS styles .responsiveBar { display: flex; flex-direction: row; flex-shrink: 0; flex-grow: 0; align-items: center; background-color: $backgroundColor-ResponsiveBar; padding: $padding-ResponsiveBar; margin: $margin-ResponsiveBar; width: 100%; overflow: visible; // Allow dropdown to be visible &.vertical { flex-direction: column; height: 100%; width: auto; align-items: stretch; max-height: 100%; // Ensure it doesn't exceed parent height } &.horizontal { flex-direction: row; width: 100%; height: auto; align-items: center; } .visibleItems { display: flex; flex-direction: row; align-items: center; flex-shrink: 0; flex-grow: 1; /* Allow this container to grow and push dropdown to the end */ min-width: 0; overflow: hidden; // Hide overflow of items, but not the dropdown &.vertical { flex-direction: column; align-items: stretch; min-height: 0; min-width: auto; flex-grow: 1; // Take up available space width: auto; overflow: hidden; // Critical: hide overflow in vertical mode } &.horizontal { flex-direction: row; align-items: center; min-width: 0; min-height: auto; width: auto; height: auto; } } .overflowDropdown { flex-shrink: 0; width: auto; min-width: 40px; /* Consistent width for calculation */ position: relative; /* Ensure dropdown can position correctly */ z-index: 1000; /* Ensure dropdown appears above other content */ } } } // --- We export the theme variables to add them to the component renderer :export { themeVars: t.json-stringify($themeVars); } ``` -------------------------------------------------------------------------------- /xmlui/dev-docs/helper-components.md: -------------------------------------------------------------------------------- ```markdown # Helper Components The following React components contribute to the rendering engine's job: - `ApiBoundComponent`: This component handles special components used as event handlers. You can declare an event handler not only as script code (string) but also as one of these components: `FileUpload`, `FileDownload`, and - `APICall`. `ApiBoundComponent` turns them into script code. - `AppContext`: Stores the object that holds the global functions and properties of xmlui. It provides a `useAppContext` hook that xmlui components can use to access the global functions and properties. - `ComponentDecorator`: This component decorates the DOM element of a component with a set of attributes. We use this feature to assign helper attributes to the app's xmlui component nodes for testing, debugging, and other development-related purposes. - `ErrorBoundary`: This component is an error boundary, catching errors within the nested components. `ErrorBoundary uses a React class component rather than a React function. - `InspectorProvider`: We allow inspection of the xmlui components of an app (as a fundamental feature of debugging and development tooling). This component wraps each rendered xmlui component with a context that allows access to run time information and inspection functionality. - `InvalidComponent`: This component displays run time errors found while the rendering engine runs. If it finds an issue that hinders regular operation, it renders this component instead of the faulty one. - `LoaderComponent`: While running an app, some information (such as fetching data from the backend) requires an async "loading" operation. This component manages the life cycle of information loading and provides an API to query and manage the operation's status. - `RestApiProxy`: This object provides operations to manage the life cycle of a REST-like backend call, including the entire request-response scenario. - `ScrollContext`: Allows storing context information for components that support scrolling. - `TableOfContentsContext`: Several components work together to represent the hierarchy of a particular app page as a TOC. This React component provides a context for storing this hierarchy information. The `useTableOfContents` hook allows access to this context. - `UnknownComponent`: When the rendering engine finds an unknown (unregistered) component in the markup, it renders this component and names the unregistered. ``` -------------------------------------------------------------------------------- /tools/vscode/package.json: -------------------------------------------------------------------------------- ```json { "name": "xmlui-vscode", "displayName": "xmlui", "description": "XMLUI language support", "icon": "resources/xmlui-logo.png", "author": "xmlui.org", "publisher": "xmlui", "license": "MIT", "version": "0.10.19", "private": true, "categories": [], "keywords": [], "repository": { "type": "git", "url": "https://github.com/xmlui-com/xmlui" }, "engines": { "vscode": "^1.75.0" }, "activationEvents": [ "onLanguage:xmlui" ], "vsce": { "dependencies": false }, "main": "./dist/extension", "contributes": { "configuration": { "type": "object", "title": "XMLUI configuration", "properties": { "XMLUILanguageService.trace.server": { "scope": "window", "type": "string", "enum": [ "off", "messages", "verbose" ], "default": "off", "description": "Traces the communication between VS Code and the language server." } } }, "languages": [ { "id": "xmlui", "aliases": [ "XMLUI", "Xmlui" ], "extensions": [ ".xmlui" ] }, { "id": "javascript", "aliases": [ "XS", "xs" ], "extensions": [ ".xs" ] } ], "grammars": [ { "language": "xmlui", "scopeName": "source.xmlui", "path": "./syntaxes/xmlui.tmLanguage.json", "embeddedLanguages": { "meta.embedded.block.javascrip": "javascript" } } ] }, "scripts": { "build:vsix": "vsce package", "build": "node esbuild.js --production", "build:all": "./build.sh", "type-check": "tsc --noEmit", "watch": "npm-run-all -p watch:*", "watch:esbuild": "node esbuild.js --watch", "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", "format-test": "node ./dist/extension.js --test ./test-samples/complex-sample.xmlui" }, "dependencies": { "vscode-languageclient": "^9.0.1", "xmlui": "*" }, "devDependencies": { "@eslint/js": "^9.13.0", "@stylistic/eslint-plugin": "^2.9.0", "@types/node": "^20", "@types/vscode": "^1.75.1", "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.1", "eslint": "^9.13.0", "npm-run-all": "^4.1.5", "typescript": "^5.7.2", "typescript-eslint": "^8.16.0" } } ``` -------------------------------------------------------------------------------- /xmlui/tests-e2e/modify-array-item-regression.spec.ts: -------------------------------------------------------------------------------- ```typescript import { expect, test } from "../src/testing/fixtures"; test("modify simple array item", async ({ page, initTestBed }) => { await initTestBed(` <Fragment var.fruits="{[{ name: 'apple', size: 'large' }]}" > <Button id="modifyButton" onClick="fruits[0].size = 'small'">Convert to small</Button> <Text id="fruits_text">{JSON.stringify(fruits)}</Text> </Fragment> `); await expect(page.getByTestId("fruits_text")).toHaveText( JSON.stringify([{ name: "apple", size: "large" }]), ); await page.getByTestId("modifyButton").click(); await expect(page.getByTestId("fruits_text")).toHaveText( JSON.stringify([{ name: "apple", size: "small" }]), ); }); test("modify simple array item 2", async ({ page, initTestBed }) => { await initTestBed(` <Fragment var.fruits="{[{ name: 'apple', size: 'large' }, {name: 'pear', size: 'somethingelse'}]}"> <Button id="modifyButton" onClick="fruits[1].size = 'small'">fruits[1].size = 'small'</Button> <Text id="fruits_text">{JSON.stringify(fruits)}</Text> </Fragment> `); await expect(page.getByTestId("fruits_text")).toHaveText( JSON.stringify([ { name: "apple", size: "large" }, { name: "pear", size: "somethingelse" }, ]), ); await page.getByTestId("modifyButton").click(); await expect(page.getByTestId("fruits_text")).toHaveText( JSON.stringify([ { name: "apple", size: "large" }, { name: "pear", size: "small" }, ]), ); }); test("modify simple array item 3", async ({ page, initTestBed }) => { await initTestBed(` <Fragment var.fruits="{ [{ name: 'apple', size: 'large', seeds: [{name: 'seed1', size: 'large'}] }, {name: 'pear', size: 'somethingelse'}] }"> <Button id="modifyButton" onClick="fruits[0].seeds[0].size = 'small'"> fruits[0].seeds[0].size = 'small' </Button> <Text id="fruits_text">{JSON.stringify(fruits)}</Text> </Fragment> `); await expect(page.getByTestId("fruits_text")).toHaveText( JSON.stringify([ { name: "apple", size: "large", seeds: [{ name: "seed1", size: "large" }] }, { name: "pear", size: "somethingelse" }, ]), ); await page.getByTestId("modifyButton").click(); await expect(page.getByTestId("fruits_text")).toHaveText( JSON.stringify([ { name: "apple", size: "large", seeds: [{ name: "seed1", size: "small" }] }, { name: "pear", size: "somethingelse" }, ]), ); }); ``` -------------------------------------------------------------------------------- /blog/src/themes/docs-theme.ts: -------------------------------------------------------------------------------- ```typescript import type { ThemeDefinition } from "xmlui"; export const DefaultDocsTheme: ThemeDefinition = { name: "XMLUI Documentation Theme", id: "docs-theme", extends: ["xmlui"], themeVars: { // --- Fundamental colors & typography backgroundColor: "$color-surface-0", "color-primary": "#3367CC", "color-surface": "#1e2734", "backgroundColor-content-App": "$color-surface-0", // --- App layout "maxWidth-App": "1320px", "boxShadow-navPanel-App": "none", // --- We intentionally use different theming to amplify the "documentation" feel // --- We use different navigation panel theming "backgroundColor-NavPanel": "$color-surface-50", "backgroundColor-navPanel-App": "$color-surface-50", "paddingVertical-NavPanel": "$space-5", "borderRightWidth-NavPanel": "1px", "maxWidth-Drawer": "100%", "textColor-NavLink": "$color-secondary-600", "color-indicator-NavLink--active": "transparent", "color-indicator-NavLink--hover": "transparent", "color-indicator-NavLink--pressed": "transparent", "backgroundColor-NavLink--hover": "$color-surface-100", "backgroundColor-NavLink--pressed": "$color-surface-100", "fontWeight-NavLink--active": "bold", "textColor-NavLink--hover": "$color-surface-900", "textColor-NavLink--active": "$color-primary-500", "textColor-NavLink--hover--active": "$color-primary-500", // --- Adjust a little bit of Markdown "fontSize-H1": "1.65rem", "fontWeight-H1": "700", "borderRadius-HtmlTable": "30px", light: { // --- Use these colors for light-tone document links "textColor-DocumentLinks": "#5B6475", "textColor-DocumentLinks--hover": "#1B232A", "backgroundColor-separator-DocumentLinks": "#e2e5ea", }, dark: { // --- These colors match better with the dark tone "backgroundColor-content-App": "$color-surface-100", backgroundColor: "$color-surface-100", "backgroundColor-NavPanel": "$color-surface-50", "backgroundColor-navPanel-App": "$color-surface-50", "border-NestedApp": "1px solid $color-surface-200", "backgroundColor-HtmlThead": "$color-surface-50", // --- Use these colors for dark-tone document links "textColor-DocumentLinks": "#8E97A8", "textColor-DocumentLinks--hover": "#F9FAFB", "backgroundColor-separator-DocumentLinks": "#38475E", }, }, resources: {}, }; export default DefaultDocsTheme; ``` -------------------------------------------------------------------------------- /docs/src/themes/docs-theme.ts: -------------------------------------------------------------------------------- ```typescript import type { ThemeDefinition } from "xmlui"; export const DefaultDocsTheme: ThemeDefinition = { name: "XMLUI Documentation Theme", id: "docs-theme", extends: ["xmlui"], themeVars: { // --- Fundamental colors & typography backgroundColor: "$color-surface-0", "color-primary": "#3367CC", "color-surface": "#1e2734", "backgroundColor-content-App": "$color-surface-0", // --- App layout "maxWidth-App": "1320px", "boxShadow-navPanel-App": "none", // --- We intentionally use different theming to amplify the "documentation" feel // --- We use different navigation panel theming "backgroundColor-NavPanel": "$color-surface-50", "backgroundColor-navPanel-App": "$color-surface-50", "paddingVertical-NavPanel": "$space-5", "borderRightWidth-NavPanel": "1px", "maxWidth-Drawer": "100%", "textColor-NavLink": "$color-secondary-600", "color-indicator-NavLink--active": "transparent", "color-indicator-NavLink--hover": "transparent", "color-indicator-NavLink--pressed": "transparent", "backgroundColor-NavLink--hover": "$color-surface-100", "backgroundColor-NavLink--pressed": "$color-surface-100", "fontWeight-NavLink--active": "bold", "textColor-NavLink--hover": "$color-surface-900", "textColor-NavLink--active": "$color-primary-500", "textColor-NavLink--hover--active": "$color-primary-500", // --- Adjust a little bit of Markdown "fontSize-H1": "1.65rem", "fontWeight-H1": "700", "borderRadius-HtmlTable": "30px", light: { // --- Use these colors for light-tone document links "textColor-DocumentLinks": "#5B6475", "textColor-DocumentLinks--hover": "#1B232A", "backgroundColor-separator-DocumentLinks": "#e2e5ea", }, dark: { // --- These colors match better with the dark tone "backgroundColor-content-App": "$color-surface-100", backgroundColor: "$color-surface-100", "backgroundColor-NavPanel": "$color-surface-50", "backgroundColor-navPanel-App": "$color-surface-50", "border-NestedApp": "1px solid $color-surface-200", "backgroundColor-HtmlThead": "$color-surface-50", // --- Use these colors for dark-tone document links "textColor-DocumentLinks": "#8E97A8", "textColor-DocumentLinks--hover": "#F9FAFB", "backgroundColor-separator-DocumentLinks": "#38475E", }, }, resources: {}, }; export default DefaultDocsTheme; ``` -------------------------------------------------------------------------------- /xmlui/src/components/Items/Items.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Simple iteration**: Maps data arrays to components using `$item`, `$itemIndex`, `$isFirst`, and `$isLast` context - **Layout agnostic**: No built-in styling or container—children determine the visual presentation - **Reverse ordering**: Optional `reverse` property to display data in opposite order - **Performance**: Lightweight alternative to `List` when you don't need virtualization or grouping >[!INFO] > `Items` is not a container! It does not wrap its items into a container; it merely renders its children. The `Items` component does not use virtualization; it maps each data item into a component. Thus, passing many items to a component instance will use many resources and slow down your app. If you plan to work with many items (more than a few dozen), use the [`List`](./List) and [`Table`](./Table) components instead. ### Inline Data You can set the list of data to be rendered via the `data` property, as the following sample shows. The nested child component describes a template to display each data entry in `Items`. In the template, you can refer to a particular entry with the [`$item`](#&item) identifier: ```xmlui-pg copy {8} display name="Example: inline data" <App> <VStack> <Items data="{[ { idx: 1, value: 'One lion' }, { idx: 2, value: 'Two monkeys' }, { idx: 3, value: 'Three rabbits' }, ]}"> <Text>{$item.idx} - {$item.value}</Text> </Items> </VStack> </App> ``` ### Data Binding You can use also API bindings to display data: ```xmlui-pg copy {4-6} display name="Example: data binding" <App> <VStack> <Items> <property name="data"> <DataSource url="https://api.spacexdata.com/v3/rockets"/> </property> <Image height="80px" width="110px" fit="cover" src="{$item.flickr_images[0]}"/> </Items> </VStack> </App> ``` %-DESC-END %-PROP-START reverse ```xmlui-pg copy {4} display name="Example: reverse" <App> <VStack> <Items reverse="true" data="{[ { idx: 1, value: 'One lion' }, { idx: 2, value: 'Two monkeys' }, { idx: 3, value: 'Three rabbits' }, ]}"> <Text>{$item.idx} - {$item.value}</Text> </Items> </VStack> </App> ``` %-PROP-END %-STYLE-START The `Items` component does not support styling. You should style the container component that wraps `Items`. You can also style the individual items via specifying a template component. %-STYLE-END ``` -------------------------------------------------------------------------------- /xmlui/src/components/ContentSeparator/ContentSeparator.md: -------------------------------------------------------------------------------- ```markdown %-DESC-START **Key features:** - **Flexible orientation**: Create horizontal dividers (default) or vertical dividers between content - **Customizable sizing**: Control thickness with the `size` property - **Automatic spacing**: Takes full width/height of container unless size is specified %-DESC-END %-PROP-START orientation See the demo for an example under [`size`](#size). >[!INFO] > The component will not be displayed if the orientation is set to `vertical` but the height of the parent container is not explicitly set to a value other than 0 or percentage is used as the size unit (e.g. 20%). > This is true even if the `ContentSeparator` has siblings in the container. > The demo below illustrates this. > Notice how the first `ContentSeparator` does not show up while the second does: ```xmlui-pg copy display name="Example: no vertical space" <App> <HStack horizontalAlignment="center"> <ContentSeparator orientation="vertical" size="8px" backgroundColor="blue" /> </HStack> <HStack horizontalAlignment="center" height="48px"> <ContentSeparator orientation="vertical" size="8px" backgroundColor="red" /> </HStack> </App> ``` %-PROP-END %-PROP-START size ```xmlui-pg copy display name="Example: size" <App> <Heading level="h2"> Lorem Ipsum </Heading> <ContentSeparator /> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. <ContentSeparator size="4px" /> <HStack height="120px"> Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <ContentSeparator orientation="vertical" size="10px" /> Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. </HStack> </App> ``` >[!INFO] > You can use the `width` and `height` layout properties to set the `ContentSeparator` dimensions. > For the horizontal separator, you can set the `height` property; the vertical separator offers the `width` property instead of `size`. > Nonetheless, we suggest you use the `size` property. %-PROP-END ``` -------------------------------------------------------------------------------- /packages/xmlui-search/src/Search.module.scss: -------------------------------------------------------------------------------- ```scss @use "xmlui/themes.scss" as t; // --- This code snippet is required to collect the theme variables used in this module $themeVars: (); @function createThemeVar($componentVariable) { $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; @return t.getThemeVar($themeVars, $componentVariable); } $componentContainer: "SearchContainer"; $themeVars: t.composePaddingVars($themeVars, $componentContainer); $componentListPanel: "SearchPanel"; $themeVars: t.composeBorderVars($themeVars, $componentListPanel); $themeVars: t.composePaddingVars($themeVars, $componentListPanel); $backgroundColor-Search: createThemeVar("backgroundColor-#{$componentListPanel}"); $boxShadow-Search: createThemeVar("boxShadow-#{$componentListPanel}"); $componentListItem: "SearchItem"; $borderRadius-SearchItem: createThemeVar("borderRadius-#{$componentListItem}"); $backgroundColor-SearchItem--hover: createThemeVar("backgroundColor-#{$componentListItem}--hover"); $borderColor-SearchItem--focus: createThemeVar("borderColor-#{$componentListItem}--focus"); .listPanel { @include t.borderVars($themeVars, $componentListPanel); @include t.paddingVars($themeVars, $componentListPanel); overflow: hidden; width: 580px; margin-top: 4px; box-shadow: $boxShadow-Search; &.inDrawer { width: var(--radix-popover-trigger-width); z-index: 1; } } .list { background-color: $backgroundColor-Search; max-height: 360px; overflow-y: auto; scrollbar-gutter: stable; scrollbar-width: thin; padding: 0.75rem; } .noResults { display: flex; justify-content: center; align-items: center; } .item { list-style: none; padding-top: 0.4rem; padding-bottom: 0.4rem; padding-inline: 0.75rem; border-radius: $borderRadius-SearchItem; border: 2px solid transparent; cursor: pointer; // This style is used to denote keyboard focus &.keyboardFocus { background-color: $backgroundColor-SearchItem--hover; // Ensures the underlying link is visible on programmatic hover & > a { color: var(--xmlui-textColor-primary); } } & > a { color: var(--xmlui-textColor-primary); &:hover { color: inherit; } &:active { color: var(--xmlui-textColor-primary); } } & > a .snippet { padding-top: 0.25rem; padding-bottom: 0.25rem; } &.header:has(+ .content):not(:first-child) { margin-top: 0.5rem; } &.content {} } :export{ themeVars: t.json-stringify($themeVars) } ``` -------------------------------------------------------------------------------- /xmlui/src/components/Timer/Timer.tsx: -------------------------------------------------------------------------------- ```typescript import { createComponentRenderer } from "../../components-core/renderers"; import { Timer, defaultProps } from "./TimerNative"; import { createMetadata } from "../metadata-helpers"; const COMP = "Timer"; export const TimerMd = createMetadata({ status: "stable", description: "`Timer` is a non-visual component that fires events at regular intervals. " + "It can be enabled or disabled and ensures that the timer event handler " + "completes before firing the next event.", props: { enabled: { description: "Whether the timer is enabled and should fire events.", valueType: "boolean", defaultValue: defaultProps.enabled, }, interval: { description: "The interval in milliseconds between timer events.", valueType: "number", defaultValue: defaultProps.interval, }, initialDelay: { description: "The delay in milliseconds before the first timer event.", valueType: "number", defaultValue: defaultProps.initialDelay, }, once: { description: "Whether the timer should stop after firing its first tick event.", valueType: "boolean", defaultValue: defaultProps.once, }, }, events: { tick: { description: "This event is triggered at each interval when the ${COMP} is enabled.", }, }, apis: { pause: { description: "Pauses the timer. The timer can be resumed later from where it left off.", signature: "pause()", }, resume: { description: "Resumes a paused timer. If the timer is not paused, this method has no effect.", signature: "resume()", }, isPaused: { description: "Returns whether the timer is currently paused.", signature: "isPaused(): boolean", }, isRunning: { description: "Returns whether the timer is currently running (enabled and not paused).", signature: "isRunning(): boolean", }, }, }); export const timerComponentRenderer = createComponentRenderer( COMP, TimerMd, ({ node, extractValue, lookupEventHandler, registerComponentApi }) => { return ( <Timer enabled={extractValue.asOptionalBoolean(node.props.enabled)} interval={extractValue.asOptionalNumber(node.props.interval)} initialDelay={extractValue.asOptionalNumber(node.props.initialDelay)} once={extractValue.asOptionalBoolean(node.props.once)} onTick={lookupEventHandler("tick")} registerComponentApi={registerComponentApi} /> ); }, ); ``` -------------------------------------------------------------------------------- /xmlui/src/components/Breakout/Breakout.spec.ts: -------------------------------------------------------------------------------- ```typescript import { getBoundingRect, SKIP_REASON } from "../../testing/component-test-helpers"; import { test, expect } from "../../testing/fixtures"; // ============================================================================= // BASIC FUNCTIONALITY TESTS // ============================================================================= test("component renders with basic content", async ({ page, initTestBed }) => { await initTestBed(`<Breakout>Content inside breakout</Breakout>`); await expect(page.getByText("Content inside breakout")).toBeVisible(); }); // ============================================================================= // ACCESSIBILITY TESTS // ============================================================================= test("children are focusable", async ({ page, initTestBed }) => { await initTestBed(`<Breakout><Button label="focusable button" /></Breakout>`); await page.keyboard.press("Tab"); await expect(page.getByRole("button")).toBeFocused(); }); // ============================================================================= // VISUAL STATE TESTS // ============================================================================= test("has the expected CSS attributes", async ({ page, initTestBed }) => { await initTestBed(`<Breakout testId="bo">Content inside breakout</Breakout>`); const breakout = page.getByTestId("bo"); const windowWidth = await page.evaluate(() => { return window.innerWidth; }); await expect(breakout).toHaveCSS("position", "relative"); await expect(breakout).toHaveCSS("left", `${Math.floor(windowWidth / 2)}px`); await expect(breakout).toHaveCSS("right", `${Math.floor(windowWidth / 2)}px`); await expect(breakout).toHaveCSS("margin-left", `-${Math.floor(windowWidth / 2)}px`); await expect(breakout).toHaveCSS("margin-right", `-${Math.floor(windowWidth / 2)}px`); await expect(breakout).toHaveCSS("width", `${windowWidth}px`); }); // ============================================================================= // EDGE CASE TESTS // ============================================================================= test("component maintains height based on its content", async ({ page, initTestBed }) => { await initTestBed(` <Breakout testId="bo"> <Stack height="150px" backgroundColor="red"> <Text>Tall content</Text> </Stack> </Breakout>`); const breakout = page.getByTestId("bo"); const { height: boHeight } = await getBoundingRect(breakout); expect(boHeight).toBeCloseTo(150); }); ```