This is page 13 of 181. Use http://codebase.md/xmlui-org/xmlui?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ ├── config.json │ └── cool-queens-look.md ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-blog.yml │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── blog-page-component.png │ │ │ │ ├── blog-scrabble.png │ │ │ │ ├── integrated-blog-search.png │ │ │ │ └── lorem-ipsum.png │ │ │ ├── lorem-ipsum.md │ │ │ ├── newest-post.md │ │ │ ├── older-post.md │ │ │ └── welcome-to-the-xmlui-blog.md │ │ ├── mockServiceWorker.js │ │ ├── netlify.toml │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Debug.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ ├── Main.xmlui.xs │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── CONTRIBUTING.md ├── docs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── ComponentRefLinks.txt │ ├── content │ │ ├── _meta.json │ │ ├── components │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── APICall.md │ │ │ ├── App.md │ │ │ ├── AppHeader.md │ │ │ ├── AppState.md │ │ │ ├── AutoComplete.md │ │ │ ├── Avatar.md │ │ │ ├── Backdrop.md │ │ │ ├── Badge.md │ │ │ ├── BarChart.md │ │ │ ├── Bookmark.md │ │ │ ├── Breakout.md │ │ │ ├── Button.md │ │ │ ├── Card.md │ │ │ ├── Carousel.md │ │ │ ├── ChangeListener.md │ │ │ ├── Checkbox.md │ │ │ ├── CHStack.md │ │ │ ├── ColorPicker.md │ │ │ ├── Column.md │ │ │ ├── ContentSeparator.md │ │ │ ├── CVStack.md │ │ │ ├── DataSource.md │ │ │ ├── DateInput.md │ │ │ ├── DatePicker.md │ │ │ ├── DonutChart.md │ │ │ ├── DropdownMenu.md │ │ │ ├── EmojiSelector.md │ │ │ ├── ExpandableItem.md │ │ │ ├── FileInput.md │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FlowLayout.md │ │ │ ├── Footer.md │ │ │ ├── Form.md │ │ │ ├── FormItem.md │ │ │ ├── FormSection.md │ │ │ ├── Fragment.md │ │ │ ├── H1.md │ │ │ ├── H2.md │ │ │ ├── H3.md │ │ │ ├── H4.md │ │ │ ├── H5.md │ │ │ ├── H6.md │ │ │ ├── Heading.md │ │ │ ├── HSplitter.md │ │ │ ├── HStack.md │ │ │ ├── Icon.md │ │ │ ├── IFrame.md │ │ │ ├── Image.md │ │ │ ├── Items.md │ │ │ ├── LabelList.md │ │ │ ├── Legend.md │ │ │ ├── LineChart.md │ │ │ ├── Link.md │ │ │ ├── List.md │ │ │ ├── Logo.md │ │ │ ├── Markdown.md │ │ │ ├── MenuItem.md │ │ │ ├── MenuSeparator.md │ │ │ ├── ModalDialog.md │ │ │ ├── NavGroup.md │ │ │ ├── NavLink.md │ │ │ ├── NavPanel.md │ │ │ ├── NoResult.md │ │ │ ├── NumberBox.md │ │ │ ├── Option.md │ │ │ ├── Page.md │ │ │ ├── PageMetaTitle.md │ │ │ ├── Pages.md │ │ │ ├── Pagination.md │ │ │ ├── PasswordInput.md │ │ │ ├── PieChart.md │ │ │ ├── ProgressBar.md │ │ │ ├── Queue.md │ │ │ ├── RadioGroup.md │ │ │ ├── RealTimeAdapter.md │ │ │ ├── Redirect.md │ │ │ ├── Select.md │ │ │ ├── Slider.md │ │ │ ├── Slot.md │ │ │ ├── SpaceFiller.md │ │ │ ├── Spinner.md │ │ │ ├── Splitter.md │ │ │ ├── Stack.md │ │ │ ├── StickyBox.md │ │ │ ├── SubMenuItem.md │ │ │ ├── Switch.md │ │ │ ├── TabItem.md │ │ │ ├── Table.md │ │ │ ├── TableOfContents.md │ │ │ ├── Tabs.md │ │ │ ├── Text.md │ │ │ ├── TextArea.md │ │ │ ├── TextBox.md │ │ │ ├── Theme.md │ │ │ ├── TimeInput.md │ │ │ ├── Timer.md │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneSwitch.md │ │ │ ├── Tooltip.md │ │ │ ├── Tree.md │ │ │ ├── VSplitter.md │ │ │ ├── VStack.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ ├── xmlui-spreadsheet │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Spreadsheet.md │ │ │ └── xmlui-website-blocks │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Carousel.md │ │ │ ├── HelloMd.md │ │ │ ├── HeroSection.md │ │ │ └── ScrollToTop.md │ │ └── extensions │ │ ├── _meta.json │ │ ├── xmlui-animations │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Animation.md │ │ │ ├── FadeAnimation.md │ │ │ ├── FadeInAnimation.md │ │ │ ├── FadeOutAnimation.md │ │ │ ├── ScaleAnimation.md │ │ │ └── SlideInAnimation.md │ │ └── xmlui-website-blocks │ │ ├── _meta.json │ │ ├── _overview.md │ │ ├── Carousel.md │ │ ├── HelloMd.md │ │ ├── HeroSection.md │ │ └── ScrollToTop.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── feed.rss │ │ ├── mockServiceWorker.js │ │ ├── pages │ │ │ ├── _meta.json │ │ │ ├── app-structure.md │ │ │ ├── build-editor-component.md │ │ │ ├── build-hello-world-component.md │ │ │ ├── components-intro.md │ │ │ ├── context-variables.md │ │ │ ├── forms.md │ │ │ ├── globals.md │ │ │ ├── glossary.md │ │ │ ├── helper-tags.md │ │ │ ├── hosted-deployment.md │ │ │ ├── howto │ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md │ │ │ │ ├── chain-a-refetch.md │ │ │ │ ├── debug-a-component.md │ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md │ │ │ │ ├── delegate-a-method.md │ │ │ │ ├── do-custom-form-validation.md │ │ │ │ ├── expose-a-method-from-a-component.md │ │ │ │ ├── filter-and-transform-data-from-an-api.md │ │ │ │ ├── group-items-in-list-by-a-property.md │ │ │ │ ├── handle-background-operations.md │ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md │ │ │ │ ├── make-a-set-of-equal-width-cards.md │ │ │ │ ├── make-a-table-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── build-system.md │ ├── build-xmlui.md │ ├── component-behaviors.md │ ├── containers.md │ ├── glossary.md │ ├── index.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── standalone-app.md │ ├── state-management.md │ └── xmlui-repo.md ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.tsx │ │ ├── SelectionStore │ │ │ ├── SelectionStore.md │ │ │ ├── SelectionStore.tsx │ │ │ └── SelectionStoreNative.tsx │ │ ├── Slider │ │ │ ├── Slider.md │ │ │ ├── Slider.module.scss │ │ │ ├── Slider.spec.ts │ │ │ ├── Slider.tsx │ │ │ └── SliderNative.tsx │ │ ├── Slot │ │ │ ├── Slot.md │ │ │ ├── Slot.spec.ts │ │ │ └── Slot.ts │ │ ├── SlotItem.tsx │ │ ├── SpaceFiller │ │ │ ├── SpaceFiller.md │ │ │ ├── SpaceFiller.module.scss │ │ │ ├── SpaceFiller.spec.ts │ │ │ ├── SpaceFiller.tsx │ │ │ └── SpaceFillerNative.tsx │ │ ├── Spinner │ │ │ ├── Spinner.md │ │ │ ├── Spinner.module.scss │ │ │ ├── Spinner.spec.ts │ │ │ ├── Spinner.tsx │ │ │ └── SpinnerNative.tsx │ │ ├── Splitter │ │ │ ├── HSplitter.md │ │ │ ├── HSplitter.spec.ts │ │ │ ├── Splitter.md │ │ │ ├── Splitter.module.scss │ │ │ ├── Splitter.spec.ts │ │ │ ├── Splitter.tsx │ │ │ ├── SplitterNative.tsx │ │ │ ├── utils.ts │ │ │ ├── VSplitter.md │ │ │ └── VSplitter.spec.ts │ │ ├── Stack │ │ │ ├── CHStack.md │ │ │ ├── CHStack.spec.ts │ │ │ ├── CVStack.md │ │ │ ├── CVStack.spec.ts │ │ │ ├── HStack.md │ │ │ ├── HStack.spec.ts │ │ │ ├── Stack.md │ │ │ ├── Stack.module.scss │ │ │ ├── Stack.spec.ts │ │ │ ├── Stack.tsx │ │ │ ├── StackNative.tsx │ │ │ ├── VStack.md │ │ │ └── VStack.spec.ts │ │ ├── StickyBox │ │ │ ├── StickyBox.md │ │ │ ├── StickyBox.module.scss │ │ │ ├── StickyBox.tsx │ │ │ └── StickyBoxNative.tsx │ │ ├── Switch │ │ │ ├── Switch.md │ │ │ ├── Switch.spec.ts │ │ │ └── Switch.tsx │ │ ├── Table │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── react-table-config.d.ts │ │ │ ├── Table.md │ │ │ ├── Table.module.scss │ │ │ ├── Table.spec.ts │ │ │ ├── Table.tsx │ │ │ ├── TableNative.tsx │ │ │ └── useRowSelection.tsx │ │ ├── TableOfContents │ │ │ ├── TableOfContents.module.scss │ │ │ ├── TableOfContents.spec.ts │ │ │ ├── TableOfContents.tsx │ │ │ └── TableOfContentsNative.tsx │ │ ├── Tabs │ │ │ ├── TabContext.tsx │ │ │ ├── TabItem.md │ │ │ ├── TabItem.tsx │ │ │ ├── TabItemNative.tsx │ │ │ ├── Tabs.md │ │ │ ├── Tabs.module.scss │ │ │ ├── Tabs.spec.ts │ │ │ ├── Tabs.tsx │ │ │ └── TabsNative.tsx │ │ ├── Text │ │ │ ├── Text.md │ │ │ ├── Text.module.scss │ │ │ ├── Text.spec.ts │ │ │ ├── Text.tsx │ │ │ └── TextNative.tsx │ │ ├── TextArea │ │ │ ├── TextArea.md │ │ │ ├── TextArea.module.scss │ │ │ ├── TextArea.spec.ts │ │ │ ├── TextArea.tsx │ │ │ ├── TextAreaNative.tsx │ │ │ ├── TextAreaResizable.tsx │ │ │ └── useComposedRef.ts │ │ ├── TextBox │ │ │ ├── TextBox.md │ │ │ ├── TextBox.module.scss │ │ │ ├── TextBox.spec.ts │ │ │ ├── TextBox.tsx │ │ │ └── TextBoxNative.tsx │ │ ├── Theme │ │ │ ├── NotificationToast.tsx │ │ │ ├── Theme.md │ │ │ ├── Theme.module.scss │ │ │ ├── Theme.spec.ts │ │ │ ├── Theme.tsx │ │ │ └── ThemeNative.tsx │ │ ├── TimeInput │ │ │ ├── TimeInput.md │ │ │ ├── TimeInput.module.scss │ │ │ ├── TimeInput.spec.ts │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeInputNative.tsx │ │ │ └── utils.ts │ │ ├── Timer │ │ │ ├── Timer.md │ │ │ ├── Timer.spec.ts │ │ │ ├── Timer.tsx │ │ │ └── TimerNative.tsx │ │ ├── Toggle │ │ │ ├── Toggle.module.scss │ │ │ └── Toggle.tsx │ │ ├── ToneChangerButton │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneChangerButton.spec.ts │ │ │ └── ToneChangerButton.tsx │ │ ├── ToneSwitch │ │ │ ├── ToneSwitch.md │ │ │ ├── ToneSwitch.module.scss │ │ │ ├── ToneSwitch.spec.ts │ │ │ ├── ToneSwitch.tsx │ │ │ └── ToneSwitchNative.tsx │ │ ├── Tooltip │ │ │ ├── Tooltip.md │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.spec.ts │ │ │ ├── Tooltip.tsx │ │ │ └── TooltipNative.tsx │ │ ├── Tree │ │ │ ├── testData.ts │ │ │ ├── Tree-dynamic.spec.ts │ │ │ ├── Tree-icons.spec.ts │ │ │ ├── Tree.md │ │ │ ├── Tree.spec.ts │ │ │ ├── TreeComponent.module.scss │ │ │ ├── TreeComponent.tsx │ │ │ └── TreeNative.tsx │ │ ├── TreeDisplay │ │ │ ├── TreeDisplay.md │ │ │ ├── TreeDisplay.module.scss │ │ │ ├── TreeDisplay.tsx │ │ │ └── TreeDisplayNative.tsx │ │ ├── ValidationSummary │ │ │ ├── ValidationSummary.module.scss │ │ │ └── ValidationSummary.tsx │ │ └── VisuallyHidden.tsx │ ├── components-core │ │ ├── abstractions │ │ │ ├── ComponentRenderer.ts │ │ │ ├── LoaderRenderer.ts │ │ │ ├── standalone.ts │ │ │ └── treeAbstractions.ts │ │ ├── action │ │ │ ├── actions.ts │ │ │ ├── APICall.tsx │ │ │ ├── FileDownloadAction.tsx │ │ │ ├── FileUploadAction.tsx │ │ │ ├── NavigateAction.tsx │ │ │ └── TimedAction.tsx │ │ ├── ApiBoundComponent.tsx │ │ ├── appContext │ │ │ ├── date-functions.ts │ │ │ ├── math-function.ts │ │ │ └── misc-utils.ts │ │ ├── AppContext.tsx │ │ ├── behaviors │ │ │ ├── Behavior.tsx │ │ │ └── CoreBehaviors.tsx │ │ ├── component-hooks.ts │ │ ├── ComponentDecorator.tsx │ │ ├── ComponentViewer.tsx │ │ ├── CompoundComponent.tsx │ │ ├── constants.ts │ │ ├── DebugViewProvider.tsx │ │ ├── descriptorHelper.ts │ │ ├── devtools │ │ │ ├── InspectorDialog.module.scss │ │ │ ├── InspectorDialog.tsx │ │ │ └── InspectorDialogVisibilityContext.tsx │ │ ├── EngineError.ts │ │ ├── event-handlers.ts │ │ ├── InspectorButton.module.scss │ │ ├── InspectorContext.tsx │ │ ├── interception │ │ │ ├── abstractions.ts │ │ │ ├── ApiInterceptor.ts │ │ │ ├── ApiInterceptorProvider.tsx │ │ │ ├── apiInterceptorWorker.ts │ │ │ ├── Backend.ts │ │ │ ├── Errors.ts │ │ │ ├── IndexedDb.ts │ │ │ ├── initMock.ts │ │ │ ├── InMemoryDb.ts │ │ │ ├── ReadonlyCollection.ts │ │ │ └── useApiInterceptorContext.tsx │ │ ├── loader │ │ │ ├── ApiLoader.tsx │ │ │ ├── DataLoader.tsx │ │ │ ├── ExternalDataLoader.tsx │ │ │ ├── Loader.tsx │ │ │ ├── MockLoaderRenderer.tsx │ │ │ └── PageableLoader.tsx │ │ ├── LoaderComponent.tsx │ │ ├── markup-check.ts │ │ ├── parts.ts │ │ ├── renderers.ts │ │ ├── rendering │ │ │ ├── AppContent.tsx │ │ │ ├── AppRoot.tsx │ │ │ ├── AppWrapper.tsx │ │ │ ├── buildProxy.ts │ │ │ ├── collectFnVarDeps.ts │ │ │ ├── ComponentAdapter.tsx │ │ │ ├── ComponentWrapper.tsx │ │ │ ├── Container.tsx │ │ │ ├── containers.ts │ │ │ ├── ContainerWrapper.tsx │ │ │ ├── ErrorBoundary.module.scss │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── InvalidComponent.module.scss │ │ │ ├── InvalidComponent.tsx │ │ │ ├── nodeUtils.ts │ │ │ ├── reducer.ts │ │ │ ├── renderChild.tsx │ │ │ ├── StandaloneComponent.tsx │ │ │ ├── StateContainer.tsx │ │ │ ├── UnknownComponent.module.scss │ │ │ ├── UnknownComponent.tsx │ │ │ └── valueExtractor.ts │ │ ├── reportEngineError.ts │ │ ├── RestApiProxy.ts │ │ ├── script-runner │ │ │ ├── asyncProxy.ts │ │ │ ├── AttributeValueParser.ts │ │ │ ├── bannedFunctions.ts │ │ │ ├── BindingTreeEvaluationContext.ts │ │ │ ├── eval-tree-async.ts │ │ │ ├── eval-tree-common.ts │ │ │ ├── eval-tree-sync.ts │ │ │ ├── ParameterParser.ts │ │ │ ├── process-statement-async.ts │ │ │ ├── process-statement-common.ts │ │ │ ├── process-statement-sync.ts │ │ │ ├── ScriptingSourceTree.ts │ │ │ ├── simplify-expression.ts │ │ │ ├── statement-queue.ts │ │ │ └── visitors.ts │ │ ├── StandaloneApp.tsx │ │ ├── StandaloneExtensionManager.ts │ │ ├── TableOfContentsContext.tsx │ │ ├── theming │ │ │ ├── _themes.scss │ │ │ ├── component-layout-resolver.ts │ │ │ ├── extendThemeUtils.ts │ │ │ ├── hvar.ts │ │ │ ├── layout-resolver.ts │ │ │ ├── parse-layout-props.ts │ │ │ ├── StyleContext.tsx │ │ │ ├── StyleRegistry.ts │ │ │ ├── ThemeContext.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ ├── themes │ │ │ │ ├── base-utils.ts │ │ │ │ ├── palette.ts │ │ │ │ ├── root.ts │ │ │ │ ├── solid.ts │ │ │ │ ├── theme-colors.ts │ │ │ │ └── xmlui.ts │ │ │ ├── themeVars.module.scss │ │ │ ├── themeVars.ts │ │ │ ├── transformThemeVars.ts │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── actionUtils.ts │ │ │ ├── audio-utils.ts │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /xmlui/src/components/FormSection/FormSection.md: -------------------------------------------------------------------------------- ```markdown 1 | %-PROP-START columnGap 2 | 3 | ```xmlui-pg copy display name="Example: columnGap" 4 | <Form padding="1rem"> 5 | <FormSection columnGap="1rem"> 6 | <FormItem width="50%" label="Name" bindTo="" /> 7 | <FormItem width="50%" label="Occupation" bindTo="" /> 8 | </FormSection> 9 | </Form> 10 | ``` 11 | 12 | %-PROP-END 13 | 14 | %-PROP-STARt rowGap 15 | 16 | ```xmlui-pg copy display name="Example: rowGap" 17 | <Form padding="1rem"> 18 | <FormSection rowGap="2rem"> 19 | <FormItem label="Name" bindTo="" /> 20 | <FormItem label="Occupation" bindTo="" /> 21 | </FormSection> 22 | </Form> 23 | ``` 24 | 25 | %-PROP-END 26 | 27 | %-PROP-START info 28 | 29 | ```xmlui-pg copy display name="Example: info" 30 | <Form padding="1rem"> 31 | <FormSection info="This is some information about a particular section."> 32 | <FormItem label="Input Field" bindTo="" /> 33 | </FormSection> 34 | </Form> 35 | ``` 36 | 37 | %-PROP-END 38 | 39 | %-PROP-START infoFontSize 40 | 41 | ```xmlui-pg copy {4} display name="Example: infoFontSize" 42 | <Form padding="1rem"> 43 | <FormSection 44 | info="This is some information about a particular section." 45 | infoFontSize="18px" 46 | > 47 | <FormItem label="Input Field" bindTo="" /> 48 | </FormSection> 49 | </Form> 50 | ``` 51 | 52 | %-PROP-END 53 | 54 | %-PROP-START heading 55 | 56 | ```xmlui-pg copy display name="Example: heading" 57 | <Form padding="1rem"> 58 | <FormSection heading="Basic Heading"> 59 | <FormItem label="Input Field" bindTo="" /> 60 | </FormSection> 61 | </Form> 62 | ``` 63 | 64 | %-PROP-END 65 | 66 | %-PROP-START headingLevel 67 | 68 | ```xmlui-pg copy display name="Example: headingLevel" 69 | <Form padding="1rem"> 70 | <FormSection heading="Basic Heading" headingLevel="h1"> 71 | <FormItem label="Input Field" bindTo="" /> 72 | </FormSection> 73 | </Form> 74 | ``` 75 | 76 | %-PROP-END 77 | 78 | %-PROP-START headingWeight 79 | 80 | The default weight is `bold`. 81 | 82 | ```xmlui-pg copy display name="Example: headingWeight" 83 | <Form padding="1rem"> 84 | <FormSection heading="Basic Heading" headingWeight="normal"> 85 | <FormItem label="Input Field" bindTo="" /> 86 | </FormSection> 87 | </Form> 88 | ``` 89 | 90 | %-PROP-END 91 | 92 | %-PROP-START paddingBottom 93 | 94 | ```xmlui-pg copy display name="Example: paddingBottom" 95 | <Form padding="1rem"> 96 | <FormSection paddingBottom="3rem" heading="Basic Info"> 97 | <FormItem width="50%" label="First Name" bindTo="" /> 98 | <FormItem width="50%" label="Last Name" bindTo="" /> 99 | </FormSection> 100 | <FormSection paddingBottom="0" heading="Job Related"> 101 | <FormItem width="50%" label="Occupation" bindTo="" /> 102 | <FormItem width="50%" label="Job Description" bindTo="" /> 103 | </FormSection> 104 | </Form> 105 | ``` 106 | 107 | %-PROP-END 108 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Avatar/Avatar.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./Avatar.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { sizeMd } from "../../components/abstractions"; 6 | import { Avatar, defaultProps } from "./AvatarNative"; 7 | import { createMetadata, d } from "../metadata-helpers"; 8 | 9 | const COMP = "Avatar"; 10 | 11 | export const AvatarMd = createMetadata({ 12 | status: "stable", 13 | description: 14 | "`Avatar` displays a user or entity's profile picture as a circular image, " + 15 | "with automatic fallback to initials when no image is provided. It's commonly " + 16 | "used in headers, user lists, comments, and anywhere you need to represent a " + 17 | "person or organization.", 18 | props: { 19 | size: { 20 | description: `This property defines the display size of the ${COMP}.`, 21 | availableValues: sizeMd, 22 | valueType: "string", 23 | defaultValue: defaultProps.size, 24 | }, 25 | name: { 26 | description: 27 | `This property sets the name value the ${COMP} uses to display initials. If neither ` + 28 | "this property nor \`url\` is defined, an empty avatar is displayed.", 29 | valueType: "string", 30 | }, 31 | url: { 32 | description: 33 | `This property specifies the URL of the image to display in the ${COMP}. ` + 34 | "If neither this property nor \`name\` is defined, an empty avatar is displayed.", 35 | valueType: "string", 36 | }, 37 | }, 38 | events: { 39 | click: d("This event is triggered when the avatar is clicked."), 40 | }, 41 | themeVars: parseScssVar(styles.themeVars), 42 | defaultThemeVars: { 43 | [`borderRadius-${COMP}`]: "4px", 44 | [`boxShadow-${COMP}`]: "inset 0 0 0 1px rgba(4,32,69,0.1)", 45 | [`textColor-${COMP}`]: "$textColor-secondary", 46 | [`fontWeight-${COMP}`]: "$fontWeight-bold", 47 | [`border-${COMP}`]: "0px solid $color-surface-400A80", 48 | [`backgroundColor-${COMP}`]: "$color-surface-100", 49 | }, 50 | }); 51 | 52 | export const avatarComponentRenderer = createComponentRenderer( 53 | COMP, 54 | AvatarMd, 55 | ({ node, extractValue, lookupEventHandler, className, extractResourceUrl }) => { 56 | return ( 57 | <Avatar 58 | className={className} 59 | size={node.props?.size} 60 | url={node.props.url ? extractResourceUrl(node.props.url) : undefined} 61 | name={extractValue(node.props.name)} 62 | onClick={lookupEventHandler("click")} 63 | /> 64 | ); 65 | }, 66 | ); 67 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ChangeListener/ChangeListener.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Value monitoring**: Watches any expression, variable, or component property for changes 5 | - **Throttling support**: Prevents excessive triggering with `throttleWaitInMs` for rapid changes 6 | - **Previous/new values**: Access both old and new values in the change handler 7 | - **Reactive patterns**: Coordinates between components or triggers side effects 8 | 9 | %-DESC-END 10 | 11 | %-PROP-START listenTo 12 | 13 | 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. 14 | 15 | ```xmlui-pg copy display name="Example: listenTo" 16 | <App var.counter="{0}" var.isEven="{false}"> 17 | <Button label="Increment counter" onClick="{counter++}" /> 18 | <ChangeListener 19 | listenTo="{counter}" 20 | onDidChange="isEven = counter % 2 == 0" /> 21 | <Text>Counter is {counter} which {isEven? "is": "isn't"} even.</Text> 22 | </App> 23 | ``` 24 | 25 | %-PROP-END 26 | 27 | %-PROP-START throttleWaitInMs 28 | 29 | 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. 30 | 31 | ```xmlui-pg copy display name="Example: throttleWaitInMs" 32 | <App var.counter="{0}" var.isEven="{false}" var.throttle="{0}"> 33 | <HStack> 34 | <Button label="Increment counter" onClick="{counter++}" /> 35 | <Button label="Set 3 sec throttling" onClick="throttle = 3000" /> 36 | <Button label="Reset throttling" onClick="throttle = 0" /> 37 | </HStack> 38 | 39 | <ChangeListener 40 | listenTo="{counter}" 41 | throttleWaitInMs="{throttle}" 42 | onDidChange="isEven = counter % 2 == 0" /> 43 | <Text>Counter is {counter} which {isEven? "is": "isn't"} even.</Text> 44 | </App> 45 | ``` 46 | 47 | %-PROP-END 48 | 49 | %-EVENT-START didChange 50 | 51 | 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). 52 | 53 | The event argument is an object with `prevValue` and `newValue` properties that (as their name suggests) contain the previous and the new values. 54 | 55 | %-EVENT-END 56 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/AppState/AppStateNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs"; 2 | import type { AsyncFunction } from "../../abstractions/FunctionDefs"; 3 | import { useIsomorphicLayoutEffect } from "../../components-core/utils/hooks"; 4 | import { useAppStateContextPart } from "../../components/App/AppStateContext"; 5 | 6 | export const defaultProps: Pick<{ bucket?: string }, "bucket"> = { 7 | bucket: "default", 8 | }; 9 | 10 | type Props = { 11 | bucket?: string; 12 | initialValue: Record<string, any>; 13 | updateState: UpdateStateFn; 14 | registerComponentApi: RegisterComponentApiFn; 15 | onDidUpdate?: AsyncFunction; 16 | }; 17 | 18 | export function AppState({ 19 | bucket = defaultProps.bucket, 20 | updateState, 21 | initialValue, 22 | registerComponentApi, 23 | onDidUpdate, 24 | }: Props) { 25 | const update = useAppStateContextPart((value) => value.update); 26 | const value = useAppStateContextPart((value) => value?.appState?.[bucket]); 27 | 28 | useIsomorphicLayoutEffect(() => { 29 | if (initialValue !== undefined) { 30 | update(bucket, initialValue); 31 | } 32 | }, [bucket, initialValue]); 33 | 34 | useIsomorphicLayoutEffect(() => { 35 | updateState({ value }); 36 | 37 | // Fire the didUpdate event when value changes 38 | if (onDidUpdate) { 39 | onDidUpdate({ bucket, value, previousValue: undefined }); // Note: previousValue tracking could be added if needed 40 | } 41 | }, [updateState, value, onDidUpdate, bucket]); 42 | 43 | useIsomorphicLayoutEffect(() => { 44 | registerComponentApi({ 45 | update: (patch) => update(bucket, patch), 46 | appendToList: (key: string, id: any) => { 47 | const currentState = value || {}; 48 | const currentArray = currentState[key] || []; 49 | // Only add if the id doesn't already exist in the array 50 | if (!currentArray.includes(id)) { 51 | const newArray = [...currentArray, id]; 52 | update(bucket, { [key]: newArray }); 53 | } 54 | }, 55 | removeFromList: (key: string, id: any) => { 56 | const currentState = value || {}; 57 | const currentArray = currentState[key] || []; 58 | const newArray = currentArray.filter((item: any) => item !== id); 59 | update(bucket, { [key]: newArray }); 60 | }, 61 | listIncludes: (key: string, id: any) => { 62 | const currentState = value || {}; 63 | const currentArray = currentState[key] || []; 64 | return currentArray.includes(id); 65 | }, 66 | }); 67 | }, [bucket, registerComponentApi, update, value]); 68 | 69 | return null; 70 | } 71 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Link/LinkNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | type CSSProperties, 3 | type ForwardedRef, 4 | forwardRef, 5 | type HTMLAttributeReferrerPolicy, 6 | type ReactNode, 7 | useMemo, 8 | } from "react"; 9 | import { Link } from "@remix-run/react"; 10 | import classnames from "classnames"; 11 | 12 | import styles from "./Link.module.scss"; 13 | 14 | import type { LinkTarget } from "../abstractions"; 15 | import { createUrlWithQueryParams } from "../component-utils"; 16 | import { Icon } from "../Icon/IconNative"; 17 | import type { To } from "react-router-dom"; 18 | 19 | // ===================================================================================================================== 20 | // React Link component implementation 21 | 22 | type Props = { 23 | to: string | { pathname: string; queryParams?: Record<string, any> }; 24 | children: ReactNode; 25 | icon?: string; 26 | active?: boolean; 27 | disabled?: boolean; 28 | onClick?: () => void; 29 | style?: CSSProperties; 30 | className?: string; 31 | } & Partial< 32 | Pick< 33 | HTMLAnchorElement, 34 | "hreflang" | "rel" | "download" | "target" | "referrerPolicy" | "ping" | "type" 35 | > 36 | >; 37 | 38 | export const defaultProps: Pick<Props, "active" | "disabled"> = { 39 | active: false, 40 | disabled: false, 41 | }; 42 | 43 | export const LinkNative = forwardRef(function LinkNative( 44 | props: Props, 45 | forwardedRef: ForwardedRef<HTMLDivElement>, 46 | ) { 47 | const { 48 | to, 49 | children, 50 | icon, 51 | active = defaultProps.active, 52 | onClick, 53 | target, 54 | disabled = defaultProps.disabled, 55 | style, 56 | className, 57 | ...anchorProps 58 | } = specifyTypes(props); 59 | 60 | const iconLink = !!icon && !children; 61 | const smartTo = useMemo(() => { 62 | return createUrlWithQueryParams(to); 63 | }, [to]) as To; 64 | 65 | const Node = to ? Link : "div"; 66 | return ( 67 | <Node 68 | {...anchorProps} 69 | ref={forwardedRef as any} 70 | to={smartTo} 71 | style={style} 72 | target={target} 73 | onClick={onClick} 74 | className={classnames(className, styles.container, { 75 | [styles.iconLink]: iconLink, 76 | [styles.active]: active, 77 | [styles.disabled]: disabled, 78 | })} 79 | > 80 | {icon && ( 81 | <div className={styles.iconWrapper}> 82 | <Icon name={icon} /> 83 | </div> 84 | )} 85 | {children} 86 | </Node> 87 | ); 88 | }); 89 | 90 | /** 91 | * Converts generic types to more specific ones. 92 | */ 93 | function specifyTypes(props: Props) { 94 | const { target, referrerPolicy } = props; 95 | return { 96 | ...props, 97 | target: target as LinkTarget, 98 | referrerPolicy: referrerPolicy as HTMLAttributeReferrerPolicy, 99 | }; 100 | } 101 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Card/CardNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { type CSSProperties, type ForwardedRef, type ReactNode, forwardRef } from "react"; 2 | import classnames from "classnames"; 3 | 4 | import styles from "./Card.module.scss"; 5 | 6 | import { Avatar } from "../Avatar/AvatarNative"; 7 | import { LinkNative } from "../Link/LinkNative"; 8 | import type { HeadingProps } from "../Heading/HeadingNative"; 9 | import { Heading } from "../Heading/HeadingNative"; 10 | import { Text } from "../Text/TextNative"; 11 | 12 | type Props = { 13 | style?: CSSProperties; 14 | className?: string; 15 | children?: ReactNode; 16 | title?: string; 17 | subtitle?: string; 18 | linkTo?: string; 19 | avatarUrl?: string; 20 | showAvatar?: boolean; 21 | avatarSize?: string; 22 | orientation?: string; 23 | onClick?: any; 24 | }; 25 | 26 | export const defaultProps: Pick<Props, "orientation" | "showAvatar"> = { 27 | orientation: "vertical", 28 | showAvatar: false, 29 | }; 30 | 31 | export const Card = forwardRef(function Card( 32 | { 33 | children, 34 | orientation = defaultProps.orientation, 35 | style, 36 | className, 37 | title, 38 | subtitle, 39 | linkTo, 40 | avatarUrl, 41 | showAvatar = !!avatarUrl || defaultProps.showAvatar, 42 | avatarSize, 43 | onClick, 44 | ...rest 45 | }: Props, 46 | forwardedRef: ForwardedRef<HTMLDivElement>, 47 | ) { 48 | const titleProps: Partial<HeadingProps> = { 49 | level: "h2", 50 | maxLines: 1, 51 | }; 52 | return ( 53 | <div 54 | {...rest} 55 | ref={forwardedRef} 56 | className={classnames( 57 | styles.wrapper, 58 | { 59 | [styles.isClickable]: !!onClick, 60 | [styles.vertical]: orientation === "vertical", 61 | [styles.horizontal]: orientation === "horizontal", 62 | }, 63 | className, 64 | )} 65 | style={style} 66 | onClick={onClick} 67 | > 68 | {[title, subtitle, avatarUrl, showAvatar].some(Boolean) && ( 69 | <div className={styles.avatarWrapper}> 70 | {showAvatar && <Avatar url={avatarUrl} name={title} size={avatarSize} />} 71 | <div className={styles.titleWrapper}> 72 | {linkTo ? ( 73 | title ? ( 74 | <LinkNative to={linkTo + ""}> 75 | <Heading {...titleProps}>{title}</Heading> 76 | </LinkNative> 77 | ) : null 78 | ) : title ? ( 79 | <Heading {...titleProps}>{title}</Heading> 80 | ) : null} 81 | {subtitle !== undefined && ( 82 | <> 83 | <Text variant="secondary">{subtitle}</Text> 84 | </> 85 | )} 86 | </div> 87 | </div> 88 | )} 89 | {children} 90 | </div> 91 | ); 92 | }); 93 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Badge/Badge.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Dynamic color mapping**: Automatically applies colors based on the badge value (e.g., status states) 5 | - **Two shape variants**: Choose between `badge` (rounded corners) or `pill` (fully rounded) 6 | - **Flexible color control**: Set just background color or customize both background and text colors 7 | 8 | %-DESC-END 9 | 10 | %-PROP-START value 11 | 12 | ```xmlui-pg copy name="Example: value" 13 | <App> 14 | <Badge value="Example value" /> 15 | <Badge value="Example badge"> 16 | Example Child 17 | </Badge> 18 | <Badge /> 19 | </App> 20 | ``` 21 | 22 | %-PROP-END 23 | 24 | %-PROP-START variant 25 | 26 | ```xmlui-pg copy display name="Example: variant" 27 | <App> 28 | <Badge value="Example badge" variant="badge" /> 29 | <Badge value="Example pill" variant="pill" /> 30 | </App> 31 | ``` 32 | 33 | %-PROP-END 34 | 35 | %-PROP-START colorMap 36 | 37 | Provide the component with a list or key-value pairs in two ways: 38 | 39 | 1. Only change the background color 40 | 41 | ```xmlui-pg copy {2} name="Example: only background color" 42 | <App var.simpleColorMap="{{ important: 'red', regular: 'blue', unimportant: 'black' }}"> 43 | <Badge value="important" colorMap="{simpleColorMap}" /> 44 | </App> 45 | ``` 46 | 47 | 2. Change the background and label color 48 | 49 | ```xmlui-pg copy display {2-5} name="Example: background and label color" 50 | <App 51 | var.simpleColorMap="{{ 52 | important: { label: 'red', background: 'pink' }, 53 | unimportant: { label: 'black', background: 'gray' } 54 | }}"> 55 | <Badge value="important" colorMap="{simpleColorMap}" /> 56 | <Badge value="unimportant" colorMap="{simpleColorMap}" /> 57 | <Badge value="other" colorMap="{simpleColorMap}" /> 58 | </App> 59 | ``` 60 | 61 | %-PROP-END 62 | 63 | %-PROP-START indicatorPosition 64 | 65 | The value of this optional property sets the string to provide a color scheme for the Alert. 66 | 67 | | Value | Description | 68 | | :------------- | :------------------------------------------------------------------- | 69 | | `start` | The indicator is displayed within the badge before its text | 70 | | `end` | The indicator is displayed within the badge after its text | 71 | | `top-start` | The indicator is displayed over the badge in the top-start corner | 72 | | `top-end` | The indicator is displayed over the badge in the top-end corner | 73 | | `bottom-start` | The indicator is displayed over the badge in the bottom-start corner | 74 | | `bottom-end` | The indicator is displayed over the badge in the bottom-end corner | 75 | 76 | %-PROP-END 77 | ``` -------------------------------------------------------------------------------- /packages/xmlui-animations/src/FadeInAnimation.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer, createMetadata } from "xmlui"; 2 | import { Animation, defaultProps } from "./AnimationNative"; 3 | 4 | const COMP = "FadeInAnimation"; 5 | 6 | const defaultAnimationValues = { 7 | from: 0, 8 | to: 1, 9 | }; 10 | 11 | export const FadeInAnimationMd = createMetadata({ 12 | description: `The \`${COMP}\` component represents an animation that fades in the content.`, 13 | status: "experimental", 14 | docFolder: "src", 15 | props: { 16 | animateWhenInView: { 17 | description: `Indicates whether the animation should start when the component is in view`, 18 | valueType: "boolean", 19 | }, 20 | duration: { 21 | description: `The duration of the animation in milliseconds`, 22 | valueType: "number", 23 | }, 24 | reverse: { 25 | description: `Indicates whether the animation should run in reverse`, 26 | defaultValue: defaultProps.reverse, 27 | valueType: "boolean", 28 | }, 29 | loop: { 30 | description: `Indicates whether the animation should loop`, 31 | defaultValue: defaultProps.loop, 32 | valueType: "boolean", 33 | }, 34 | delay: { 35 | description: `The delay before the animation starts in milliseconds`, 36 | defaultValue: defaultProps.delay, 37 | valueType: "number", 38 | }, 39 | }, 40 | events: { 41 | started: { description: `Event fired when the animation starts` }, 42 | stopped: { description: `Event fired when the animation stops` }, 43 | }, 44 | apis: { 45 | start: { description: `Starts the animation` }, 46 | stop: { description: `Stops the animation` }, 47 | }, 48 | }); 49 | 50 | export const fadeInAnimationRenderer = createComponentRenderer( 51 | "FadeInAnimation", 52 | FadeInAnimationMd, 53 | ({ node, renderChild, extractValue, registerComponentApi, lookupEventHandler }) => { 54 | return ( 55 | <Animation 56 | registerComponentApi={registerComponentApi} 57 | animation={{ 58 | from: { opacity: defaultAnimationValues.from }, 59 | to: { opacity: defaultAnimationValues.to }, 60 | }} 61 | duration={extractValue.asOptionalNumber(node.props.duration)} 62 | onStop={lookupEventHandler("stopped")} 63 | onStart={lookupEventHandler("started")} 64 | animateWhenInView={extractValue.asOptionalBoolean(node.props.animateWhenInView)} 65 | reverse={extractValue.asOptionalBoolean(node.props.reverse)} 66 | loop={extractValue.asOptionalBoolean(node.props.loop)} 67 | delay={extractValue.asOptionalNumber(node.props.delay)} 68 | > 69 | {renderChild(node.children)} 70 | </Animation> 71 | ); 72 | }, 73 | ); 74 | ``` -------------------------------------------------------------------------------- /docs/public/resources/files/tutorials/p2do/todo-logo.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="63" height="23" viewBox="0 0 63 23" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <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"/> 3 | <path d="M21.5 7.75V11.5L24 12.75" stroke="#106CBD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 4 | <path d="M12.7129 5.96875H8.68945V18H6.36523V5.96875H2.35156V3.99609H12.7129V5.96875Z" fill="#106CBD"/> 5 | <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"/> 6 | <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"/> 7 | <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"/> 8 | </svg> 9 | ``` -------------------------------------------------------------------------------- /docs/public/pages/tutorial-05.md: -------------------------------------------------------------------------------- ```markdown 1 | # Charts 2 | 3 | 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. 4 | 5 | ```xmlui-pg noHeader 6 | ---app display 7 | <App> 8 | <Statuses /> 9 | </App> 10 | ---desc 11 | `Statuses` uses three critical properties of `DonutChart`. 12 | - `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. 13 | - `dataKey`: The object key that holds data. 14 | - `nameKey`: The object key whose value is the data label. 15 | ---comp display copy /dataKey/ /nameKey/ 16 | <Component name="Statuses"> 17 | <DataSource id="dashboardStats" url="/resources/files/dashboard-stats.json" method="GET" /> 18 | <Card title="Statuses" height="400px" width="{$props.width}"> 19 | <DonutChart 20 | data="{ 21 | [ 22 | { 23 | name: 'sent', 24 | value: dashboardStats.value[0].sent_invoices 25 | }, 26 | { 27 | name: 'draft', 28 | value: dashboardStats.value[0].draft_invoices 29 | }, 30 | { 31 | name: 'paid', 32 | value: dashboardStats.value[0].paid_invoices 33 | }, 34 | ] 35 | }" 36 | dataKey="value" 37 | nameKey="name" 38 | /> 39 | </Card> 40 | </Component> 41 | ``` 42 | 43 | ## Multiseries charts 44 | 45 | [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. 46 | 47 | ```xmlui-pg /data/ noHeader 48 | ---app display /data/ /xKey/ /yKeys/ 49 | <App> 50 | <MonthlyStatus /> 51 | </App> 52 | ---comp display /data/ /yKeys/ 53 | <Component name="MonthlyStatus"> 54 | 55 | <DataSource 56 | id="monthlyStatus" 57 | url="/resources/files/monthly-status.json" 58 | method="GET" /> 59 | 60 | <VStack width="{$props.width}"> 61 | <H1>{$props.title}</H1> 62 | 63 | <Card height="400px"> 64 | <BarChart 65 | orientation="horizontal" 66 | data="{ monthlyStatus }" 67 | xKey="month" 68 | yKeys="{['paid_revenue', 'sent_revenue']}" 69 | stacked="true" 70 | showLegend="true" 71 | tickFormatter="{(value) => { 72 | return window.formatMonth(value); 73 | }}" 74 | /> 75 | </Card> 76 | </VStack> 77 | 78 | </Component> 79 | ``` ``` -------------------------------------------------------------------------------- /packages/xmlui-animations/src/FadeOutAnimation.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer, createMetadata } from "xmlui"; 2 | import { Animation, defaultProps } from "./AnimationNative"; 3 | 4 | const COMP = "FadeOutAnimation"; 5 | 6 | const defaultAnimationValues = { 7 | from: 1, 8 | to: 0, 9 | }; 10 | 11 | export const FadeOutAnimationMd = createMetadata({ 12 | description: `The \`${COMP}\` component represents an animation that fades out the content.`, 13 | status: "experimental", 14 | docFolder: "src", 15 | props: { 16 | animateWhenInView: { 17 | description: `Indicates whether the animation should start when the component is in view`, 18 | valueType: "boolean", 19 | }, 20 | duration: { 21 | description: `The duration of the animation in milliseconds`, 22 | valueType: "number", 23 | }, 24 | reverse: { 25 | description: `Indicates whether the animation should run in reverse`, 26 | defaultValue: defaultProps.reverse, 27 | valueType: "boolean", 28 | }, 29 | loop: { 30 | description: `Indicates whether the animation should loop`, 31 | defaultValue: defaultProps.loop, 32 | valueType: "boolean", 33 | }, 34 | delay: { 35 | description: `The delay before the animation starts in milliseconds`, 36 | defaultValue: defaultProps.delay, 37 | valueType: "number", 38 | }, 39 | }, 40 | events: { 41 | started: { description: `Event fired when the animation starts` }, 42 | stopped: { description: `Event fired when the animation stops` }, 43 | }, 44 | apis: { 45 | start: { description: `Starts the animation` }, 46 | stop: { description: `Stops the animation` }, 47 | }, 48 | }); 49 | 50 | export const fadeOutAnimationRenderer = createComponentRenderer( 51 | "FadeOutAnimation", 52 | FadeOutAnimationMd, 53 | ({ node, renderChild, extractValue, registerComponentApi, lookupEventHandler }) => { 54 | return ( 55 | <Animation 56 | registerComponentApi={registerComponentApi} 57 | animation={{ 58 | from: { opacity: defaultAnimationValues.from }, 59 | to: { opacity: defaultAnimationValues.to }, 60 | }} 61 | duration={extractValue.asOptionalNumber(node.props.duration)} 62 | onStop={lookupEventHandler("stopped")} 63 | onStart={lookupEventHandler("started")} 64 | animateWhenInView={extractValue.asOptionalBoolean(node.props.animateWhenInView)} 65 | reverse={extractValue.asOptionalBoolean(node.props.reverse)} 66 | loop={extractValue.asOptionalBoolean(node.props.loop)} 67 | delay={extractValue.asOptionalNumber(node.props.delay)} 68 | > 69 | {renderChild(node.children)} 70 | </Animation> 71 | ); 72 | }, 73 | ); 74 | ``` -------------------------------------------------------------------------------- /docs/content/components/PieChart.md: -------------------------------------------------------------------------------- ```markdown 1 | # PieChart [#piechart] 2 | 3 | `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. 4 | 5 | **Key features:** 6 | - **Proportional visualization**: Displays data segments as slices of a complete circle 7 | - **Flexible labeling**: Configurable label positions both inside and outside chart segments 8 | - **Data binding**: Connects to array data with specified keys for values and labels 9 | - **Label list display**: Optional legend-style list showing all segments and values 10 | - **Customizable sizing**: Configurable dimensions and outer radius for different layouts 11 | 12 | For a variation with a hollow center, see [DonutChart](/components/DonutChart). 13 | 14 | ## Properties [#properties] 15 | 16 | ### `data` [#data] 17 | 18 | The data to be displayed in the chart. Needs to be an array of objects. 19 | 20 | ### `dataKey` [#datakey] 21 | 22 | This property specifies the key in the data objects that will be used to render the chart. 23 | 24 | ### `labelListPosition` (default: "inside") [#labellistposition-default-inside] 25 | 26 | The position of the label list. 27 | 28 | Available values: `top`, `left`, `right`, `bottom`, `inside` **(default)**, `outside`, `insideLeft`, `insideRight`, `insideTop`, `insideBottom`, `insideTopLeft`, `insideBottomLeft`, `insideTopRight`, `insideBottomRight`, `insideStart`, `insideEnd`, `end`, `center`, `centerTop`, `centerBottom`, `middle` 29 | 30 | ### `nameKey` [#namekey] 31 | 32 | Specifies the key in the data objects that will be used to label the different data series. 33 | 34 | ### `outerRadius` [#outerradius] 35 | 36 | The outer radius of the pie chart, can be a number or a string (e.g., '100%'). 37 | 38 | ### `showLabel` (default: true) [#showlabel-default-true] 39 | 40 | Toggles whether to show labels (`true`) or not (`false`). 41 | 42 | ### `showLabelList` (default: false) [#showlabellist-default-false] 43 | 44 | Whether to show labels in a list (`true`) or not (`false`). 45 | 46 | ### `showLegend` (default: false) [#showlegend-default-false] 47 | 48 | Toggles whether to show legend (`true`) or not (`false`). 49 | 50 | ## Events [#events] 51 | 52 | This component does not have any events. 53 | 54 | ## Exposed Methods [#exposed-methods] 55 | 56 | This component does not expose any methods. 57 | 58 | ## Styling [#styling] 59 | 60 | ### Theme Variables [#theme-variables] 61 | 62 | | Variable | Default Value (Light) | Default Value (Dark) | 63 | | --- | --- | --- | 64 | | [textColor](../styles-and-themes/common-units/#color)-labelList-PieChart | $textColor-primary | $textColor-primary | 65 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Pages/Pages.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { TableOfContentsProvider } from "../../components-core/TableOfContentsContext"; 3 | import { createMetadata, d, dInternal } from "../metadata-helpers"; 4 | import { Pages, RouteWrapper, defaultProps } from "./PagesNative"; 5 | import { extractPaddings } from "../../components-core/utils/css-utils"; 6 | 7 | const PAGE = "Page"; 8 | 9 | export const PageMd = createMetadata({ 10 | status: "stable", 11 | docFolder: "Pages", 12 | description: 13 | "`Page` defines route endpoints within an application, mapping specific URL " + 14 | "patterns to content that displays when users navigate to those routes. Each " + 15 | "Page represents a distinct view or screen in your single-page application's " + 16 | "routing system.", 17 | props: { 18 | //TODO illesg rename to path 19 | url: d( 20 | `The URL of the route associated with the content. If not set, the page is not available.`, 21 | ), 22 | navLabel: dInternal( 23 | "The label of the page that is displayed in the navigation panel. If provided, the " + 24 | "a new entry will be added to the navigation panel.", 25 | ), 26 | }, 27 | }); 28 | 29 | export const pageRenderer = createComponentRenderer( 30 | PAGE, 31 | PageMd, 32 | ({ node, extractValue, renderChild, className }) => { 33 | const paddings = extractPaddings(extractValue, node.props); 34 | return ( 35 | <TableOfContentsProvider> 36 | <RouteWrapper 37 | childRoute={node.children} 38 | uid={node.uid} 39 | renderChild={renderChild} 40 | key={extractValue(node.props.url)} 41 | className={className} 42 | {...paddings} 43 | /> 44 | </TableOfContentsProvider> 45 | ); 46 | }, 47 | ); 48 | 49 | const COMP = "Pages"; 50 | 51 | export const PagesMd = createMetadata({ 52 | status: "stable", 53 | description: 54 | "`Pages` serves as the routing coordinator within an [App](/components/App), " + 55 | "managing which [Page](/components/Page) displays based on the current URL.", 56 | props: { 57 | fallbackPath: { 58 | description: `The fallback path when the current URL does not match any of the paths of the pages.`, 59 | defaultValue: defaultProps.fallbackPath, 60 | }, 61 | }, 62 | }); 63 | 64 | export const pagesRenderer = createComponentRenderer( 65 | COMP, 66 | PagesMd, 67 | ({ node, extractValue, renderChild }) => { 68 | return ( 69 | <Pages 70 | fallbackPath={extractValue(node.props.fallbackPath)} 71 | node={node} 72 | renderChild={renderChild} 73 | extractValue={extractValue} 74 | /> 75 | ); 76 | }, 77 | ); 78 | ``` -------------------------------------------------------------------------------- /packages/xmlui-animations/src/Animation.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer, createMetadata } from "xmlui"; 2 | import { Animation, defaultProps } from "./AnimationNative"; 3 | 4 | const COMP = "Animation"; 5 | 6 | export const AnimationMd = createMetadata({ 7 | status: "experimental", 8 | description: `The \`${COMP}\` component represents a generic animation wrapper that can apply various animations to its children.`, 9 | docFolder: "src", 10 | props: { 11 | animation: { 12 | description: `The animation object to be applied to the component`, 13 | }, 14 | animateWhenInView: { 15 | description: `Indicates whether the animation should start when the component is in view`, 16 | }, 17 | duration: { 18 | description: `The duration of the animation in milliseconds`, 19 | }, 20 | once: { 21 | description: `Indicates whether the animation should only run once`, 22 | defaultValue: defaultProps.once, 23 | }, 24 | reverse: { 25 | description: `Indicates whether the animation should run in reverse`, 26 | defaultValue: defaultProps.reverse, 27 | }, 28 | loop: { 29 | description: `Indicates whether the animation should loop`, 30 | defaultValue: defaultProps.loop, 31 | }, 32 | delay: { 33 | description: `The delay before the animation starts in milliseconds`, 34 | defaultValue: defaultProps.delay, 35 | }, 36 | }, 37 | events: { 38 | started: { description: `Event fired when the animation starts` }, 39 | stopped: { description: `Event fired when the animation stops` }, 40 | }, 41 | apis: { 42 | start: { description: `Starts the animation` }, 43 | stop: { description: `Stops the animation` }, 44 | }, 45 | }); 46 | 47 | export const animationComponentRenderer = createComponentRenderer( 48 | COMP, 49 | AnimationMd, 50 | ({ registerComponentApi, renderChild, node, extractValue, lookupEventHandler }) => { 51 | return ( 52 | <Animation 53 | registerComponentApi={registerComponentApi} 54 | animation={extractValue(node.props.animation)} 55 | onStop={lookupEventHandler("stopped")} 56 | onStart={lookupEventHandler("started")} 57 | duration={extractValue.asOptionalNumber(node.props.duration)} 58 | animateWhenInView={extractValue.asOptionalBoolean(node.props.animateWhenInView)} 59 | once={extractValue.asOptionalBoolean(node.props.once)} 60 | reverse={extractValue.asOptionalBoolean(node.props.reverse)} 61 | loop={extractValue.asOptionalBoolean(node.props.loop)} 62 | delay={extractValue.asOptionalNumber(node.props.delay)} 63 | > 64 | {renderChild(node.children)} 65 | </Animation> 66 | ); 67 | }, 68 | ); 69 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ResponsiveBar/ResponsiveBar.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | // Define theme variables 11 | $backgroundColor-ResponsiveBar: createThemeVar("backgroundColor-ResponsiveBar"); 12 | $padding-ResponsiveBar: createThemeVar("padding-ResponsiveBar"); 13 | $margin-ResponsiveBar: createThemeVar("margin-ResponsiveBar"); 14 | 15 | @layer components { 16 | // --- This part defines the CSS styles 17 | .responsiveBar { 18 | display: flex; 19 | flex-direction: row; 20 | flex-shrink: 0; 21 | flex-grow: 0; 22 | align-items: center; 23 | background-color: $backgroundColor-ResponsiveBar; 24 | padding: $padding-ResponsiveBar; 25 | margin: $margin-ResponsiveBar; 26 | width: 100%; 27 | overflow: visible; // Allow dropdown to be visible 28 | 29 | &.vertical { 30 | flex-direction: column; 31 | height: 100%; 32 | width: auto; 33 | align-items: stretch; 34 | max-height: 100%; // Ensure it doesn't exceed parent height 35 | } 36 | 37 | &.horizontal { 38 | flex-direction: row; 39 | width: 100%; 40 | height: auto; 41 | align-items: center; 42 | } 43 | 44 | .visibleItems { 45 | display: flex; 46 | flex-direction: row; 47 | align-items: center; 48 | flex-shrink: 0; 49 | flex-grow: 1; /* Allow this container to grow and push dropdown to the end */ 50 | min-width: 0; 51 | overflow: hidden; // Hide overflow of items, but not the dropdown 52 | 53 | &.vertical { 54 | flex-direction: column; 55 | align-items: stretch; 56 | min-height: 0; 57 | min-width: auto; 58 | flex-grow: 1; // Take up available space 59 | width: auto; 60 | overflow: hidden; // Critical: hide overflow in vertical mode 61 | } 62 | 63 | &.horizontal { 64 | flex-direction: row; 65 | align-items: center; 66 | min-width: 0; 67 | min-height: auto; 68 | width: auto; 69 | height: auto; 70 | } 71 | } 72 | 73 | .overflowDropdown { 74 | flex-shrink: 0; 75 | width: auto; 76 | min-width: 40px; /* Consistent width for calculation */ 77 | position: relative; /* Ensure dropdown can position correctly */ 78 | z-index: 1000; /* Ensure dropdown appears above other content */ 79 | } 80 | } 81 | } 82 | 83 | // --- We export the theme variables to add them to the component renderer 84 | :export { 85 | themeVars: t.json-stringify($themeVars); 86 | } 87 | ``` -------------------------------------------------------------------------------- /tools/vscode/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "xmlui-vscode", 3 | "displayName": "xmlui", 4 | "description": "XMLUI language support", 5 | "icon": "resources/xmlui-logo.png", 6 | "author": "xmlui.org", 7 | "publisher": "xmlui", 8 | "license": "MIT", 9 | "version": "0.10.22", 10 | "private": true, 11 | "categories": [], 12 | "keywords": [], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/xmlui-com/xmlui" 16 | }, 17 | "engines": { 18 | "vscode": "^1.75.0" 19 | }, 20 | "activationEvents": [ 21 | "onLanguage:xmlui" 22 | ], 23 | "vsce": { 24 | "dependencies": false 25 | }, 26 | "main": "./dist/extension", 27 | "contributes": { 28 | "configuration": { 29 | "type": "object", 30 | "title": "XMLUI configuration", 31 | "properties": { 32 | "XMLUILanguageService.trace.server": { 33 | "scope": "window", 34 | "type": "string", 35 | "enum": [ 36 | "off", 37 | "messages", 38 | "verbose" 39 | ], 40 | "default": "off", 41 | "description": "Traces the communication between VS Code and the language server." 42 | } 43 | } 44 | }, 45 | "languages": [ 46 | { 47 | "id": "xmlui", 48 | "aliases": [ 49 | "XMLUI", 50 | "Xmlui" 51 | ], 52 | "extensions": [ 53 | ".xmlui" 54 | ] 55 | }, 56 | { 57 | "id": "javascript", 58 | "aliases": [ 59 | "XS", 60 | "xs" 61 | ], 62 | "extensions": [ 63 | ".xs" 64 | ] 65 | } 66 | ], 67 | "grammars": [ 68 | { 69 | "language": "xmlui", 70 | "scopeName": "source.xmlui", 71 | "path": "./syntaxes/xmlui.tmLanguage.json", 72 | "embeddedLanguages": { 73 | "meta.embedded.block.javascrip": "javascript" 74 | } 75 | } 76 | ] 77 | }, 78 | "scripts": { 79 | "build:vsix": "vsce package", 80 | "build": "node esbuild.js --production", 81 | "build:all": "./build.sh", 82 | "type-check": "tsc --noEmit", 83 | "watch": "npm-run-all -p watch:*", 84 | "watch:esbuild": "node esbuild.js --watch", 85 | "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", 86 | "format-test": "node ./dist/extension.js --test ./test-samples/complex-sample.xmlui" 87 | }, 88 | "dependencies": { 89 | "vscode-languageclient": "^9.0.1", 90 | "xmlui": "*" 91 | }, 92 | "devDependencies": { 93 | "@eslint/js": "^9.13.0", 94 | "@stylistic/eslint-plugin": "^2.9.0", 95 | "@types/node": "^20", 96 | "@types/vscode": "^1.75.1", 97 | "@vscode/vsce": "^3.6.0", 98 | "esbuild": "^0.25.1", 99 | "eslint": "^9.13.0", 100 | "npm-run-all": "^4.1.5", 101 | "typescript": "^5.7.2", 102 | "typescript-eslint": "^8.16.0" 103 | } 104 | } 105 | ``` -------------------------------------------------------------------------------- /xmlui/tests-e2e/modify-array-item-regression.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { expect, test } from "../src/testing/fixtures"; 2 | 3 | test("modify simple array item", async ({ page, initTestBed }) => { 4 | await initTestBed(` 5 | <Fragment var.fruits="{[{ name: 'apple', size: 'large' }]}" > 6 | <Button id="modifyButton" onClick="fruits[0].size = 'small'">Convert to small</Button> 7 | <Text id="fruits_text">{JSON.stringify(fruits)}</Text> 8 | </Fragment> 9 | `); 10 | await expect(page.getByTestId("fruits_text")).toHaveText( 11 | JSON.stringify([{ name: "apple", size: "large" }]), 12 | ); 13 | await page.getByTestId("modifyButton").click(); 14 | await expect(page.getByTestId("fruits_text")).toHaveText( 15 | JSON.stringify([{ name: "apple", size: "small" }]), 16 | ); 17 | }); 18 | 19 | test("modify simple array item 2", async ({ page, initTestBed }) => { 20 | await initTestBed(` 21 | <Fragment var.fruits="{[{ name: 'apple', size: 'large' }, {name: 'pear', size: 'somethingelse'}]}"> 22 | <Button id="modifyButton" onClick="fruits[1].size = 'small'">fruits[1].size = 'small'</Button> 23 | <Text id="fruits_text">{JSON.stringify(fruits)}</Text> 24 | </Fragment> 25 | `); 26 | await expect(page.getByTestId("fruits_text")).toHaveText( 27 | JSON.stringify([ 28 | { name: "apple", size: "large" }, 29 | { name: "pear", size: "somethingelse" }, 30 | ]), 31 | ); 32 | await page.getByTestId("modifyButton").click(); 33 | await expect(page.getByTestId("fruits_text")).toHaveText( 34 | JSON.stringify([ 35 | { name: "apple", size: "large" }, 36 | { name: "pear", size: "small" }, 37 | ]), 38 | ); 39 | }); 40 | 41 | test("modify simple array item 3", async ({ page, initTestBed }) => { 42 | await initTestBed(` 43 | <Fragment var.fruits="{ 44 | [{ name: 'apple', size: 'large', seeds: [{name: 'seed1', size: 'large'}] }, {name: 'pear', size: 'somethingelse'}] 45 | }"> 46 | <Button id="modifyButton" onClick="fruits[0].seeds[0].size = 'small'"> 47 | fruits[0].seeds[0].size = 'small' 48 | </Button> 49 | <Text id="fruits_text">{JSON.stringify(fruits)}</Text> 50 | </Fragment> 51 | `); 52 | await expect(page.getByTestId("fruits_text")).toHaveText( 53 | JSON.stringify([ 54 | { name: "apple", size: "large", seeds: [{ name: "seed1", size: "large" }] }, 55 | { name: "pear", size: "somethingelse" }, 56 | ]), 57 | ); 58 | await page.getByTestId("modifyButton").click(); 59 | await expect(page.getByTestId("fruits_text")).toHaveText( 60 | JSON.stringify([ 61 | { name: "apple", size: "large", seeds: [{ name: "seed1", size: "small" }] }, 62 | { name: "pear", size: "somethingelse" }, 63 | ]), 64 | ); 65 | }); 66 | ``` -------------------------------------------------------------------------------- /blog/src/themes/docs-theme.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { ThemeDefinition } from "xmlui"; 2 | 3 | export const DefaultDocsTheme: ThemeDefinition = { 4 | name: "XMLUI Documentation Theme", 5 | id: "docs-theme", 6 | extends: ["xmlui"], 7 | themeVars: { 8 | 9 | // --- Fundamental colors & typography 10 | backgroundColor: "$color-surface-0", 11 | "color-primary": "#3367CC", 12 | "color-surface": "#1e2734", 13 | "backgroundColor-content-App": "$color-surface-0", 14 | 15 | // --- App layout 16 | "maxWidth-App": "1320px", 17 | "boxShadow-navPanel-App": "none", 18 | 19 | // --- We intentionally use different theming to amplify the "documentation" feel 20 | // --- We use different navigation panel theming 21 | "backgroundColor-NavPanel": "$color-surface-50", 22 | "backgroundColor-navPanel-App": "$color-surface-50", 23 | "paddingVertical-NavPanel": "$space-5", 24 | "borderRightWidth-NavPanel": "1px", 25 | "maxWidth-Drawer": "100%", 26 | "textColor-NavLink": "$color-secondary-600", 27 | "color-indicator-NavLink--active": "transparent", 28 | "color-indicator-NavLink--hover": "transparent", 29 | "color-indicator-NavLink--pressed": "transparent", 30 | "backgroundColor-NavLink--hover": "$color-surface-100", 31 | "backgroundColor-NavLink--pressed": "$color-surface-100", 32 | "fontWeight-NavLink--active": "bold", 33 | "textColor-NavLink--hover": "$color-surface-900", 34 | "textColor-NavLink--active": "$color-primary-500", 35 | "textColor-NavLink--hover--active": "$color-primary-500", 36 | 37 | // --- Adjust a little bit of Markdown 38 | "fontSize-H1": "1.65rem", 39 | "fontWeight-H1": "700", 40 | "borderRadius-HtmlTable": "30px", 41 | 42 | light: { 43 | // --- Use these colors for light-tone document links 44 | "textColor-DocumentLinks": "#5B6475", 45 | "textColor-DocumentLinks--hover": "#1B232A", 46 | "backgroundColor-separator-DocumentLinks": "#e2e5ea", 47 | }, 48 | dark: { 49 | // --- These colors match better with the dark tone 50 | "backgroundColor-content-App": "$color-surface-100", 51 | backgroundColor: "$color-surface-100", 52 | "backgroundColor-NavPanel": "$color-surface-50", 53 | "backgroundColor-navPanel-App": "$color-surface-50", 54 | 55 | "border-NestedApp": "1px solid $color-surface-200", 56 | "backgroundColor-HtmlThead": "$color-surface-50", 57 | 58 | // --- Use these colors for dark-tone document links 59 | "textColor-DocumentLinks": "#8E97A8", 60 | "textColor-DocumentLinks--hover": "#F9FAFB", 61 | "backgroundColor-separator-DocumentLinks": "#38475E", 62 | }, 63 | }, 64 | resources: {}, 65 | }; 66 | 67 | export default DefaultDocsTheme; 68 | ``` -------------------------------------------------------------------------------- /docs/src/themes/docs-theme.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { ThemeDefinition } from "xmlui"; 2 | 3 | export const DefaultDocsTheme: ThemeDefinition = { 4 | name: "XMLUI Documentation Theme", 5 | id: "docs-theme", 6 | extends: ["xmlui"], 7 | themeVars: { 8 | 9 | // --- Fundamental colors & typography 10 | backgroundColor: "$color-surface-0", 11 | "color-primary": "#3367CC", 12 | "color-surface": "#1e2734", 13 | "backgroundColor-content-App": "$color-surface-0", 14 | 15 | // --- App layout 16 | "maxWidth-App": "1320px", 17 | "boxShadow-navPanel-App": "none", 18 | 19 | // --- We intentionally use different theming to amplify the "documentation" feel 20 | // --- We use different navigation panel theming 21 | "backgroundColor-NavPanel": "$color-surface-50", 22 | "backgroundColor-navPanel-App": "$color-surface-50", 23 | "paddingVertical-NavPanel": "$space-5", 24 | "borderRightWidth-NavPanel": "1px", 25 | "maxWidth-Drawer": "100%", 26 | "textColor-NavLink": "$color-secondary-600", 27 | "color-indicator-NavLink--active": "transparent", 28 | "color-indicator-NavLink--hover": "transparent", 29 | "color-indicator-NavLink--pressed": "transparent", 30 | "backgroundColor-NavLink--hover": "$color-surface-100", 31 | "backgroundColor-NavLink--pressed": "$color-surface-100", 32 | "fontWeight-NavLink--active": "bold", 33 | "textColor-NavLink--hover": "$color-surface-900", 34 | "textColor-NavLink--active": "$color-primary-500", 35 | "textColor-NavLink--hover--active": "$color-primary-500", 36 | 37 | // --- Adjust a little bit of Markdown 38 | "fontSize-H1": "1.65rem", 39 | "fontWeight-H1": "700", 40 | "borderRadius-HtmlTable": "30px", 41 | 42 | light: { 43 | // --- Use these colors for light-tone document links 44 | "textColor-DocumentLinks": "#5B6475", 45 | "textColor-DocumentLinks--hover": "#1B232A", 46 | "backgroundColor-separator-DocumentLinks": "#e2e5ea", 47 | }, 48 | dark: { 49 | // --- These colors match better with the dark tone 50 | "backgroundColor-content-App": "$color-surface-100", 51 | backgroundColor: "$color-surface-100", 52 | "backgroundColor-NavPanel": "$color-surface-50", 53 | "backgroundColor-navPanel-App": "$color-surface-50", 54 | 55 | "border-NestedApp": "1px solid $color-surface-200", 56 | "backgroundColor-HtmlThead": "$color-surface-50", 57 | 58 | // --- Use these colors for dark-tone document links 59 | "textColor-DocumentLinks": "#8E97A8", 60 | "textColor-DocumentLinks--hover": "#F9FAFB", 61 | "backgroundColor-separator-DocumentLinks": "#38475E", 62 | }, 63 | }, 64 | resources: {}, 65 | }; 66 | 67 | export default DefaultDocsTheme; 68 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Items/Items.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Simple iteration**: Maps data arrays to components using `$item`, `$itemIndex`, `$isFirst`, and `$isLast` context 5 | - **Layout agnostic**: No built-in styling or container—children determine the visual presentation 6 | - **Reverse ordering**: Optional `reverse` property to display data in opposite order 7 | - **Performance**: Lightweight alternative to `List` when you don't need virtualization or grouping 8 | 9 | >[!INFO] 10 | > `Items` is not a container! It does not wrap its items into a container; it merely renders its children. 11 | 12 | The `Items` component does not use virtualization; it maps each data item into a component. 13 | Thus, passing many items to a component instance will use many resources and slow down your app. 14 | If you plan to work with many items (more than a few dozen), use the [`List`](./List) and [`Table`](./Table) components instead. 15 | 16 | ### Inline Data 17 | 18 | You can set the list of data to be rendered via the `data` property, as the following sample shows. 19 | The nested child component describes a template to display each data entry in `Items`. 20 | In the template, you can refer to a particular entry with the [`$item`](#&item) identifier: 21 | 22 | ```xmlui-pg copy {8} display name="Example: inline data" 23 | <App> 24 | <VStack> 25 | <Items data="{[ 26 | { idx: 1, value: 'One lion' }, 27 | { idx: 2, value: 'Two monkeys' }, 28 | { idx: 3, value: 'Three rabbits' }, 29 | ]}"> 30 | <Text>{$item.idx} - {$item.value}</Text> 31 | </Items> 32 | </VStack> 33 | </App> 34 | ``` 35 | 36 | ### Data Binding 37 | 38 | You can use also API bindings to display data: 39 | 40 | ```xmlui-pg copy {4-6} display name="Example: data binding" 41 | <App> 42 | <VStack> 43 | <Items> 44 | <property name="data"> 45 | <DataSource url="https://api.spacexdata.com/v3/rockets"/> 46 | </property> 47 | <Image height="80px" width="110px" fit="cover" src="{$item.flickr_images[0]}"/> 48 | </Items> 49 | </VStack> 50 | </App> 51 | ``` 52 | 53 | %-DESC-END 54 | 55 | %-PROP-START reverse 56 | 57 | ```xmlui-pg copy {4} display name="Example: reverse" 58 | <App> 59 | <VStack> 60 | <Items 61 | reverse="true" 62 | data="{[ 63 | { idx: 1, value: 'One lion' }, 64 | { idx: 2, value: 'Two monkeys' }, 65 | { idx: 3, value: 'Three rabbits' }, 66 | ]}"> 67 | <Text>{$item.idx} - {$item.value}</Text> 68 | </Items> 69 | </VStack> 70 | </App> 71 | ``` 72 | 73 | %-PROP-END 74 | 75 | %-STYLE-START 76 | 77 | The `Items` component does not support styling. 78 | You should style the container component that wraps `Items`. 79 | You can also style the individual items via specifying a template component. 80 | 81 | %-STYLE-END 82 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/ContentSeparator/ContentSeparator.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Flexible orientation**: Create horizontal dividers (default) or vertical dividers between content 5 | - **Customizable sizing**: Control thickness with the `size` property 6 | - **Automatic spacing**: Takes full width/height of container unless size is specified 7 | 8 | %-DESC-END 9 | 10 | %-PROP-START orientation 11 | 12 | See the demo for an example under [`size`](#size). 13 | 14 | >[!INFO] 15 | > 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%). 16 | > This is true even if the `ContentSeparator` has siblings in the container. 17 | > The demo below illustrates this. 18 | > Notice how the first `ContentSeparator` does not show up while the second does: 19 | 20 | ```xmlui-pg copy display name="Example: no vertical space" 21 | <App> 22 | <HStack horizontalAlignment="center"> 23 | <ContentSeparator orientation="vertical" size="8px" backgroundColor="blue" /> 24 | </HStack> 25 | <HStack horizontalAlignment="center" height="48px"> 26 | <ContentSeparator orientation="vertical" size="8px" backgroundColor="red" /> 27 | </HStack> 28 | </App> 29 | ``` 30 | 31 | %-PROP-END 32 | 33 | %-PROP-START size 34 | 35 | ```xmlui-pg copy display name="Example: size" 36 | <App> 37 | <Heading level="h2"> 38 | Lorem Ipsum 39 | </Heading> 40 | <ContentSeparator /> 41 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, 42 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 43 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi 44 | ut aliquip ex ea commodo consequat. 45 | <ContentSeparator size="4px" /> 46 | <HStack height="120px"> 47 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum 48 | dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 49 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 50 | <ContentSeparator orientation="vertical" size="10px" /> 51 | Sed ut perspiciatis unde omnis iste natus error sit voluptatem 52 | accusantium doloremque laudantium, totam rem aperiam, 53 | eaque ipsa quae ab illo inventore veritatis et quasi architecto 54 | beatae vitae dicta sunt explicabo. 55 | </HStack> 56 | </App> 57 | ``` 58 | 59 | 60 | >[!INFO] 61 | > You can use the `width` and `height` layout properties to set the `ContentSeparator` dimensions. 62 | > For the horizontal separator, you can set the `height` property; the vertical separator offers the `width` property instead of `size`. 63 | > Nonetheless, we suggest you use the `size` property. 64 | 65 | %-PROP-END 66 | ``` -------------------------------------------------------------------------------- /packages/xmlui-search/src/Search.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $componentContainer: "SearchContainer"; 11 | $themeVars: t.composePaddingVars($themeVars, $componentContainer); 12 | 13 | $componentListPanel: "SearchPanel"; 14 | $themeVars: t.composeBorderVars($themeVars, $componentListPanel); 15 | $themeVars: t.composePaddingVars($themeVars, $componentListPanel); 16 | $backgroundColor-Search: createThemeVar("backgroundColor-#{$componentListPanel}"); 17 | $boxShadow-Search: createThemeVar("boxShadow-#{$componentListPanel}"); 18 | 19 | $componentListItem: "SearchItem"; 20 | $borderRadius-SearchItem: createThemeVar("borderRadius-#{$componentListItem}"); 21 | $backgroundColor-SearchItem--hover: createThemeVar("backgroundColor-#{$componentListItem}--hover"); 22 | $borderColor-SearchItem--focus: createThemeVar("borderColor-#{$componentListItem}--focus"); 23 | 24 | .listPanel { 25 | @include t.borderVars($themeVars, $componentListPanel); 26 | @include t.paddingVars($themeVars, $componentListPanel); 27 | overflow: hidden; 28 | width: 580px; 29 | margin-top: 4px; 30 | box-shadow: $boxShadow-Search; 31 | 32 | &.inDrawer { 33 | width: var(--radix-popover-trigger-width); 34 | z-index: 1; 35 | } 36 | } 37 | 38 | .list { 39 | background-color: $backgroundColor-Search; 40 | max-height: 360px; 41 | overflow-y: auto; 42 | scrollbar-gutter: stable; 43 | scrollbar-width: thin; 44 | padding: 0.75rem; 45 | } 46 | 47 | .noResults { 48 | display: flex; 49 | justify-content: center; 50 | align-items: center; 51 | } 52 | 53 | .item { 54 | list-style: none; 55 | padding-top: 0.4rem; 56 | padding-bottom: 0.4rem; 57 | padding-inline: 0.75rem; 58 | border-radius: $borderRadius-SearchItem; 59 | border: 2px solid transparent; 60 | cursor: pointer; 61 | 62 | // This style is used to denote keyboard focus 63 | &.keyboardFocus { 64 | background-color: $backgroundColor-SearchItem--hover; 65 | 66 | // Ensures the underlying link is visible on programmatic hover 67 | & > a { 68 | color: var(--xmlui-textColor-primary); 69 | } 70 | } 71 | 72 | & > a { 73 | color: var(--xmlui-textColor-primary); 74 | 75 | &:hover { 76 | color: inherit; 77 | } 78 | &:active { 79 | color: var(--xmlui-textColor-primary); 80 | } 81 | } 82 | 83 | & > a .snippet { 84 | padding-top: 0.25rem; 85 | padding-bottom: 0.25rem; 86 | } 87 | 88 | &.header:has(+ .content):not(:first-child) { 89 | margin-top: 0.5rem; 90 | } 91 | 92 | &.content {} 93 | } 94 | 95 | :export{ 96 | themeVars: t.json-stringify($themeVars) 97 | } 98 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Timer/Timer.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { Timer, defaultProps } from "./TimerNative"; 3 | import { createMetadata } from "../metadata-helpers"; 4 | 5 | const COMP = "Timer"; 6 | 7 | export const TimerMd = createMetadata({ 8 | status: "stable", 9 | description: 10 | "`Timer` is a non-visual component that fires events at regular intervals. " + 11 | "It can be enabled or disabled and ensures that the timer event handler " + 12 | "completes before firing the next event.", 13 | props: { 14 | enabled: { 15 | description: "Whether the timer is enabled and should fire events.", 16 | valueType: "boolean", 17 | defaultValue: defaultProps.enabled, 18 | }, 19 | interval: { 20 | description: "The interval in milliseconds between timer events.", 21 | valueType: "number", 22 | defaultValue: defaultProps.interval, 23 | }, 24 | initialDelay: { 25 | description: "The delay in milliseconds before the first timer event.", 26 | valueType: "number", 27 | defaultValue: defaultProps.initialDelay, 28 | }, 29 | once: { 30 | description: "Whether the timer should stop after firing its first tick event.", 31 | valueType: "boolean", 32 | defaultValue: defaultProps.once, 33 | }, 34 | }, 35 | events: { 36 | tick: { 37 | description: "This event is triggered at each interval when the ${COMP} is enabled.", 38 | }, 39 | }, 40 | apis: { 41 | pause: { 42 | description: "Pauses the timer. The timer can be resumed later from where it left off.", 43 | signature: "pause()", 44 | }, 45 | resume: { 46 | description: "Resumes a paused timer. If the timer is not paused, this method has no effect.", 47 | signature: "resume()", 48 | }, 49 | isPaused: { 50 | description: "Returns whether the timer is currently paused.", 51 | signature: "isPaused(): boolean", 52 | }, 53 | isRunning: { 54 | description: "Returns whether the timer is currently running (enabled and not paused).", 55 | signature: "isRunning(): boolean", 56 | }, 57 | }, 58 | }); 59 | 60 | export const timerComponentRenderer = createComponentRenderer( 61 | COMP, 62 | TimerMd, 63 | ({ node, extractValue, lookupEventHandler, registerComponentApi }) => { 64 | return ( 65 | <Timer 66 | enabled={extractValue.asOptionalBoolean(node.props.enabled)} 67 | interval={extractValue.asOptionalNumber(node.props.interval)} 68 | initialDelay={extractValue.asOptionalNumber(node.props.initialDelay)} 69 | once={extractValue.asOptionalBoolean(node.props.once)} 70 | onTick={lookupEventHandler("tick")} 71 | registerComponentApi={registerComponentApi} 72 | /> 73 | ); 74 | }, 75 | ); 76 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Breakout/Breakout.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { getBoundingRect, SKIP_REASON } from "../../testing/component-test-helpers"; 2 | import { test, expect } from "../../testing/fixtures"; 3 | 4 | // ============================================================================= 5 | // BASIC FUNCTIONALITY TESTS 6 | // ============================================================================= 7 | 8 | test("component renders with basic content", async ({ page, initTestBed }) => { 9 | await initTestBed(`<Breakout>Content inside breakout</Breakout>`); 10 | 11 | await expect(page.getByText("Content inside breakout")).toBeVisible(); 12 | }); 13 | 14 | // ============================================================================= 15 | // ACCESSIBILITY TESTS 16 | // ============================================================================= 17 | 18 | test("children are focusable", async ({ page, initTestBed }) => { 19 | await initTestBed(`<Breakout><Button label="focusable button" /></Breakout>`); 20 | await page.keyboard.press("Tab"); 21 | await expect(page.getByRole("button")).toBeFocused(); 22 | }); 23 | 24 | // ============================================================================= 25 | // VISUAL STATE TESTS 26 | // ============================================================================= 27 | 28 | test("has the expected CSS attributes", async ({ page, initTestBed }) => { 29 | await initTestBed(`<Breakout testId="bo">Content inside breakout</Breakout>`); 30 | 31 | const breakout = page.getByTestId("bo"); 32 | const windowWidth = await page.evaluate(() => { return window.innerWidth; }); 33 | await expect(breakout).toHaveCSS("position", "relative"); 34 | await expect(breakout).toHaveCSS("left", `${Math.floor(windowWidth / 2)}px`); 35 | await expect(breakout).toHaveCSS("right", `${Math.floor(windowWidth / 2)}px`); 36 | await expect(breakout).toHaveCSS("margin-left", `-${Math.floor(windowWidth / 2)}px`); 37 | await expect(breakout).toHaveCSS("margin-right", `-${Math.floor(windowWidth / 2)}px`); 38 | await expect(breakout).toHaveCSS("width", `${windowWidth}px`); 39 | }); 40 | 41 | // ============================================================================= 42 | // EDGE CASE TESTS 43 | // ============================================================================= 44 | 45 | test("component maintains height based on its content", async ({ page, initTestBed }) => { 46 | await initTestBed(` 47 | <Breakout testId="bo"> 48 | <Stack height="150px" backgroundColor="red"> 49 | <Text>Tall content</Text> 50 | </Stack> 51 | </Breakout>`); 52 | 53 | const breakout = page.getByTestId("bo"); 54 | const { height: boHeight } = await getBoundingRect(breakout); 55 | 56 | expect(boHeight).toBeCloseTo(150); 57 | }); 58 | ``` -------------------------------------------------------------------------------- /xmlui/bin/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | #!/usr/bin/env node 2 | 3 | import { build } from "./build"; 4 | import { start } from "./start"; 5 | import { preview } from "./preview"; 6 | import { argv } from "yargs"; 7 | import AdmZip from "adm-zip"; 8 | import { buildLib } from "./build-lib"; 9 | 10 | process.on("unhandledRejection", (err) => { 11 | throw err; 12 | }); 13 | const args = process.argv.slice(2); 14 | 15 | const scriptIndex = args.findIndex((x) => x === "build" || x === "eject" || x === "start" || x === "test"); 16 | const script = scriptIndex === -1 ? args[0] : args[scriptIndex]; 17 | 18 | async function zipDirectory(sourceDir: string, outPath: string = sourceDir) { 19 | const zip = new AdmZip(); 20 | zip.addLocalFolder(sourceDir); 21 | await zip.writeZipPromise(outPath); 22 | console.log(`Zip file created: ${outPath}`); 23 | } 24 | 25 | async function zipDist({target = "ui.zip", source="dist"}: {target?: string, source?: string}) { 26 | await zipDirectory(`${process.cwd()}/${source}`, `${process.cwd()}/${target}`); 27 | } 28 | 29 | function dedupeArg(arg: any) { 30 | if (Array.isArray(arg)) { 31 | return arg[arg.length - 1]; 32 | } 33 | return arg; 34 | } 35 | 36 | function getBoolArg(arg: any, defaultValue?: boolean | undefined) { 37 | if (arg === undefined) { 38 | return defaultValue; 39 | } 40 | return dedupeArg(arg) !== "false"; 41 | } 42 | 43 | function getStringArg(arg: any, defaultValue: string | undefined) { 44 | if (arg === undefined) { 45 | return defaultValue; 46 | } 47 | return dedupeArg(arg); 48 | } 49 | 50 | switch (script) { 51 | case "build": { 52 | const { flatDist, prod, buildMode, withMock, withHostingMetaFiles, withRelativeRoot } = 53 | argv as any; 54 | 55 | build({ 56 | buildMode: getStringArg(buildMode, prod ? "CONFIG_ONLY" : undefined), 57 | withMock: getBoolArg(withMock, prod ? false : undefined), 58 | withHostingMetaFiles: getBoolArg(withHostingMetaFiles, prod ? false : undefined), 59 | withRelativeRoot: getBoolArg(withRelativeRoot, prod ? true : undefined), 60 | flatDist: getBoolArg(flatDist, prod ? true : undefined), 61 | }); 62 | break; 63 | } 64 | case "build-lib": { 65 | const { watch, mode } = argv as any; 66 | buildLib({watchMode: getBoolArg(watch, false), mode: getStringArg(mode, "")}); 67 | break; 68 | } 69 | case "start": { 70 | const { port, withMock, proxy } = argv as any; 71 | start({ port, withMock: getBoolArg(withMock), proxy }); 72 | break; 73 | } 74 | case "preview": { 75 | const { proxy } = argv as any; 76 | preview({proxy}); 77 | break; 78 | } 79 | case "zip-dist": { 80 | const { target, source } = argv as any; 81 | zipDist({target, source}); 82 | break; 83 | } 84 | default: { 85 | console.log('Unknown script "' + script + '".'); 86 | console.log("Perhaps you need to update xmlui?"); 87 | } 88 | } 89 | ``` -------------------------------------------------------------------------------- /xmlui/src/parsers/xmlui-parser/CharacterCodes.ts: -------------------------------------------------------------------------------- ```typescript 1 | // Character codes used by the scanner when tokenizing input 2 | export const enum CharacterCodes { 3 | EOF = -1, 4 | nullCharacter = 0, 5 | maxAsciiCharacter = 0x7f, 6 | 7 | // Line break caharacters 8 | lineFeed = 0x0a, // \n 9 | carriageReturn = 0x0d, // \r 10 | lineSeparator = 0x2028, 11 | paragraphSeparator = 0x2029, 12 | nextLine = 0x0085, 13 | 14 | // Unicode 3.0 space characters 15 | space = 0x0020, // " " 16 | nonBreakingSpace = 0x00a0, // 17 | enQuad = 0x2000, 18 | emQuad = 0x2001, 19 | enSpace = 0x2002, 20 | emSpace = 0x2003, 21 | threePerEmSpace = 0x2004, 22 | fourPerEmSpace = 0x2005, 23 | sixPerEmSpace = 0x2006, 24 | figureSpace = 0x2007, 25 | punctuationSpace = 0x2008, 26 | thinSpace = 0x2009, 27 | hairSpace = 0x200a, 28 | zeroWidthSpace = 0x200b, 29 | narrowNoBreakSpace = 0x202f, 30 | ideographicSpace = 0x3000, 31 | mathematicalSpace = 0x205f, 32 | ogham = 0x1680, 33 | 34 | // Unicode replacement character produced when a byte sequence is invalid 35 | replacementCharacter = 0xfffd, 36 | 37 | _ = 0x5f, 38 | $ = 0x24, 39 | 40 | _0 = 0x30, 41 | _1 = 0x31, 42 | _2 = 0x32, 43 | _3 = 0x33, 44 | _4 = 0x34, 45 | _5 = 0x35, 46 | _6 = 0x36, 47 | _7 = 0x37, 48 | _8 = 0x38, 49 | _9 = 0x39, 50 | 51 | a = 0x61, 52 | b = 0x62, 53 | c = 0x63, 54 | d = 0x64, 55 | e = 0x65, 56 | f = 0x66, 57 | g = 0x67, 58 | h = 0x68, 59 | i = 0x69, 60 | j = 0x6a, 61 | k = 0x6b, 62 | l = 0x6c, 63 | m = 0x6d, 64 | n = 0x6e, 65 | o = 0x6f, 66 | p = 0x70, 67 | q = 0x71, 68 | r = 0x72, 69 | s = 0x73, 70 | t = 0x74, 71 | u = 0x75, 72 | v = 0x76, 73 | w = 0x77, 74 | x = 0x78, 75 | y = 0x79, 76 | z = 0x7a, 77 | 78 | A = 0x41, 79 | B = 0x42, 80 | C = 0x43, 81 | D = 0x44, 82 | E = 0x45, 83 | F = 0x46, 84 | G = 0x47, 85 | H = 0x48, 86 | I = 0x49, 87 | J = 0x4a, 88 | K = 0x4b, 89 | L = 0x4c, 90 | M = 0x4d, 91 | N = 0x4e, 92 | O = 0x4f, 93 | P = 0x50, 94 | Q = 0x51, 95 | R = 0x52, 96 | S = 0x53, 97 | T = 0x54, 98 | U = 0x55, 99 | V = 0x56, 100 | W = 0x57, 101 | X = 0x58, 102 | Y = 0x59, 103 | Z = 0x5a, 104 | 105 | ampersand = 0x26, // & 106 | asterisk = 0x2a, // * 107 | at = 0x40, // @ 108 | backslash = 0x5c, // \ 109 | backtick = 0x60, // ` 110 | bar = 0x7c, // | 111 | caret = 0x5e, // ^ 112 | closeBrace = 0x7d, // } 113 | closeBracket = 0x5d, // ] 114 | closeParen = 0x29, // ) 115 | colon = 0x3a, // : 116 | comma = 0x2c, // , 117 | dot = 0x2e, // . 118 | doubleQuote = 0x22, // " 119 | equals = 0x3d, // = 120 | exclamation = 0x21, // ! 121 | greaterThan = 0x3e, // > 122 | hash = 0x23, // # 123 | lessThan = 0x3c, // < 124 | minus = 0x2d, // - 125 | openBrace = 0x7b, // { 126 | openBracket = 0x5b, // [ 127 | openParen = 0x28, // ( 128 | percent = 0x25, // % 129 | plus = 0x2b, // + 130 | question = 0x3f, // ? 131 | semicolon = 0x3b, // ; 132 | singleQuote = 0x27, // ' 133 | slash = 0x2f, // / 134 | tilde = 0x7e, // ~ 135 | 136 | backspace = 0x08, // \b 137 | formFeed = 0x0c, // \f 138 | byteOrderMark = 0xfeff, 139 | tab = 0x09, // \t 140 | verticalTab = 0x0b, // \v 141 | } 142 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/loader/MockLoaderRenderer.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { useCallback } from "react"; 2 | 3 | import type { 4 | LoaderErrorFn, 5 | LoaderInProgressChangedFn, 6 | LoaderLoadedFn, 7 | } from "../abstractions/LoaderRenderer"; 8 | import type { ComponentDef } from "../../abstractions/ComponentDefs"; 9 | import type { ContainerState } from "../rendering/ContainerWrapper"; 10 | import { asyncWait } from "../utils/misc"; 11 | import { extractParam } from "../utils/extractParam"; 12 | import { createLoaderRenderer } from "../renderers"; 13 | import { useAppContext } from "../AppContext"; 14 | import { Loader } from "./Loader"; 15 | import { createMetadata, d } from "../../components/metadata-helpers"; 16 | 17 | type MockLoaderProps = { 18 | loader: MockLoaderDef; 19 | loaderInProgressChanged: LoaderInProgressChangedFn; 20 | loaderIsRefetchingChanged: LoaderInProgressChangedFn; 21 | loaderLoaded: LoaderLoadedFn; 22 | loaderError: LoaderErrorFn; 23 | state: ContainerState; 24 | structuralSharing?: boolean; 25 | }; 26 | 27 | function MockLoader({ 28 | loader, 29 | loaderInProgressChanged, 30 | loaderIsRefetchingChanged, 31 | loaderError, 32 | loaderLoaded, 33 | state, 34 | structuralSharing, 35 | }: MockLoaderProps) { 36 | const appContext = useAppContext(); 37 | const waitTime: number = extractParam(state, loader.props.waitTime, appContext); 38 | const responseObj: Record<string, any>[] = extractParam(state, loader.props.data, appContext); 39 | 40 | const doLoad = useCallback(async () => { 41 | if (waitTime) { 42 | await asyncWait(waitTime); 43 | } 44 | return responseObj; 45 | }, [responseObj, waitTime]); 46 | 47 | return ( 48 | <Loader 49 | state={state} 50 | loader={loader} 51 | loaderInProgressChanged={loaderInProgressChanged} 52 | loaderIsRefetchingChanged={loaderIsRefetchingChanged} 53 | loaderLoaded={loaderLoaded} 54 | loaderError={loaderError} 55 | loaderFn={doLoad} 56 | structuralSharing={structuralSharing} 57 | /> 58 | ); 59 | } 60 | 61 | export const MockLoaderMd = createMetadata({ 62 | status: "stable", 63 | description: "A loader that simulates a delay and returns a predefined response", 64 | props: { 65 | waitTime: d("The time to wait before returning the response"), 66 | data: d("The data to return"), 67 | }, 68 | }); 69 | 70 | type MockLoaderDef = ComponentDef<typeof MockLoaderMd>; 71 | 72 | export const mockLoaderRenderer = createLoaderRenderer( 73 | "MockLoader", 74 | ({ loader, state, loaderInProgressChanged, loaderLoaded, loaderError }) => { 75 | return ( 76 | <MockLoader 77 | loader={loader} 78 | state={state} 79 | loaderInProgressChanged={loaderInProgressChanged} 80 | loaderIsRefetchingChanged={loaderInProgressChanged} 81 | loaderLoaded={loaderLoaded} 82 | loaderError={loaderError} 83 | /> 84 | ); 85 | }, 86 | MockLoaderMd, 87 | ); 88 | ```