This is page 28 of 181. Use http://codebase.md/xmlui-org/xmlui/mockApiDef.js?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .changeset │ └── config.json ├── .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 │ ├── layout-changes.md │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── blog-page-component.png │ │ │ │ ├── blog-scrabble.png │ │ │ │ ├── integrated-blog-search.png │ │ │ │ └── lorem-ipsum.png │ │ │ ├── lorem-ipsum.md │ │ │ ├── newest-post.md │ │ │ ├── older-post.md │ │ │ └── welcome-to-the-xmlui-blog.md │ │ ├── mockServiceWorker.js │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Debug.xmlui │ │ │ ├── Headlines.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ └── blog-theme.ts │ └── tsconfig.json ├── CONTRIBUTING.md ├── docs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── ComponentRefLinks.txt │ ├── content │ │ ├── _meta.json │ │ ├── components │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── APICall.md │ │ │ ├── App.md │ │ │ ├── AppHeader.md │ │ │ ├── AppState.md │ │ │ ├── AutoComplete.md │ │ │ ├── Avatar.md │ │ │ ├── Backdrop.md │ │ │ ├── Badge.md │ │ │ ├── BarChart.md │ │ │ ├── Bookmark.md │ │ │ ├── Breakout.md │ │ │ ├── Button.md │ │ │ ├── Card.md │ │ │ ├── Carousel.md │ │ │ ├── ChangeListener.md │ │ │ ├── Checkbox.md │ │ │ ├── CHStack.md │ │ │ ├── ColorPicker.md │ │ │ ├── Column.md │ │ │ ├── ContentSeparator.md │ │ │ ├── CVStack.md │ │ │ ├── DataSource.md │ │ │ ├── DateInput.md │ │ │ ├── DatePicker.md │ │ │ ├── DonutChart.md │ │ │ ├── DropdownMenu.md │ │ │ ├── EmojiSelector.md │ │ │ ├── ExpandableItem.md │ │ │ ├── FileInput.md │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FlowLayout.md │ │ │ ├── Footer.md │ │ │ ├── Form.md │ │ │ ├── FormItem.md │ │ │ ├── FormSection.md │ │ │ ├── Fragment.md │ │ │ ├── H1.md │ │ │ ├── H2.md │ │ │ ├── H3.md │ │ │ ├── H4.md │ │ │ ├── H5.md │ │ │ ├── H6.md │ │ │ ├── Heading.md │ │ │ ├── HSplitter.md │ │ │ ├── HStack.md │ │ │ ├── Icon.md │ │ │ ├── IFrame.md │ │ │ ├── Image.md │ │ │ ├── Items.md │ │ │ ├── LabelList.md │ │ │ ├── Legend.md │ │ │ ├── LineChart.md │ │ │ ├── Link.md │ │ │ ├── List.md │ │ │ ├── Logo.md │ │ │ ├── Markdown.md │ │ │ ├── MenuItem.md │ │ │ ├── MenuSeparator.md │ │ │ ├── ModalDialog.md │ │ │ ├── NavGroup.md │ │ │ ├── NavLink.md │ │ │ ├── NavPanel.md │ │ │ ├── NoResult.md │ │ │ ├── NumberBox.md │ │ │ ├── Option.md │ │ │ ├── Page.md │ │ │ ├── PageMetaTitle.md │ │ │ ├── Pages.md │ │ │ ├── Pagination.md │ │ │ ├── PasswordInput.md │ │ │ ├── PieChart.md │ │ │ ├── ProgressBar.md │ │ │ ├── Queue.md │ │ │ ├── RadioGroup.md │ │ │ ├── RealTimeAdapter.md │ │ │ ├── Redirect.md │ │ │ ├── Select.md │ │ │ ├── Slider.md │ │ │ ├── Slot.md │ │ │ ├── SpaceFiller.md │ │ │ ├── Spinner.md │ │ │ ├── Splitter.md │ │ │ ├── Stack.md │ │ │ ├── StickyBox.md │ │ │ ├── SubMenuItem.md │ │ │ ├── Switch.md │ │ │ ├── TabItem.md │ │ │ ├── Table.md │ │ │ ├── TableOfContents.md │ │ │ ├── Tabs.md │ │ │ ├── Text.md │ │ │ ├── TextArea.md │ │ │ ├── TextBox.md │ │ │ ├── Theme.md │ │ │ ├── TimeInput.md │ │ │ ├── Timer.md │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneSwitch.md │ │ │ ├── Tooltip.md │ │ │ ├── Tree.md │ │ │ ├── VSplitter.md │ │ │ ├── VStack.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ ├── xmlui-spreadsheet │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Spreadsheet.md │ │ │ └── xmlui-website-blocks │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Carousel.md │ │ │ ├── HelloMd.md │ │ │ ├── HeroSection.md │ │ │ └── ScrollToTop.md │ │ └── extensions │ │ ├── _meta.json │ │ ├── xmlui-animations │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Animation.md │ │ │ ├── FadeAnimation.md │ │ │ ├── FadeInAnimation.md │ │ │ ├── FadeOutAnimation.md │ │ │ ├── ScaleAnimation.md │ │ │ └── SlideInAnimation.md │ │ └── xmlui-website-blocks │ │ ├── _meta.json │ │ ├── _overview.md │ │ ├── Carousel.md │ │ ├── HelloMd.md │ │ ├── HeroSection.md │ │ └── ScrollToTop.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── feed.rss │ │ ├── mockServiceWorker.js │ │ ├── pages │ │ │ ├── _meta.json │ │ │ ├── app-structure.md │ │ │ ├── build-editor-component.md │ │ │ ├── build-hello-world-component.md │ │ │ ├── components-intro.md │ │ │ ├── context-variables.md │ │ │ ├── forms.md │ │ │ ├── globals.md │ │ │ ├── glossary.md │ │ │ ├── helper-tags.md │ │ │ ├── hosted-deployment.md │ │ │ ├── howto │ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md │ │ │ │ ├── chain-a-refetch.md │ │ │ │ ├── debug-a-component.md │ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md │ │ │ │ ├── delegate-a-method.md │ │ │ │ ├── do-custom-form-validation.md │ │ │ │ ├── expose-a-method-from-a-component.md │ │ │ │ ├── filter-and-transform-data-from-an-api.md │ │ │ │ ├── group-items-in-list-by-a-property.md │ │ │ │ ├── handle-background-operations.md │ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md │ │ │ │ ├── make-a-set-of-equal-width-cards.md │ │ │ │ ├── make-a-table-responsive.md │ │ │ │ ├── make-navpanel-width-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── build-system.md │ ├── build-xmlui.md │ ├── component-behaviors.md │ ├── containers.md │ ├── glossary.md │ ├── index.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── standalone-app.md │ ├── state-management.md │ ├── ud-components.md │ └── xmlui-repo.md ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.tsx │ │ ├── SelectionStore │ │ │ ├── SelectionStore.md │ │ │ ├── SelectionStore.tsx │ │ │ └── SelectionStoreNative.tsx │ │ ├── Slider │ │ │ ├── Slider.md │ │ │ ├── Slider.module.scss │ │ │ ├── Slider.spec.ts │ │ │ ├── Slider.tsx │ │ │ └── SliderNative.tsx │ │ ├── Slot │ │ │ ├── Slot.md │ │ │ ├── Slot.spec.ts │ │ │ └── Slot.ts │ │ ├── SlotItem.tsx │ │ ├── SpaceFiller │ │ │ ├── SpaceFiller.md │ │ │ ├── SpaceFiller.module.scss │ │ │ ├── SpaceFiller.spec.ts │ │ │ ├── SpaceFiller.tsx │ │ │ └── SpaceFillerNative.tsx │ │ ├── Spinner │ │ │ ├── Spinner.md │ │ │ ├── Spinner.module.scss │ │ │ ├── Spinner.spec.ts │ │ │ ├── Spinner.tsx │ │ │ └── SpinnerNative.tsx │ │ ├── Splitter │ │ │ ├── HSplitter.md │ │ │ ├── HSplitter.spec.ts │ │ │ ├── Splitter.md │ │ │ ├── Splitter.module.scss │ │ │ ├── Splitter.spec.ts │ │ │ ├── Splitter.tsx │ │ │ ├── SplitterNative.tsx │ │ │ ├── utils.ts │ │ │ ├── VSplitter.md │ │ │ └── VSplitter.spec.ts │ │ ├── Stack │ │ │ ├── CHStack.md │ │ │ ├── CHStack.spec.ts │ │ │ ├── CVStack.md │ │ │ ├── CVStack.spec.ts │ │ │ ├── HStack.md │ │ │ ├── HStack.spec.ts │ │ │ ├── Stack.md │ │ │ ├── Stack.module.scss │ │ │ ├── Stack.spec.ts │ │ │ ├── Stack.tsx │ │ │ ├── StackNative.tsx │ │ │ ├── VStack.md │ │ │ └── VStack.spec.ts │ │ ├── StickyBox │ │ │ ├── StickyBox.md │ │ │ ├── StickyBox.module.scss │ │ │ ├── StickyBox.tsx │ │ │ └── StickyBoxNative.tsx │ │ ├── Switch │ │ │ ├── Switch.md │ │ │ ├── Switch.spec.ts │ │ │ └── Switch.tsx │ │ ├── Table │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── react-table-config.d.ts │ │ │ ├── Table.md │ │ │ ├── Table.module.scss │ │ │ ├── Table.spec.ts │ │ │ ├── Table.tsx │ │ │ ├── TableNative.tsx │ │ │ └── useRowSelection.tsx │ │ ├── TableOfContents │ │ │ ├── TableOfContents.module.scss │ │ │ ├── TableOfContents.spec.ts │ │ │ ├── TableOfContents.tsx │ │ │ └── TableOfContentsNative.tsx │ │ ├── Tabs │ │ │ ├── TabContext.tsx │ │ │ ├── TabItem.md │ │ │ ├── TabItem.tsx │ │ │ ├── TabItemNative.tsx │ │ │ ├── Tabs.md │ │ │ ├── Tabs.module.scss │ │ │ ├── Tabs.spec.ts │ │ │ ├── Tabs.tsx │ │ │ └── TabsNative.tsx │ │ ├── Text │ │ │ ├── Text.md │ │ │ ├── Text.module.scss │ │ │ ├── Text.spec.ts │ │ │ ├── Text.tsx │ │ │ └── TextNative.tsx │ │ ├── TextArea │ │ │ ├── TextArea.md │ │ │ ├── TextArea.module.scss │ │ │ ├── TextArea.spec.ts │ │ │ ├── TextArea.tsx │ │ │ ├── TextAreaNative.tsx │ │ │ ├── TextAreaResizable.tsx │ │ │ └── useComposedRef.ts │ │ ├── TextBox │ │ │ ├── TextBox.md │ │ │ ├── TextBox.module.scss │ │ │ ├── TextBox.spec.ts │ │ │ ├── TextBox.tsx │ │ │ └── TextBoxNative.tsx │ │ ├── Theme │ │ │ ├── NotificationToast.tsx │ │ │ ├── Theme.md │ │ │ ├── Theme.module.scss │ │ │ ├── Theme.spec.ts │ │ │ ├── Theme.tsx │ │ │ └── ThemeNative.tsx │ │ ├── TimeInput │ │ │ ├── TimeInput.md │ │ │ ├── TimeInput.module.scss │ │ │ ├── TimeInput.spec.ts │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeInputNative.tsx │ │ │ └── utils.ts │ │ ├── Timer │ │ │ ├── Timer.md │ │ │ ├── Timer.spec.ts │ │ │ ├── Timer.tsx │ │ │ └── TimerNative.tsx │ │ ├── Toggle │ │ │ ├── Toggle.module.scss │ │ │ └── Toggle.tsx │ │ ├── ToneChangerButton │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneChangerButton.spec.ts │ │ │ └── ToneChangerButton.tsx │ │ ├── ToneSwitch │ │ │ ├── ToneSwitch.md │ │ │ ├── ToneSwitch.module.scss │ │ │ ├── ToneSwitch.spec.ts │ │ │ ├── ToneSwitch.tsx │ │ │ └── ToneSwitchNative.tsx │ │ ├── Tooltip │ │ │ ├── Tooltip.md │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.spec.ts │ │ │ ├── Tooltip.tsx │ │ │ └── TooltipNative.tsx │ │ ├── Tree │ │ │ ├── testData.ts │ │ │ ├── Tree-dynamic.spec.ts │ │ │ ├── Tree-icons.spec.ts │ │ │ ├── Tree.md │ │ │ ├── Tree.spec.ts │ │ │ ├── TreeComponent.module.scss │ │ │ ├── TreeComponent.tsx │ │ │ └── TreeNative.tsx │ │ ├── TreeDisplay │ │ │ ├── TreeDisplay.md │ │ │ ├── TreeDisplay.module.scss │ │ │ ├── TreeDisplay.tsx │ │ │ └── TreeDisplayNative.tsx │ │ ├── ValidationSummary │ │ │ ├── ValidationSummary.module.scss │ │ │ └── ValidationSummary.tsx │ │ └── VisuallyHidden.tsx │ ├── components-core │ │ ├── abstractions │ │ │ ├── ComponentRenderer.ts │ │ │ ├── LoaderRenderer.ts │ │ │ ├── standalone.ts │ │ │ └── treeAbstractions.ts │ │ ├── action │ │ │ ├── actions.ts │ │ │ ├── APICall.tsx │ │ │ ├── FileDownloadAction.tsx │ │ │ ├── FileUploadAction.tsx │ │ │ ├── NavigateAction.tsx │ │ │ └── TimedAction.tsx │ │ ├── ApiBoundComponent.tsx │ │ ├── appContext │ │ │ ├── date-functions.ts │ │ │ ├── math-function.ts │ │ │ └── misc-utils.ts │ │ ├── AppContext.tsx │ │ ├── behaviors │ │ │ ├── Behavior.tsx │ │ │ └── CoreBehaviors.tsx │ │ ├── component-hooks.ts │ │ ├── ComponentDecorator.tsx │ │ ├── ComponentViewer.tsx │ │ ├── CompoundComponent.tsx │ │ ├── constants.ts │ │ ├── DebugViewProvider.tsx │ │ ├── descriptorHelper.ts │ │ ├── devtools │ │ │ ├── InspectorDialog.module.scss │ │ │ ├── InspectorDialog.tsx │ │ │ └── InspectorDialogVisibilityContext.tsx │ │ ├── EngineError.ts │ │ ├── event-handlers.ts │ │ ├── InspectorButton.module.scss │ │ ├── InspectorContext.tsx │ │ ├── interception │ │ │ ├── abstractions.ts │ │ │ ├── ApiInterceptor.ts │ │ │ ├── ApiInterceptorProvider.tsx │ │ │ ├── apiInterceptorWorker.ts │ │ │ ├── Backend.ts │ │ │ ├── Errors.ts │ │ │ ├── IndexedDb.ts │ │ │ ├── initMock.ts │ │ │ ├── InMemoryDb.ts │ │ │ ├── ReadonlyCollection.ts │ │ │ └── useApiInterceptorContext.tsx │ │ ├── loader │ │ │ ├── ApiLoader.tsx │ │ │ ├── DataLoader.tsx │ │ │ ├── ExternalDataLoader.tsx │ │ │ ├── Loader.tsx │ │ │ ├── MockLoaderRenderer.tsx │ │ │ └── PageableLoader.tsx │ │ ├── LoaderComponent.tsx │ │ ├── markup-check.ts │ │ ├── parts.ts │ │ ├── renderers.ts │ │ ├── rendering │ │ │ ├── AppContent.tsx │ │ │ ├── AppRoot.tsx │ │ │ ├── AppWrapper.tsx │ │ │ ├── buildProxy.ts │ │ │ ├── collectFnVarDeps.ts │ │ │ ├── ComponentAdapter.tsx │ │ │ ├── ComponentWrapper.tsx │ │ │ ├── Container.tsx │ │ │ ├── containers.ts │ │ │ ├── ContainerWrapper.tsx │ │ │ ├── ErrorBoundary.module.scss │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── InvalidComponent.module.scss │ │ │ ├── InvalidComponent.tsx │ │ │ ├── nodeUtils.ts │ │ │ ├── reducer.ts │ │ │ ├── renderChild.tsx │ │ │ ├── StandaloneComponent.tsx │ │ │ ├── StateContainer.tsx │ │ │ ├── UnknownComponent.module.scss │ │ │ ├── UnknownComponent.tsx │ │ │ └── valueExtractor.ts │ │ ├── reportEngineError.ts │ │ ├── RestApiProxy.ts │ │ ├── script-runner │ │ │ ├── asyncProxy.ts │ │ │ ├── AttributeValueParser.ts │ │ │ ├── bannedFunctions.ts │ │ │ ├── BindingTreeEvaluationContext.ts │ │ │ ├── eval-tree-async.ts │ │ │ ├── eval-tree-common.ts │ │ │ ├── eval-tree-sync.ts │ │ │ ├── ParameterParser.ts │ │ │ ├── process-statement-async.ts │ │ │ ├── process-statement-common.ts │ │ │ ├── process-statement-sync.ts │ │ │ ├── ScriptingSourceTree.ts │ │ │ ├── simplify-expression.ts │ │ │ ├── statement-queue.ts │ │ │ └── visitors.ts │ │ ├── StandaloneApp.tsx │ │ ├── StandaloneExtensionManager.ts │ │ ├── TableOfContentsContext.tsx │ │ ├── theming │ │ │ ├── _themes.scss │ │ │ ├── component-layout-resolver.ts │ │ │ ├── extendThemeUtils.ts │ │ │ ├── hvar.ts │ │ │ ├── layout-resolver.ts │ │ │ ├── parse-layout-props.ts │ │ │ ├── StyleContext.tsx │ │ │ ├── StyleRegistry.ts │ │ │ ├── ThemeContext.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ ├── themes │ │ │ │ ├── base-utils.ts │ │ │ │ ├── palette.ts │ │ │ │ ├── root.ts │ │ │ │ ├── solid.ts │ │ │ │ ├── theme-colors.ts │ │ │ │ └── xmlui.ts │ │ │ ├── themeVars.module.scss │ │ │ ├── themeVars.ts │ │ │ ├── transformThemeVars.ts │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── actionUtils.ts │ │ │ ├── audio-utils.ts │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /xmlui/src/parsers/scripting/TokenTrait.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { TokenType } from "./TokenType"; 2 | 3 | // Describes the special traits of a token 4 | type TokenTrait = { 5 | expressionStart?: boolean; 6 | isAssignment?: boolean; 7 | canBeUnary?: boolean; 8 | keywordLike?: boolean; 9 | isPropLiteral?: boolean; 10 | }; 11 | 12 | // Individual traits of tokens 13 | export const tokenTraits: Record<TokenType, TokenTrait> = { 14 | [TokenType.Eof]: {}, 15 | [TokenType.Ws]: {}, 16 | [TokenType.DollarLBrace]: {}, 17 | [TokenType.Backtick]: {expressionStart: true}, 18 | [TokenType.BlockComment]: {}, 19 | [TokenType.EolComment]: {}, 20 | [TokenType.Unknown]: {}, 21 | [TokenType.LParent]: { expressionStart: true }, 22 | [TokenType.RParent]: {}, 23 | [TokenType.Identifier]: { expressionStart: true, keywordLike: true, isPropLiteral: true }, 24 | [TokenType.Exponent]: {}, 25 | [TokenType.Divide]: {}, 26 | [TokenType.Multiply]: {}, 27 | [TokenType.Remainder]: {}, 28 | [TokenType.Plus]: { expressionStart: true, canBeUnary: true }, 29 | [TokenType.Minus]: { expressionStart: true, canBeUnary: true }, 30 | [TokenType.BitwiseXor]: {}, 31 | [TokenType.BitwiseOr]: {}, 32 | [TokenType.LogicalOr]: {}, 33 | [TokenType.BitwiseAnd]: {}, 34 | [TokenType.LogicalAnd]: {}, 35 | [TokenType.Assignment]: { isAssignment: true }, 36 | [TokenType.AddAssignment]: { isAssignment: true }, 37 | [TokenType.SubtractAssignment]: { isAssignment: true }, 38 | [TokenType.ExponentAssignment]: { isAssignment: true }, 39 | [TokenType.MultiplyAssignment]: { isAssignment: true }, 40 | [TokenType.DivideAssignment]: { isAssignment: true }, 41 | [TokenType.RemainderAssignment]: { isAssignment: true }, 42 | [TokenType.ShiftLeftAssignment]: { isAssignment: true }, 43 | [TokenType.ShiftRightAssignment]: { isAssignment: true }, 44 | [TokenType.SignedShiftRightAssignment]: { isAssignment: true }, 45 | [TokenType.BitwiseAndAssignment]: { isAssignment: true }, 46 | [TokenType.BitwiseXorAssignment]: { isAssignment: true }, 47 | [TokenType.BitwiseOrAssignment]: { isAssignment: true }, 48 | [TokenType.LogicalAndAssignment]: { isAssignment: true }, 49 | [TokenType.LogicalOrAssignment]: { isAssignment: true }, 50 | [TokenType.NullCoalesceAssignment]: { isAssignment: true }, 51 | [TokenType.Semicolon]: {}, 52 | [TokenType.Comma]: {}, 53 | [TokenType.Colon]: {}, 54 | [TokenType.LSquare]: { expressionStart: true }, 55 | [TokenType.RSquare]: {}, 56 | [TokenType.QuestionMark]: {}, 57 | [TokenType.NullCoalesce]: {}, 58 | [TokenType.OptionalChaining]: {}, 59 | [TokenType.BinaryNot]: { expressionStart: true, canBeUnary: true }, 60 | [TokenType.LBrace]: { expressionStart: true }, 61 | [TokenType.RBrace]: {}, 62 | [TokenType.Equal]: {}, 63 | [TokenType.StrictEqual]: {}, 64 | [TokenType.LogicalNot]: { expressionStart: true, canBeUnary: true }, 65 | [TokenType.NotEqual]: {}, 66 | [TokenType.StrictNotEqual]: {}, 67 | [TokenType.LessThan]: {}, 68 | [TokenType.LessThanOrEqual]: {}, 69 | [TokenType.ShiftLeft]: {}, 70 | [TokenType.GreaterThan]: {}, 71 | [TokenType.GreaterThanOrEqual]: {}, 72 | [TokenType.ShiftRight]: {}, 73 | [TokenType.SignedShiftRight]: {}, 74 | [TokenType.Dot]: {}, 75 | [TokenType.Spread]: { expressionStart: true, isPropLiteral: true }, 76 | [TokenType.Global]: { expressionStart: true }, 77 | [TokenType.DecimalLiteral]: { expressionStart: true, isPropLiteral: true}, 78 | [TokenType.HexadecimalLiteral]: { expressionStart: true, isPropLiteral: true }, 79 | [TokenType.BinaryLiteral]: { expressionStart: true, isPropLiteral: true }, 80 | [TokenType.RealLiteral]: { expressionStart: true, isPropLiteral: true }, 81 | [TokenType.StringLiteral]: { expressionStart: true, isPropLiteral: true }, 82 | [TokenType.IncOp]: { expressionStart: true }, 83 | [TokenType.DecOp]: { expressionStart: true }, 84 | [TokenType.Infinity]: { expressionStart: true, keywordLike: true }, 85 | [TokenType.NaN]: { expressionStart: true, keywordLike: true }, 86 | [TokenType.True]: { expressionStart: true, keywordLike: true, isPropLiteral: true}, 87 | [TokenType.False]: { expressionStart: true, keywordLike: true, isPropLiteral: true}, 88 | [TokenType.Typeof]: { expressionStart: true, canBeUnary: true, keywordLike: true }, 89 | [TokenType.Null]: { expressionStart: true, keywordLike: true }, 90 | [TokenType.Undefined]: { expressionStart: true, keywordLike: true }, 91 | [TokenType.In]: { keywordLike: true }, 92 | [TokenType.Let]: { keywordLike: true }, 93 | [TokenType.Const]: { keywordLike: true }, 94 | [TokenType.Var]: { keywordLike: true }, 95 | [TokenType.If]: { keywordLike: true }, 96 | [TokenType.Else]: { keywordLike: true }, 97 | [TokenType.Arrow]: { keywordLike: true }, 98 | [TokenType.Return]: { keywordLike: true }, 99 | [TokenType.Break]: { keywordLike: true }, 100 | [TokenType.Continue]: { keywordLike: true }, 101 | [TokenType.Do]: { keywordLike: true }, 102 | [TokenType.While]: { keywordLike: true }, 103 | [TokenType.For]: { keywordLike: true }, 104 | [TokenType.Of]: { keywordLike: true }, 105 | [TokenType.Throw]: { keywordLike: true }, 106 | [TokenType.Try]: { keywordLike: true }, 107 | [TokenType.Catch]: { keywordLike: true }, 108 | [TokenType.Finally]: { keywordLike: true }, 109 | [TokenType.Switch]: { keywordLike: true }, 110 | [TokenType.Case]: { keywordLike: true }, 111 | [TokenType.Default]: { keywordLike: true }, 112 | [TokenType.Delete]: { expressionStart: true, canBeUnary: true, keywordLike: true }, 113 | [TokenType.Function]: { keywordLike: true, expressionStart: true }, 114 | [TokenType.As]: { keywordLike: true }, 115 | }; 116 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Tabs/Tabs.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./Tabs.module.scss"; 2 | 3 | import { parseScssVar } from "../../components-core/theming/themeVars"; 4 | import { createComponentRenderer } from "../../components-core/renderers"; 5 | 6 | import { MemoizedItem } from "../container-helpers"; 7 | import { Tabs, defaultProps } from "./TabsNative"; 8 | import { createMetadata, d, dComponent, dDidChange } from "../metadata-helpers"; 9 | 10 | const COMP = "Tabs"; 11 | 12 | export const TabsMd = createMetadata({ 13 | status: "experimental", 14 | description: 15 | "`Tabs` enables users to switch among content panels using clickable tab headers. " + 16 | "It provides an efficient way to present multiple related sections in a single " + 17 | "interface area, with each tab containing distinct content defined by " + 18 | "[TabItem](/components/TabItem) components.", 19 | props: { 20 | activeTab: d( 21 | `This property indicates the index of the active tab. The indexing starts from 0, ` + 22 | `representing the starting (leftmost) tab. If not set, the first tab is selected by default.`, 23 | ), 24 | orientation: { 25 | description: 26 | `This property indicates the orientation of the component. In horizontal orientation, ` + 27 | `the tab sections are laid out on the left side of the content panel, while in vertical ` + 28 | `orientation, the buttons are at the top. Note: This property is ignored when ` + 29 | `accordionView is set to true.`, 30 | availableValues: ["horizontal", "vertical"], 31 | defaultValue: defaultProps.orientation, 32 | valueType: "string", 33 | }, 34 | tabAlignment: { 35 | description: 36 | `This property controls how tabs are aligned within the tab header container in ` + 37 | `horizontal orientation. Use 'start' to align tabs to the left, 'end' to align to the ` + 38 | `right, 'center' to center the tabs, and 'stretch' to make tabs fill the full width ` + 39 | `of the header. Note: This property is ignored when orientation is set to 'vertical' ` + 40 | `or when accordionView is enabled.`, 41 | availableValues: ["start", "end", "center", "stretch"], 42 | defaultValue: defaultProps.tabAlignment, 43 | valueType: "string", 44 | }, 45 | accordionView: { 46 | description: 47 | `When enabled, displays tabs in an accordion-like view where tab headers are stacked ` + 48 | `vertically and only the active tab's content is visible. Each tab header remains visible ` + 49 | `and clicking a header opens its content while closing others. When enabled, the ` + 50 | `orientation property is ignored.`, 51 | defaultValue: defaultProps.accordionView, 52 | valueType: "boolean", 53 | }, 54 | headerTemplate: { 55 | ...dComponent(`This property declares the template for the clickable tab area.`), 56 | }, 57 | }, 58 | events: { 59 | didChange: dDidChange(COMP), 60 | }, 61 | apis: { 62 | next: { 63 | description: `This method selects the next tab. If the current tab is the last one, it wraps around to the first tab.`, 64 | signature: "next(): void", 65 | }, 66 | prev: { 67 | description: `This method selects the previous tab. If the current tab is the first one, it wraps around to the last tab.`, 68 | signature: "prev(): void", 69 | }, 70 | setActiveTabIndex: { 71 | description: `This method sets the active tab by index (0-based).`, 72 | signature: "setActiveTabIndex(index: number): void", 73 | }, 74 | setActiveTabById: { 75 | description: `This method sets the active tab by its ID.`, 76 | signature: "setActiveTabById(id: string): void", 77 | }, 78 | }, 79 | themeVars: parseScssVar(styles.themeVars), 80 | defaultThemeVars: { 81 | // [`backgroundColor-${COMP}`]: "transparent", 82 | [`borderStyle-${COMP}`]: "solid", 83 | [`borderColor-${COMP}`]: "$borderColor", 84 | [`borderColor-active-${COMP}`]: "$color-primary", 85 | [`borderWidth-${COMP}`]: "2px", 86 | // [`backgroundColor-trigger-${COMP}`]: "transparent", 87 | [`backgroundColor-trigger-${COMP}--hover`]: "$color-surface-100", 88 | [`padding-trigger-${COMP}`]: "$space-4", 89 | // [`backgroundColor-list-${COMP}`]: "$color-primary-50", 90 | // [`textColor-trigger-${COMP}`]: "$color-primary-100", 91 | }, 92 | }); 93 | 94 | export const tabsComponentRenderer = createComponentRenderer( 95 | COMP, 96 | TabsMd, 97 | ({ extractValue, node, renderChild, className, registerComponentApi, lookupEventHandler }) => { 98 | return ( 99 | <Tabs 100 | id={node?.uid} 101 | className={className} 102 | headerRenderer={ 103 | node?.props?.headerTemplate 104 | ? (item) => ( 105 | <MemoizedItem 106 | node={node.props.headerTemplate! as any} 107 | itemKey="$header" 108 | contextVars={{ 109 | $header: item, 110 | }} 111 | renderChild={renderChild} 112 | /> 113 | ) 114 | : undefined 115 | } 116 | activeTab={extractValue(node.props?.activeTab)} 117 | orientation={extractValue(node.props?.orientation)} 118 | tabAlignment={extractValue(node.props?.tabAlignment)} 119 | accordionView={extractValue(node.props?.accordionView)} 120 | onDidChange={lookupEventHandler("didChange")} 121 | registerComponentApi={registerComponentApi} 122 | > 123 | {renderChild(node.children)} 124 | </Tabs> 125 | ); 126 | }, 127 | ); 128 | ``` -------------------------------------------------------------------------------- /xmlui/dev-docs/release-method.md: -------------------------------------------------------------------------------- ```markdown 1 | **Overall Philosophy:** 2 | 3 | * **`main` branch:** The source of truth for all development. 4 | * **Changesets:** Used for managing versioning and changelogs. 5 | * Can be added manually (`npx changeset add`). 6 | * Or, ideally, auto-generated from Conventional Commits on PRs (see `add-changeset.yml` below). 7 | * **Beta Releases:** Fully automated on every merge to `main` that includes new changesets. Published to npm with a `beta` tag. 8 | * **Stable Releases:** A manually triggered process that: 9 | 1. Creates a "Version Packages" Pull Request for review. 10 | 2. Upon merging this PR, publishes packages to npm (with the `latest` tag) and creates GitHub Releases. 11 | * **Handling Race Conditions for Stable Release:** If `main` changes significantly after the "Version Packages" PR is created, the safest approach is to close that PR and re-trigger the stable release process to generate a new, up-to-date "Version Packages" PR. 12 | 13 | --- 14 | 15 | **The Process Steps:** 16 | 17 | 1. **Development:** 18 | * Developers create feature branches from `main`. 19 | * They make code changes and write **Conventional Commit messages**. 20 | * **(Automation Option):** When a PR is opened/updated, the `add-changeset.yml` workflow runs, analyzes conventional commits, and automatically adds/updates `.changeset/*.md` files to the PR branch. 21 | * **(Manual Option):** Developer runs `npx changeset add` before committing changes that require a version bump, and commits the generated changeset file. 22 | * Developer opens/updates a Pull Request to `main`. 23 | 24 | 2. **Pull Request Merged to `main`:** 25 | * The `beta-release.yml` workflow triggers. 26 | * It consumes any `.changeset` files merged from the PR. 27 | * It versions affected packages with a beta suffix (e.g., `1.0.1-beta.shortsha`). 28 | * It publishes these beta versions to npm under the `beta` dist-tag. 29 | * It commits and pushes these snapshot version changes back to `main`. 30 | 31 | 3. **Preparing for a Stable Release (Manual Trigger):** 32 | * A designated person (release manager) decides it's time for a stable release. 33 | * **Pre-check:** Briefly check if `main` is relatively stable or communicate a short "quiet period" for merges to `main` to minimize conflicts with the version PR. 34 | * Go to GitHub Actions -> "Stable Release Process" workflow -> Run workflow. 35 | * The `stable-release.yml` workflow's `create_version_pr` job runs: 36 | * It uses `changesets/action` to consume all current `.changeset` files on `main`. 37 | * It generates version bumps, updates `CHANGELOG.md` files. 38 | * It commits these changes to a new branch (e.g., `changeset-release/main`). 39 | * It opens a "Version Packages for Release" Pull Request to `main`. 40 | 41 | 4. **Reviewing and Merging the "Version Packages" PR:** 42 | * The team reviews this PR. It shows exactly which packages will be versioned and what their changelogs will contain for the stable release. 43 | * **Critical Check:** Before merging, verify if `main` has received significant new changes (especially new changesets that have triggered new beta releases) *since this "Version Packages" PR was created*. 44 | * **If `main` has changed significantly:** Close the current "Version Packages" PR and its branch. Go back to Step 3 and re-trigger the "Stable Release Process" workflow to generate a fresh "Version Packages" PR based on the latest `main`. 45 | * **If `main` is stable or changes are minor/unrelated:** Proceed to merge the "Version Packages" PR. 46 | 47 | 5. **"Version Packages" PR is Merged to `main`:** 48 | * The `stable-release.yml` workflow's `publish_and_release` job triggers. 49 | * It uses `changesets/action` to: 50 | * Publish the versioned packages (now merged into `main`) to npm, this time to the `latest` dist-tag. 51 | * Create Git tags for each published package version. 52 | * Create corresponding GitHub Releases, populating them with content from the `CHANGELOG.md` files. 53 | 54 | --- 55 | 56 | 57 | **1. `.github/workflows/add-changeset.yml`** 58 | *Automates creation of changeset files from Conventional Commits in PRs.* 59 | 60 | ```yaml 61 | name: Add Changeset from Conventional Commits (NPM) 62 | 63 | on: 64 | pull_request: 65 | types: [opened, synchronize, reopened, ready_for_review] 66 | 67 | permissions: 68 | contents: write 69 | pull-requests: read 70 | 71 | jobs: 72 | add_changeset: 73 | if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' 74 | runs-on: ubuntu-latest 75 | steps: 76 | - name: Checkout Repo 77 | uses: actions/checkout@v4 78 | with: 79 | fetch-depth: 0 80 | 81 | - name: Setup Node.js 82 | uses: actions/setup-node@v4 83 | with: 84 | node-version: '18' # Or your Node.js version 85 | cache: 'npm' # Use npm cache 86 | 87 | - name: Install Dependencies 88 | run: npm ci # Uses package-lock.json 89 | 90 | - name: Create Changeset File 91 | uses: tripsit/conventional-changesets-action@v4 92 | with: 93 | github-token: ${{ secrets.GITHUB_TOKEN }} 94 | commit-message: "chore: add generated changeset(s) [skip ci]" 95 | skip-ci: "true" 96 | # Configure type mappings if needed 97 | ``` 98 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/rendering/renderChild.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { ReactNode } from "react"; 2 | 3 | import type { ComponentDef } from "../../abstractions/ComponentDefs"; 4 | import type { InnerRendererContext } from "../abstractions/ComponentRenderer"; 5 | import type { ComponentCleanupFn } from "../rendering/ContainerWrapper"; 6 | import { shouldKeep, extractParam } from "../utils/extractParam"; 7 | import { ComponentWrapper } from "./ComponentWrapper"; 8 | import type { StatePartChangedFn } from "./ContainerWrapper"; 9 | 10 | /** 11 | * This type represents the context in which the React component belonging to a 12 | * particular component definition is rendered with the `renderChild()` function. 13 | */ 14 | export interface ChildRendererContext extends InnerRendererContext { 15 | statePartChanged: StatePartChangedFn; 16 | cleanup: ComponentCleanupFn; 17 | } 18 | 19 | /** 20 | * This function is the jolly-joker of the rendering process. It renders a child component 21 | * based on the specified context, which contains the component's definition, the current state, 22 | * and other necessary information. 23 | * 24 | * The function checks a few special cases: 25 | * - <Slot> with a single text node child: it renders the text in the context of the parent component. 26 | * - CDATA text nodes: it renders the text as is without parsing it. 27 | * - TextNode: it extracts the text from the node and renders it. 28 | * 29 | * In other cases, it extracts the component's ID and renders the component as a <ComponentNode>. 30 | * 31 | * As this function passes itself as a renderChild function to the <ComponentNode>, it can render 32 | * nested components recursively. 33 | */ 34 | export function renderChild({ 35 | node, 36 | state, 37 | dispatch, 38 | appContext, 39 | lookupAction, 40 | lookupSyncCallback, 41 | registerComponentApi, 42 | renderChild, 43 | statePartChanged, 44 | layoutContext, 45 | parentRenderContext, 46 | memoedVarsRef, 47 | cleanup, 48 | uidInfoRef, 49 | }: ChildRendererContext): ReactNode { 50 | // --- Render only visible components 51 | if (!shouldKeep(node.when, state, appContext)) { 52 | return null; 53 | } 54 | 55 | // --- We do not parse text nodes specified with CDATA to avoid whitespace collapsing 56 | const nodeValue = (node.props as any)?.value; 57 | if (node.type === "TextNodeCData") { 58 | return nodeValue ?? ""; 59 | } 60 | 61 | // --- A TextNode value may contain nexted expressions, so we extract it. 62 | if (node.type === "TextNode") { 63 | const extractedValue = extractParam(state, nodeValue, appContext, true); 64 | return typeof extractedValue === "boolean" ? extractedValue.toString() : extractedValue; 65 | } 66 | 67 | // --- Rendering a Slot requires some preparations, as TextNode and 68 | // --- TextNodeCData are virtual nodes. Also, slots may have default templates 69 | // --- to render when no slot children are specified. The following section 70 | // --- handles these cases. 71 | if (node.type === "Slot") { 72 | // --- Check for special Slot cases 73 | let slotChildren: ComponentDef | ComponentDef[]; 74 | const templateName = node.props?.name; 75 | // console.log("templateName", templateName); 76 | if (templateName) { 77 | // --- Let's check the validity of the slot name 78 | if (!templateName.endsWith("Template")) { 79 | throw new Error( 80 | `Slot name '${templateName}' is not valid. ` + 81 | "A named slot should use a name ending with 'Template'.", 82 | ); 83 | } 84 | 85 | // --- Named slot: use a template property from the parent component 86 | slotChildren = parentRenderContext?.props?.[templateName]; 87 | } else { 88 | // --- Children slot: use the children of the parent component 89 | slotChildren = parentRenderContext?.children; 90 | } 91 | 92 | if (!slotChildren) { 93 | // --- No children to render, let's try the default slot template (if there is any) 94 | slotChildren = node.children; 95 | } 96 | 97 | if (slotChildren) { 98 | const toRender = Array.isArray(slotChildren) ? slotChildren : [slotChildren]; 99 | // --- Check for the virtual nodes. At this point, parentRendererContext is 100 | // --- undefined when the parent does not provide slot children. In this case, 101 | // --- the ComponentBed component will render the default slot template. 102 | if (toRender.length === 1 && parentRenderContext) { 103 | if (toRender[0].type === "TextNodeCData" || toRender[0].type === "TextNode") { 104 | // --- Preserve the text and render it in the parent context 105 | return parentRenderContext.renderChild(toRender); 106 | } 107 | } 108 | } 109 | } 110 | 111 | // --- In other cases, we extract the component ID, and then render the component. 112 | // --- A component's ID is generally a string with identifier syntax. However, some 113 | // --- internal components have IDs with expressions, so we evaluate them. 114 | const key = extractParam(state, node.uid, appContext, true); 115 | 116 | return ( 117 | <ComponentWrapper 118 | key={key} 119 | resolvedKey={key} 120 | node={node} 121 | cleanup={cleanup} 122 | statePartChanged={statePartChanged} 123 | memoedVarsRef={memoedVarsRef} 124 | state={state} 125 | dispatch={dispatch} 126 | appContext={appContext} 127 | lookupAction={lookupAction} 128 | lookupSyncCallback={lookupSyncCallback} 129 | registerComponentApi={registerComponentApi} 130 | renderChild={renderChild} 131 | layoutContext={layoutContext} 132 | parentRenderContext={parentRenderContext} 133 | uidInfoRef={uidInfoRef} 134 | /> 135 | ); 136 | } 137 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/utils/LruCache.ts: -------------------------------------------------------------------------------- ```typescript 1 | // ==================================================================================================================== 2 | // Types to implement an LRU cache we use to provide stable 3 | // Implementation source: https://www.nickang.com/2021-11-28-how-to-implement-an-lru-cache-in-javascript/ 4 | 5 | /** 6 | * A single node of the LRU cache 7 | */ 8 | class DoublyLinkedNode { 9 | prev: DoublyLinkedNode | undefined; 10 | next: DoublyLinkedNode | undefined; 11 | constructor(public readonly value: any, public readonly key: string) { 12 | this.next = undefined; 13 | this.prev = undefined; 14 | } 15 | } 16 | 17 | /** 18 | * We keep values in the LRU cache in a doubly linked list 19 | */ 20 | class DoublyLinkedList { 21 | head: DoublyLinkedNode | undefined; 22 | tail: DoublyLinkedNode | undefined; 23 | size = 0; 24 | constructor() { 25 | this.head = undefined; 26 | this.tail = undefined; 27 | this.size = 0; 28 | } 29 | 30 | /** 31 | * Adds a new node to the head of the list 32 | * @param node The node to add to head of list 33 | */ 34 | unshift(node: DoublyLinkedNode) { 35 | // case 1: there is only a root node in the list 36 | // point node.prev to root node 37 | // point node.next to undefined 38 | // point DoublyLinkedList head to node 39 | // point DoublyLinkedList tail to node 40 | // increment DoublyLinkedList size by 1 41 | // case 2: there are data nodes in the list 42 | // point head node.prev to node 43 | // point node.next to head node 44 | // point node.prev to root node 45 | // point DoublyLinkedList head to node 46 | // increment DoublyLinkedList size by 1 47 | 48 | if (this.size === 0) { 49 | // case 1 50 | this.head = node; 51 | this.tail = node; 52 | this.size++; 53 | } else { 54 | // case 2 55 | this.head!.prev = node; 56 | node.next = this.head; 57 | node.prev = undefined; 58 | this.head = node; 59 | this.size++; 60 | } 61 | } 62 | 63 | /** 64 | * Remove least recently used node from tail 65 | */ 66 | pop() { 67 | const node = this.tail; 68 | if (!node) { 69 | return undefined; 70 | } else if (this.head === this.tail) { 71 | this.head = undefined; 72 | this.tail = undefined; 73 | } else { 74 | this.tail!.prev!.next = undefined; 75 | } 76 | this.tail = node.prev; 77 | this.size--; 78 | return node; 79 | } 80 | 81 | /** 82 | * Moves the specified node to the head 83 | */ 84 | moveToHead(node: DoublyLinkedNode) { 85 | if (node === this.head) { 86 | return; 87 | } 88 | 89 | if (node === this.head && node === this.tail) { 90 | return; 91 | } 92 | 93 | if (node === this.tail) { 94 | // set tail to tail node.prev 95 | this.tail = this.tail.prev; 96 | // set new tail node.next to undefined 97 | this.tail!.next = undefined; 98 | // set node.next to current head 99 | node.next = this.head; 100 | // set current head node.prev to node 101 | this.head!.prev = node; 102 | // set head to node 103 | this.head = node; 104 | // set node.prev to undefined 105 | node.prev = undefined; 106 | } else { 107 | // set node.prev.next to node.next 108 | node.prev!.next = node.next; 109 | // set node.next.prev to node.prev 110 | node.next!.prev = node.prev; 111 | // set node.next to current head 112 | node.next = this.head; 113 | // set current head node.prev to node 114 | this.head!.prev = node; 115 | // set node as new head 116 | this.head = node; 117 | // set node.prev to undefined 118 | node.prev = undefined; 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * This class implements the LRU cache 125 | */ 126 | export class LRUCache { 127 | private store: Record<string, DoublyLinkedNode> = {}; 128 | list = new DoublyLinkedList(); 129 | constructor(public readonly maxSize: number) {} 130 | 131 | /** 132 | * Gets the number of items stored in the cache 133 | */ 134 | get length(): number { 135 | return this.list.size; 136 | } 137 | 138 | /** 139 | * Gets the value with the specified key 140 | * @param key 141 | */ 142 | get(key: string): any { 143 | // case 1: node with this key found 144 | // update position of node to head of DoublyLinkedList 145 | // return existing node 146 | // case 2: node with this key not found (i.e. doesn't exist) 147 | // return undefined 148 | 149 | const existingNode = this.store[key]; 150 | if (existingNode) { 151 | this.list.moveToHead(existingNode); 152 | } 153 | return existingNode?.value; 154 | } 155 | 156 | /** 157 | * Sets the value for a particular key within the cache 158 | * @param key Cache item key 159 | * @param value Cache item value 160 | */ 161 | set(key: string, value: any): void { 162 | // case 1: search and found existing node with this key 163 | // use get() to obtain node 164 | // if not exist, go to case 2 165 | // if exist, let get() handle re-ordering in DoublyLinkedList 166 | // set node to hold new value 167 | const existingNode = this.get(key); 168 | if (existingNode) { 169 | existingNode.value = value; 170 | } 171 | 172 | // case 2: search and couldn't find existing node with this key 173 | // create new node 174 | // insert key-value pair into store 175 | // insert as new head of DoublyLinkedList 176 | 177 | const newNode = new DoublyLinkedNode(value, key); 178 | this.store[key] = newNode; 179 | this.list.unshift(newNode); 180 | 181 | if (this.hasReachedMaxSize()) { 182 | this.evictLeastRecentlyUsed(); 183 | } 184 | } 185 | 186 | hasReachedMaxSize() { 187 | return this.list.size === this.maxSize + 1; 188 | } 189 | 190 | evictLeastRecentlyUsed() { 191 | const evictedNode = this.list.pop(); 192 | delete this.store[evictedNode!.key]; 193 | } 194 | } 195 | ``` -------------------------------------------------------------------------------- /xmlui/conventions/mermaid.md: -------------------------------------------------------------------------------- ```markdown 1 | # Mermaid Diagram Conventions 2 | 3 | This document captures best practices and lessons learned for creating effective Mermaid diagrams in XMLUI documentation. 4 | 5 | ## Block Diagrams vs Flowcharts 6 | 7 | ### When to Use Block Diagrams 8 | - **Container representations**: Block diagrams excel at showing rectangular containers with structured content 9 | - **Clean layouts**: Better for side-by-side arrangements without connecting lines 10 | - **Text-heavy content**: Superior handling of multi-line text and tree structures 11 | - **Consistent styling**: More predictable alignment and spacing 12 | 13 | ### When to Use Flowcharts 14 | - **Process flows**: When you need arrows and connections between elements 15 | - **Decision trees**: For branching logic and conditional paths 16 | - **Simple node relationships**: Basic parent-child relationships with connections 17 | 18 | ## Block Diagram Best Practices 19 | 20 | ### Basic Structure 21 | ```mermaid 22 | %%{init: {"block": {"padding": 8}}}%% 23 | block-beta 24 | columns 3 25 | 26 | block:GroupName:3 27 | ITEM["Content goes here"] 28 | end 29 | 30 | ITEM1["Item 1"] 31 | ITEM2["Item 2"] 32 | ITEM3["Item 3"] 33 | ``` 34 | 35 | ### Configuration 36 | - Always include `%%{init: {"block": {"padding": 8}}}%%` for consistent padding 37 | - Use `columns N` to control horizontal layout (1 for single column, 3 for three-column grid) 38 | - Use `block:Name:N` syntax to span multiple columns 39 | 40 | ### Content Formatting 41 | 42 | #### Left-Aligned Text 43 | ```mermaid 44 | CONTAINER["<div style='text-align: left; width: 100%'>Title<br/>Content line 1<br/>Content line 2</div>"] 45 | ``` 46 | 47 | #### Tree Structures 48 | Use ` ` entities for precise indentation: 49 | ```mermaid 50 | ITEM["<div style='text-align: left; width: 100%'>🏠 Container<br/>💡 State:<br/> ├─ item1: value<br/> └─ item2: value<br/>🌳 Tree:<br/> Root:<br/> ├─ Child 1<br/> └─ Child 2</div>"] 51 | ``` 52 | 53 | #### Spacing Guidelines 54 | - **Root level items**: 5 ` ` entities to align with section headers 55 | - **Tree children**: 7-8 ` ` entities for proper indentation under parents 56 | - **Section headers**: Align with icon text, not the icon itself 57 | 58 | ## Styling 59 | 60 | ### CSS Classes 61 | ```mermaid 62 | classDef rootContainer fill:#f0f0f0,stroke:#888,stroke-width:2px,color:#333 63 | classDef innerContainer fill:#f8f8f8,stroke:#aaa,stroke-width:1px,color:#333 64 | 65 | class ITEM1,ITEM2 rootContainer 66 | class ITEM3 innerContainer 67 | ``` 68 | 69 | ### Background Colors 70 | - **Light containers**: `fill:#f0f0f0` with `stroke:#888` 71 | - **Inner elements**: `fill:#f8f8f8` with `stroke:#aaa` 72 | - **Text color**: Always use `color:#333` for readability 73 | 74 | ### Padding 75 | - Apply padding directly in HTML: `<div style='padding: 0 15px;'>` for horizontal-only padding 76 | - Avoid CSS class padding - it may not work consistently in all Mermaid versions 77 | 78 | ## Layout Patterns 79 | 80 | ### Single Container 81 | ```mermaid 82 | %%{init: {"block": {"padding": 8}}}%% 83 | block-beta 84 | columns 1 85 | 86 | CONTAINER["<div style='text-align: left; width: 100%'>Content</div>"] 87 | 88 | classDef rootContainer fill:#f0f0f0,stroke:#888,stroke-width:2px,color:#333 89 | class CONTAINER rootContainer 90 | ``` 91 | 92 | ### Multi-Container Grid 93 | ```mermaid 94 | %%{init: {"block": {"padding": 8}}}%% 95 | block-beta 96 | columns 3 97 | 98 | block:Header:3 99 | HEADER["Header spanning full width"] 100 | end 101 | 102 | ITEM1["Item 1"] 103 | ITEM2["Item 2"] 104 | ITEM3["Item 3"] 105 | ``` 106 | 107 | ## Common Pitfalls 108 | 109 | ### Text Alignment Issues 110 | - **Problem**: Content appears centered even with left-align styling 111 | - **Solution**: Always use `<div style='text-align: left; width: 100%'>` wrapper 112 | 113 | ### Inconsistent Spacing 114 | - **Problem**: Tree items don't align properly 115 | - **Solution**: Count ` ` entities carefully - use consistent patterns (5 for root, 7-8 for children) 116 | 117 | ### Styling Not Applied 118 | - **Problem**: CSS classes don't affect appearance 119 | - **Solution**: Apply styles directly in HTML divs rather than relying solely on CSS classes 120 | 121 | ### Layout Breaks 122 | - **Problem**: Items don't arrange as expected 123 | - **Solution**: Use `columns N` consistently and test with different content lengths 124 | 125 | ## Icons and Emojis 126 | 127 | ### Standard Icons 128 | - 🏠 - Containers 129 | - 💡 - State/Data 130 | - 🌳 - Trees/Hierarchies 131 | - 📦 - Components 132 | - ⚙️ - Configuration 133 | 134 | ### Tree Symbols 135 | - `├─` - Branch item 136 | - `└─` - Final branch item 137 | - Use these consistently for hierarchical structures 138 | 139 | ## Testing Tips 140 | 141 | 1. **Always preview**: Mermaid rendering can vary between environments 142 | 2. **Check alignment**: Verify tree structures align properly at different zoom levels 143 | 3. **Test content length**: Ensure layout works with both short and long text 144 | 4. **Validate styling**: Confirm background colors and padding appear correctly 145 | 5. **Cross-platform**: Test on different operating systems if possible 146 | 147 | ## Migration from Flowcharts 148 | 149 | When converting existing flowcharts to block diagrams: 150 | 151 | 1. Replace `graph TB/LR` with `block-beta` 152 | 2. Remove connection arrows (`---`, `-->`) 153 | 3. Convert subgraphs to block groups 154 | 4. Add HTML div wrappers for text alignment 155 | 5. Apply consistent CSS styling 156 | 6. Test tree structure indentation 157 | 158 | This approach provides cleaner, more maintainable diagrams with better text formatting capabilities. ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/LineChart/LineChart.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { defaultProps, LineChart } from "./LineChartNative"; 2 | import { createComponentRenderer } from "../../../components-core/renderers"; 3 | import { createMetadata, d } from "../../metadata-helpers"; 4 | import { parseScssVar } from "../../../components-core/theming/themeVars"; 5 | import styles from "./LineChart.module.scss"; 6 | import { MemoizedItem } from "../../container-helpers"; 7 | 8 | const COMP = "LineChart"; 9 | 10 | export const LineChartMd = createMetadata({ 11 | status: "experimental", 12 | description: 13 | "`LineChart` displays data as connected points over a continuous axis, ideal " + 14 | "for showing trends, changes over time, or relationships between variables. " + 15 | "Use it time series data, progress tracking, and comparing multiple data " + 16 | "series on the same scale.", 17 | docFolder: "Charts/LineChart", 18 | props: { 19 | data: { 20 | description: 21 | "The data to be displayed in the line chart." + 22 | "It needs to be an array of objects, where each object represents a data point.", 23 | }, 24 | xKey: { 25 | description: "The key in the data objects used for labeling different data series.", 26 | valueType: "string", 27 | }, 28 | yKeys: { 29 | description: 30 | "This property specifies the keys in the data objects that should be used for rendering the lines.", 31 | valueType: "string", 32 | }, 33 | hideX: { 34 | description: 35 | "Determines whether the X-axis should be hidden. If set to (`true`), the axis will not be displayed.", 36 | valueType: "boolean", 37 | defaultValue: defaultProps.hideX, 38 | }, 39 | hideY: { 40 | description: 41 | "Determines whether the Y-axis should be hidden. If set to (`true`), the axis will not be displayed.", 42 | valueType: "boolean", 43 | defaultValue: defaultProps.hideY, 44 | }, 45 | hideTickX: { 46 | description: 47 | "Determines whether the X-axis ticks should be hidden. If set to (`true`), the ticks will not be displayed.", 48 | valueType: "boolean", 49 | defaultValue: defaultProps.hideTickX, 50 | }, 51 | hideTickY: { 52 | description: 53 | "Determines whether the Y-axis ticks should be hidden. If set to (`true`), the ticks will not be displayed.", 54 | valueType: "boolean", 55 | defaultValue: defaultProps.hideTickY, 56 | }, 57 | hideTooltip: { 58 | description: 59 | "Determines whether the tooltip should be hidden." + 60 | "If set to (`true`), no tooltip will be shown when hovering over data points.", 61 | valueType: "boolean", 62 | defaultValue: defaultProps.hideTooltip, 63 | }, 64 | tickFormatterX: { 65 | description: 66 | "A function that formats the X-axis tick labels. It receives a tick value and returns a formatted string.", 67 | }, 68 | tickFormatterY: { 69 | description: 70 | "A function that formats the Y-axis tick labels. It receives a tick value and returns a formatted string.", 71 | }, 72 | showLegend: { 73 | description: "Determines whether the legend should be displayed.", 74 | valueType: "boolean", 75 | defaultValue: defaultProps.showLegend, 76 | }, 77 | tooltipTemplate: { 78 | description: "This property allows replacing the default template to display a tooltip.", 79 | }, 80 | marginTop: d("The top margin of the chart"), 81 | marginRight: d("The right margin of the chart"), 82 | marginBottom: d("The bottom margin of the chart"), 83 | marginLeft: d("The left margin of the chart"), 84 | }, 85 | themeVars: parseScssVar(styles.themeVars), 86 | defaultThemeVars: { 87 | [`width-line-LineChart`]: "1px", 88 | }, 89 | }); 90 | 91 | export const lineChartComponentRenderer = createComponentRenderer( 92 | COMP, 93 | LineChartMd, 94 | ({ extractValue, node, className, lookupSyncCallback, renderChild }: any) => { 95 | return ( 96 | <LineChart 97 | tickFormatterX={lookupSyncCallback(node.props?.tickFormatterX)} 98 | tickFormatterY={lookupSyncCallback(node.props?.tickFormatterY)} 99 | hideTickX={extractValue(node.props?.hideTickX)} 100 | hideTickY={extractValue(node.props?.hideTickY)} 101 | data={extractValue(node.props?.data)} 102 | className={className} 103 | dataKeys={extractValue(node.props?.yKeys)} 104 | nameKey={extractValue(node.props?.xKey)} 105 | hideX={extractValue(node.props?.hideX)} 106 | hideY={extractValue(node.props?.hideY)} 107 | hideTooltip={extractValue(node.props?.hideTooltip)} 108 | showLegend={extractValue.asOptionalBoolean(node.props?.showLegend)} 109 | marginTop={extractValue.asOptionalNumber(node.props?.marginTop)} 110 | marginRight={extractValue.asOptionalNumber(node.props?.marginRight)} 111 | marginBottom={extractValue.asOptionalNumber(node.props?.marginBottom)} 112 | marginLeft={extractValue.asOptionalNumber(node.props?.marginLeft)} 113 | tooltipRenderer={ 114 | node.props.tooltipTemplate 115 | ? (tooltipData) => { 116 | return ( 117 | <MemoizedItem 118 | node={node.props.tooltipTemplate} 119 | item={tooltipData} 120 | contextVars={{ 121 | $tooltip: tooltipData, 122 | }} 123 | renderChild={renderChild} 124 | /> 125 | ); 126 | } 127 | : undefined 128 | } 129 | > 130 | {renderChild(node.children)} 131 | </LineChart> 132 | ); 133 | }, 134 | ); 135 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/TimeInput/TimeInput.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Time format support**: 12-hour and 24-hour formats with customizable display 5 | - **Precision control**: Configure precision for hours, minutes, and seconds 6 | - **Input validation**: Real-time validation with visual feedback for invalid times 7 | - **Accessibility**: Full keyboard navigation and screen reader support 8 | - **Localization**: Automatic AM/PM labels based on user locale 9 | 10 | %-DESC-END 11 | 12 | %-API-START setValue 13 | 14 | ```xmlui-pg copy {3, 9, 12} display name="Example: setValue" 15 | <App> 16 | <HStack> 17 | <Button 18 | label="Set Time to 14:30" 19 | onClick="picker.setValue('14:30')" /> 20 | <Button 21 | label="Remove Time" 22 | onClick="picker.setValue('')" /> 23 | </HStack> 24 | <TimeInput id="picker" /> 25 | </App> 26 | ``` 27 | 28 | %-API-END 29 | 30 | %-PROP-START initialValue 31 | 32 | ```xmlui-pg copy display name="Example: initialValue" height="120px" 33 | <App> 34 | <TimeInput initialValue="14:30:15" /> 35 | </App> 36 | ``` 37 | 38 | %-PROP-END 39 | 40 | %-PROP-START placeholder 41 | 42 | ```xmlui-pg copy display name="Example: placeholder" height="120px" 43 | <App> 44 | <TimeInput placeholder="Select a time" /> 45 | </App> 46 | ``` 47 | 48 | %-PROP-END 49 | 50 | %-PROP-START enabled 51 | 52 | ```xmlui-pg copy display name="Example: enabled" height="120px" 53 | <App> 54 | <TimeInput enabled="false" initialValue="14:30" /> 55 | </App> 56 | ``` 57 | 58 | %-PROP-END 59 | 60 | %-PROP-START validationStatus 61 | 62 | | Value | Description | 63 | | :-------- | :---------------------------------------------------- | 64 | | `valid` | Visual indicator for an input that is accepted | 65 | | `warning` | Visual indicator for an input that produced a warning | 66 | | `error` | Visual indicator for an input that produced an error | 67 | 68 | ```xmlui-pg copy display name="Example: validationStatus" 69 | <App> 70 | <TimeInput validationStatus="valid" initialValue="11:30" /> 71 | <TimeInput validationStatus="warning" initialValue="11:30" /> 72 | <TimeInput validationStatus="error" initialValue="11:30" /> 73 | </App> 74 | ``` 75 | 76 | %-PROP-END 77 | 78 | %-PROP-START format 79 | 80 | The `format` prop controls how time is displayed and which parts are editable. Based on Unicode Technical Standard #35. 81 | 82 | | Format | Description | Example | 83 | | :----- | :---------- | :------ | 84 | | `H:mm` | 24-hour format with hours and minutes | 14:30 | 85 | | `HH:mm:ss` | 24-hour format with hours, minutes, seconds | 14:30:15 | 86 | | `h:mm a` | 12-hour format with AM/PM | 2:30 PM | 87 | | `hh:mm:ss a` | 12-hour format with seconds and AM/PM | 02:30:15 PM | 88 | 89 | ```xmlui-pg copy display name="Example: format" 90 | <App> 91 | <TimeInput format="H:mm" initialValue="14:30" /> 92 | <TimeInput format="h:mm a" initialValue="14:30" /> 93 | <TimeInput format="HH:mm:ss" initialValue="14:30:15" /> 94 | <TimeInput format="HH:mm:ss a" initialValue="14:30:15" /> 95 | </App> 96 | ``` 97 | 98 | %-PROP-END 99 | 100 | %-PROP-START clearable 101 | 102 | When enabled, it displays a clear button that allows users to reset the time picker back to its initial value. Change the time value in this app and then click the clear button: 103 | 104 | ```xmlui-pg copy display name="Example: clearable" /clearable/ 105 | <App> 106 | <TimeInput initialValue="11:30" /> 107 | <TimeInput clearable="true" initialValue="10:20" /> 108 | </App> 109 | ``` 110 | 111 | %-PROP-END 112 | 113 | %-PROP-START clearIcon 114 | 115 | ```xmlui-pg copy display name="Example: clearIcon" /clearIcon/ 116 | <App> 117 | <TimeInput initialValue="11:30" clearIcon="trash" /> 118 | </App> 119 | ``` 120 | 121 | %-PROP-END 122 | 123 | %-PROP-START required 124 | 125 | Marks the time input as required for form validation. 126 | 127 | ```xmlui-pg copy display name="Example: required" height="120px" 128 | <App> 129 | <TimeInput required="true" /> 130 | </App> 131 | ``` 132 | 133 | %-PROP-END 134 | 135 | %-PROP-START mute 136 | 137 | When `true`, prevents audible beeps but still fires the `beep` event for programmatic handling. 138 | 139 | %-PROP-END 140 | 141 | %-PROP-START emptyCharacter 142 | 143 | Character to use as placeholder for empty time values. If longer than 1 character, uses the first character. Defaults to '-'. 144 | 145 | ```xmlui-pg copy display name="Example: emptyCharacter" 146 | <App> 147 | <TimeInput emptyCharacter="." /> 148 | <TimeInput emptyCharacter="*" /> 149 | <TimeInput emptyCharacter="abc" /> 150 | </App> 151 | ``` 152 | 153 | %-PROP-END 154 | 155 | %-EVENT-START didChange 156 | 157 | Fired when the time value changes. Receives the new time value as a parameter. 158 | 159 | > [!INFO] The time value changes when the edited input part (hour, minute, second) loses focus or the AM/PM selectro changes. 160 | 161 | ```xmlui-pg copy {2} display name="Example: didChange" height="180px" 162 | <App var.selectedTime="No time selected"> 163 | <Text value="{selectedTime}" /> 164 | <TimeInput 165 | format="h:m:s a" 166 | initialValue="07:30:05" 167 | onDidChange="(time) => selectedTime = time" /> 168 | </App> 169 | ``` 170 | 171 | %-EVENT-END 172 | 173 | %-EVENT-START gotFocus 174 | 175 | Fired when the time picker receives focus. 176 | 177 | ```xmlui-pg copy {4-5} display name="Example: gotFocus/lostFocus" 178 | <App var.isFocused="{false}"> 179 | <Text value="{isFocused 180 | ? 'TimeInput focused' : 'TimeInput lost focus'}" 181 | /> 182 | <TimeInput 183 | format="HH:mm:ss a" 184 | onGotFocus="isFocused = true" 185 | onLostFocus="isFocused = false" 186 | initialValue="14:30" 187 | /> 188 | </App> 189 | ``` 190 | 191 | %-EVENT-END 192 | 193 | %-EVENT-START invalidTime 194 | 195 | Fired when the user enters an invalid time value. 196 | 197 | ```xmlui-pg copy {2} display name="Example: invalidTime" 198 | <App var.errorMessage=""> 199 | <Text value="{errorMessage}" /> 200 | <TimeInput 201 | onInvalidTime="(error) => errorMessage = 'Invalid time entered'" 202 | onDidChange="errorMessage = ''" /> 203 | </App> 204 | ``` 205 | 206 | %-EVENT-END 207 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Checkbox/Checkbox.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Flexible labeling**: Position labels on any side and support custom label templates 5 | - **Validation support**: Built-in validation states for form error handling 6 | - **Indeterminate state**: Special visual state for mixed selections (useful for "select all" scenarios) 7 | 8 | To bind data to a `Checkbox`, use the XMLUI [Forms infrastructure](/forms). 9 | 10 | ## Checkbox Values 11 | 12 | The `initialValue` and `value` properties of the checkbox are transformed to a Boolean value to display the checked (`true`) or unchecked (`false`) state with this logic: 13 | - `null` and `undefined` go to `false`. 14 | - If the property is Boolean, the property value is used as is. 15 | - If it is a number, `NaN` and `0` result in `false`; other values represent `true`. 16 | - If the property is a string, the empty string and the literal "false" string result in `false`; others result in `true`. 17 | - The empty array value goes to `false`; other array values result in `true`. 18 | - Object values with no properties result in `false`; other values represent `true`. 19 | 20 | %-DESC-END 21 | 22 | %-PROP-START enabled 23 | 24 | ```xmlui-pg copy display {4-5, 9-10} name="Example: enabled" 25 | <App> 26 | Enabled checkboxes: 27 | <HStack> 28 | <Checkbox initialValue="true" enabled="true" /> 29 | <Checkbox initialValue="false" enabled="true" /> 30 | </HStack> 31 | Disabled checkboxes: 32 | <HStack> 33 | <Checkbox initialValue="true" enabled="false" /> 34 | <Checkbox initilaValue="false" enabled="false" /> 35 | </HStack> 36 | </App> 37 | ``` 38 | 39 | %-PROP-END 40 | 41 | %-PROP-START indeterminate 42 | 43 | This prop is commonly used if there are several other checkboxes linked to one checkbox and some items in that group of checkboxes are in a mixed state: at least one item has a different value compared to the rest. 44 | 45 | The following sample binds the state of two checkboxes to one and updates the state of the top checkbox accordingly. When the states of the bound checkboxes are different, the top checkbox is set to indeterminate: 46 | 47 | ```xmlui-pg copy display name="Example: indeterminate" 48 | ---app copy display {4} 49 | <App var.indeterminate="{false}"> 50 | <Checkbox 51 | label="Indeterminate Checkbox" 52 | indeterminate="{indeterminate}" 53 | initialValue="{cb1.value}" 54 | readOnly="true" /> 55 | <ChangeListener 56 | listenTo="{ { v1: cb1.value, v2: cb2.value } }" 57 | onDidChange="indeterminate = cb1.value !== cb2.value" /> 58 | Group of checkboxes: 59 | <HStack> 60 | <Checkbox label="Checkbox #1" id="cb1" initialValue="true" /> 61 | <Checkbox label="Checkbox #2" id="cb2" initialValue="false" /> 62 | </HStack> 63 | </App> 64 | ---desc 65 | Try this sample by clicking the bottom group of checkboxes. 66 | ``` 67 | 68 | %-PROP-END 69 | 70 | %-PROP-START label 71 | 72 | ```xmlui-pg copy display name="Example: label" 73 | <App> 74 | <Checkbox label="Example label" initialValue="true" /> 75 | <Checkbox label="Another label" intialValue="false" /> 76 | </App> 77 | ``` 78 | 79 | %-PROP-END 80 | 81 | %-PROP-START labelPosition 82 | 83 | ```xmlui-pg copy display name="Example: labelPosition" 84 | <App> 85 | <Checkbox label="Top label" labelPosition="top" initialValue="true" /> 86 | <Checkbox label="End label" labelPosition="end" initialValue="true" /> 87 | <Checkbox label="Bottom label" labelPosition="bottom" initialValue="true" /> 88 | <Checkbox label="Start label" labelPosition="start" initialValue="true" /> 89 | </App> 90 | ``` 91 | 92 | %-PROP-END 93 | 94 | %-PROP-START readOnly 95 | 96 | ```xmlui-pg copy {3} display name="Example: readOnly" 97 | <App> 98 | <Checkbox readOnly="true" label="Checked" initialValue="true" /> 99 | <Checkbox readOnly="true" label="Unchecked" intialValue="false" /> 100 | </App> 101 | ``` 102 | 103 | %-PROP-END 104 | 105 | %-API-START value 106 | 107 | You can query this read-only API property to query the checkbox's current value (`true`: checked, `false`: unchecked). 108 | 109 | See an example in the `setValue` API method. 110 | 111 | %-API-END 112 | 113 | %-API-START setValue 114 | 115 | You can use this method to set the checkbox's current value programmatically (`true`: checked, `false`: unchecked). 116 | 117 | ```xmlui-pg copy {10,13,15} display name="Example: value and setValue" 118 | <App var.changes=""> 119 | <Checkbox 120 | id="checkbox" 121 | readOnly="true" 122 | label="This checkbox can be set only programmatically" 123 | onDidChange="changes += '+'" /> 124 | <HStack> 125 | <Button 126 | label="Check" 127 | onClick="checkbox.setValue(true)" /> 128 | <Button 129 | label="Uncheck" 130 | onClick="checkbox.setValue(false)" /> 131 | </HStack> 132 | <Text>The checkbox is {checkbox.value ? "checked" : "unchecked"}</Text> 133 | <Text value="Changes: {changes}" /> 134 | </App> 135 | ``` 136 | 137 | %-API-END 138 | 139 | %-EVENT-START didChange 140 | 141 | ```xmlui-pg copy display name="Example: didChange" 142 | <App verticalAlignment="center" var.changes=""> 143 | <Checkbox label="Changeable" onDidChange="changes += '+'" /> 144 | <Checkbox 145 | label="Readonly" 146 | readOnly="true" 147 | onDidChange="changes += '-'" /> 148 | <Text value="Changes: {changes}" /> 149 | </App> 150 | ``` 151 | 152 | %-EVENT-END 153 | 154 | %-EVENT-START gotFocus 155 | 156 | Click the `Checkbox` in the example demo to change the label text. Note how clicking elsewhere resets the text to the original. 157 | 158 | ```xmlui-pg copy display name="Example: gotFocus/lostFocus" 159 | <App var.focused="{false}" verticalAlignment="center"> 160 | <Checkbox 161 | value="true" 162 | onGotFocus="focused = true" 163 | onLostFocus="focused = false" 164 | /> 165 | <Text value="{focused === true ? 'I am focused!' : 'I have lost the focus!'}" /> 166 | </App> 167 | ``` 168 | 169 | %-EVENT-END 170 | 171 | %-EVENT-START lostFocus 172 | 173 | (See the example above) 174 | 175 | %-EVENT-END 176 | ``` -------------------------------------------------------------------------------- /xmlui/src/language-server/services/common/metadata-utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { RiEmpathizeFill } from "react-icons/ri"; 2 | import type { ComponentMetadata, ComponentPropertyMetadata } from "../../../abstractions/ComponentDefs" 3 | import { layoutOptionKeys } from "../../../components-core/descriptorHelper"; 4 | import { onPrefixRegex, stripOnPrefix } from "../../../parsers/xmlui-parser"; 5 | import { viewportSizeMd } from "../../../components/abstractions"; 6 | 7 | type RestrictedComponentMetadata = Pick<ComponentMetadata, "description" | "status" | "props" | "events" | "apis" | "contextVars" | "allowArbitraryProps" | "shortDescription"> 8 | 9 | export type ComponentMetadataCollection = Record<string, RestrictedComponentMetadata> 10 | 11 | export class MetadataProvider { 12 | constructor(private readonly metadataCollection: ComponentMetadataCollection) {} 13 | 14 | componentNames(): string[] { 15 | return Object.keys(this.metadataCollection); 16 | } 17 | 18 | getComponent(componentName: string): ComponentMetadataProvider | null { 19 | const providerData = this.metadataCollection[componentName]; 20 | if (!providerData) { 21 | return null; 22 | } 23 | 24 | return new ComponentMetadataProvider(providerData); 25 | } 26 | } 27 | 28 | export type AttributeKind = "prop" | "event" | "api" | "implicit" | "layout" 29 | export type TaggedAttribute = { name: string, kind: AttributeKind }; 30 | 31 | export class ComponentMetadataProvider { 32 | constructor(private readonly metadata: RestrictedComponentMetadata) {} 33 | 34 | /** 35 | * Retrieves the metadata for a given property, explicit or implicit. 36 | * @param name The name of the property. 37 | * @returns The metadata for the property, or `undefined` if not found. 38 | */ 39 | getProp(name: string) { 40 | return this.metadata.props[name] ?? implicitPropsMetadata[name]; 41 | } 42 | 43 | getAttr(name: string) { 44 | if (onPrefixRegex.test(name)){ 45 | const eventName = stripOnPrefix(name) 46 | const event = this.metadata.events?.[eventName]; 47 | if (event) { 48 | return event; 49 | } 50 | } 51 | const explicitProp = this.metadata.props?.[name]; 52 | if (explicitProp) { 53 | return explicitProp; 54 | } 55 | const api = this.metadata.apis?.[name]; 56 | if (api) { 57 | return api; 58 | } 59 | 60 | const layout = layoutMdForKey(name); 61 | if (layout) { 62 | return layout; 63 | } 64 | return implicitPropsMetadata[name]; 65 | } 66 | 67 | getAttrForKind({ name, kind}: TaggedAttribute){ 68 | switch (kind){ 69 | case "api": 70 | return this.metadata.apis[name]; 71 | case "event": 72 | return this.metadata.events[name]; 73 | case "prop": 74 | return this.metadata.props[name]; 75 | case "implicit": 76 | return implicitPropsMetadata[name]; 77 | case "layout": 78 | return layoutMdForKey(name) 79 | } 80 | } 81 | 82 | getAllAttributes() { 83 | const attrNames: TaggedAttribute[] = []; 84 | for (const key of Object.keys(this.metadata.props ?? {})) { 85 | attrNames.push({ name: key, kind: "prop" }); 86 | } 87 | for (const key of Object.keys(this.metadata.events ?? {})) { 88 | attrNames.push({ name: key, kind: "event" }); 89 | } 90 | for (const key of Object.keys(this.metadata.apis ?? {})) { 91 | attrNames.push({ name: key, kind: "api" }); 92 | } 93 | for (const layoutKey of layoutOptionKeys){ 94 | attrNames.push({name: layoutKey, kind: "layout"}) 95 | } 96 | for (const implicitPropKey of Object.keys(implicitPropsMetadata)){ 97 | attrNames.push({name: implicitPropKey, kind: "implicit"}) 98 | } 99 | 100 | return attrNames; 101 | } 102 | 103 | getEvent(name: string){ 104 | return this.metadata.events?.[name]; 105 | } 106 | 107 | getApi(name: string){ 108 | return this.metadata.apis?.[name]; 109 | } 110 | 111 | get events(): Record<string, string> { 112 | return this.metadata.events; 113 | } 114 | 115 | get apis(): Record<string, string> { 116 | return this.metadata.apis; 117 | } 118 | 119 | get contextVars(): Record<string, string> { 120 | return this.metadata.contextVars; 121 | } 122 | 123 | get allowArbitraryProps(): boolean { 124 | return this.metadata.allowArbitraryProps; 125 | } 126 | 127 | get shortDescription(): string { 128 | return this.metadata.shortDescription; 129 | } 130 | 131 | getMetadata(): RestrictedComponentMetadata { 132 | return this.metadata; 133 | } 134 | } 135 | 136 | function layoutMdForKey(name: string): ComponentPropertyMetadata { 137 | const metadata = { 138 | description: "Layout property. Not yet documented", 139 | }; 140 | if (layoutOptionKeys.includes(name)){ 141 | return metadata; 142 | } 143 | for(const size of viewportSizeMd){ 144 | const suffix = "-" + ((size as { 145 | value: string | number; 146 | description: string; 147 | }).value); 148 | 149 | if(name.endsWith(suffix)){ 150 | const nameWithoutSize = name.slice(0, -suffix.length); 151 | if(layoutOptionKeys.includes(nameWithoutSize)){ 152 | return metadata; 153 | } 154 | } 155 | } 156 | return null; 157 | } 158 | 159 | const implicitPropsMetadata: Record<string, ComponentPropertyMetadata> = { 160 | inspect: { 161 | description: "Determines whether the component can be inspected or not", 162 | defaultValue: false, 163 | valueType: "boolean", 164 | }, 165 | data: { 166 | description: "Specifies the data source for a component. Can be a URL string (fetched automatically), a DataSource or an expression to evaluate. Changes to this property trigger UI updates once data is loaded.", 167 | }, 168 | when: { 169 | description: "Specifies a condition that must be met for the component to be displayed", 170 | defaultValue: true, 171 | valueType: "boolean", 172 | } 173 | }; 174 | 175 | export function addOnPrefix(name: string) { 176 | return "on" + name[0].toUpperCase() + name.substring(1); 177 | } 178 | ``` -------------------------------------------------------------------------------- /docs/content/components/NoResult.md: -------------------------------------------------------------------------------- ```markdown 1 | # NoResult [#noresult] 2 | 3 | `NoResult` displays a visual indication that a query or search returned nothing. 4 | 5 | ## Properties [#properties] 6 | 7 | ### `hideIcon` (default: false) [#hideicon-default-false] 8 | 9 | This boolean property indicates if the icon should be hidden. 10 | 11 | ```xmlui-pg copy display name="Example: hideIcon" 12 | <App> 13 | <FlowLayout> 14 | <NoResult hideIcon="true" width="50%" /> 15 | <NoResult hideIcon="false" width="50%" /> 16 | </FlowLayout> 17 | </App> 18 | ``` 19 | 20 | ### `icon` (default: "noresult") [#icon-default-noresult] 21 | 22 | This property defines the icon to display with the component. 23 | 24 | This property defines the icon to display with the component. For a list of of available icons consult [`Icon` documentation](/components/Icon). 25 | 26 | ```xmlui-pg copy display name="Example: icon" 27 | <App> 28 | <NoResult icon="error" height="100%" /> 29 | </App> 30 | ``` 31 | 32 | ### `label` [#label] 33 | 34 | This property sets the label of the component. If not set, the component will not display a label. 35 | 36 | Customize the displayed text using this property. Leave empty to omit it. 37 | 38 | ```xmlui-pg copy display name="Example: label" 39 | <App> 40 | <NoResult label="Sorry, found nothing!" height="100%" /> 41 | </App> 42 | ``` 43 | 44 | ## Events [#events] 45 | 46 | This component does not have any events. 47 | 48 | ## Exposed Methods [#exposed-methods] 49 | 50 | This component does not expose any methods. 51 | 52 | ## Styling [#styling] 53 | 54 | ### Theme Variables [#theme-variables] 55 | 56 | | Variable | Default Value (Light) | Default Value (Dark) | 57 | | --- | --- | --- | 58 | | [border](../styles-and-themes/common-units/#border)-NoResult | 0px solid $borderColor | 0px solid $borderColor | 59 | | [borderBottom](../styles-and-themes/common-units/#border)-NoResult | *none* | *none* | 60 | | [borderBottomColor](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 61 | | [borderBottomStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 62 | | [borderBottomWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 63 | | [borderColor](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 64 | | [borderEndEndRadius](../styles-and-themes/common-units/#border-rounding)-NoResult | *none* | *none* | 65 | | [borderEndStartRadius](../styles-and-themes/common-units/#border-rounding)-NoResult | *none* | *none* | 66 | | [borderHorizontal](../styles-and-themes/common-units/#border)-NoResult | *none* | *none* | 67 | | [borderHorizontalColor](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 68 | | [borderHorizontalStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 69 | | [borderHorizontalWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 70 | | [borderLeft](../styles-and-themes/common-units/#border)-NoResult | *none* | *none* | 71 | | [color](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 72 | | [borderLeftStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 73 | | [borderLeftWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 74 | | [borderRight](../styles-and-themes/common-units/#border)-NoResult | *none* | *none* | 75 | | [color](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 76 | | [borderRightStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 77 | | [borderRightWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 78 | | [borderStartEndRadius](../styles-and-themes/common-units/#border-rounding)-NoResult | *none* | *none* | 79 | | [borderStartStartRadius](../styles-and-themes/common-units/#border-rounding)-NoResult | *none* | *none* | 80 | | [borderStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 81 | | [borderTop](../styles-and-themes/common-units/#border)-NoResult | *none* | *none* | 82 | | [borderTopColor](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 83 | | [borderTopStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 84 | | [borderTopWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 85 | | [borderHorizontal](../styles-and-themes/common-units/#border)-NoResult | *none* | *none* | 86 | | [borderVerticalColor](../styles-and-themes/common-units/#color)-NoResult | *none* | *none* | 87 | | [borderVerticalStyle](../styles-and-themes/common-units/#border-style)-NoResult | *none* | *none* | 88 | | [borderVerticalWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 89 | | [borderWidth](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 90 | | [gap](../styles-and-themes/common-units/#size)-icon-NoResult | $space-2 | $space-2 | 91 | | [padding](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 92 | | [paddingBottom](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 93 | | [paddingHorizontal](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 94 | | [paddingLeft](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 95 | | [paddingRight](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 96 | | [paddingTop](../styles-and-themes/common-units/#size)-NoResult | *none* | *none* | 97 | | [paddingVertical](../styles-and-themes/common-units/#size)-NoResult | $space-2 | $space-2 | 98 | | [size](../styles-and-themes/common-units/#size)-icon-NoResult | $space-8 | $space-8 | 99 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/LoaderComponent.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { type MutableRefObject, useCallback, useEffect, useMemo } from "react"; 2 | 3 | import type { ContainerDispatcher, MemoedVars } from "./abstractions/ComponentRenderer"; 4 | import type { RegisterComponentApiFn } from "../abstractions/RendererDefs"; 5 | import type { ContainerState, RegisterComponentApiFnInner } from "./rendering/ContainerWrapper"; 6 | import type { ComponentDef } from "../abstractions/ComponentDefs"; 7 | import type { 8 | LookupAsyncFn, 9 | LookupAsyncFnInner, 10 | LookupSyncFn, 11 | LookupSyncFnInner, 12 | } from "../abstractions/ActionDefs"; 13 | 14 | import { useComponentRegistry } from "../components/ComponentRegistryContext"; 15 | import { ContainerActionKind } from "./rendering/containers"; 16 | import { createValueExtractor } from "./rendering/valueExtractor"; 17 | import { useReferenceTrackedApi } from "./utils/hooks"; 18 | import type { AppContextObject } from "../abstractions/AppContextDefs"; 19 | 20 | interface LoaderRendererContext { 21 | node: ComponentDef; 22 | state: ContainerState; 23 | dispatch: ContainerDispatcher; 24 | registerComponentApi: RegisterComponentApiFnInner; 25 | lookupAction: LookupAsyncFnInner; 26 | lookupSyncCallback: LookupSyncFnInner; 27 | memoedVarsRef: MutableRefObject<MemoedVars>; 28 | appContext: AppContextObject; 29 | onUnmount: (uid: symbol) => void; 30 | } 31 | 32 | export function LoaderComponent({ 33 | node, 34 | state, 35 | dispatch, 36 | lookupAction, 37 | lookupSyncCallback, 38 | registerComponentApi, 39 | onUnmount, 40 | appContext, 41 | memoedVarsRef, 42 | }: LoaderRendererContext) { 43 | const componentRegistry = useComponentRegistry(); 44 | const uid = useMemo(() => Symbol(node.uid), [node.uid]); 45 | 46 | useEffect(() => { 47 | return () => { 48 | onUnmount(uid); 49 | }; 50 | }, [onUnmount, uid]); 51 | 52 | // --- Memoizes component API registration 53 | const memoedRegisterComponentApi: RegisterComponentApiFn = useCallback( 54 | (api) => { 55 | registerComponentApi(uid, api); 56 | }, 57 | [registerComponentApi, uid], 58 | ); 59 | 60 | // --- Memoizes the action resolution by action definition value 61 | const memoedLookupAction: LookupAsyncFn = useCallback( 62 | (action, actionOptions) => { 63 | return lookupAction(action, uid, actionOptions); 64 | }, 65 | [lookupAction, uid], 66 | ); 67 | 68 | // --- Get the tracked APIs of the compomnent 69 | const referenceTrackedApi = useReferenceTrackedApi(state); 70 | 71 | // --- Memoizes the value extractor object 72 | const valueExtractor = useMemo(() => { 73 | return createValueExtractor(state, appContext, referenceTrackedApi, memoedVarsRef); 74 | }, [appContext, memoedVarsRef, referenceTrackedApi, state]); 75 | 76 | // --- Memoizes the action resolution by action definition value 77 | const memoedLookupSyncCallback: LookupSyncFn = useCallback( 78 | (action) => { 79 | if (!action) { 80 | return undefined; 81 | } 82 | return lookupSyncCallback(valueExtractor(action), uid); 83 | }, 84 | [lookupSyncCallback, uid, valueExtractor], 85 | ); 86 | 87 | const memoedLoaderInProgressChanged = useCallback( 88 | (isInProgress: boolean) => { 89 | dispatch(loaderInProgressChanged(uid, isInProgress)); 90 | }, 91 | [dispatch, uid], 92 | ); 93 | 94 | const memoedLoaderIsRefetchingChanged = useCallback( 95 | (isRefetching: boolean) => { 96 | dispatch(loaderIsRefetchingChanged(uid, isRefetching)); 97 | }, 98 | [dispatch, uid], 99 | ); 100 | 101 | const memoedLoaderLoaded = useCallback( 102 | (data: any, pageInfo: any) => { 103 | dispatch(loaderLoaded(uid, data, pageInfo)); 104 | }, 105 | [dispatch, uid], 106 | ); 107 | 108 | const memoedLoaderError = useCallback( 109 | (error: any) => { 110 | dispatch(loaderError(uid, error)); 111 | }, 112 | [dispatch, uid], 113 | ); 114 | 115 | const renderer = componentRegistry.lookupLoaderRenderer(node.type); 116 | if (!renderer) { 117 | console.error( 118 | `Loader ${node.type} is not available. Did you forget to register it in the loaderRegistry?`, 119 | ); 120 | return null; 121 | } 122 | 123 | return renderer({ 124 | loader: node, 125 | state, 126 | dispatch, 127 | loaderInProgressChanged: memoedLoaderInProgressChanged, 128 | loaderIsRefetchingChanged: memoedLoaderIsRefetchingChanged, 129 | loaderLoaded: memoedLoaderLoaded, 130 | loaderError: memoedLoaderError, 131 | extractValue: valueExtractor, 132 | registerComponentApi: memoedRegisterComponentApi, 133 | lookupAction: memoedLookupAction, 134 | lookupSyncCallback: memoedLookupSyncCallback, 135 | }); 136 | } 137 | 138 | // Signs that a particular loader (`uid`) has just started fetching its data (or executing its operation). 139 | function loaderInProgressChanged(uid: symbol, isInProgress: boolean) { 140 | return { 141 | type: ContainerActionKind.LOADER_IN_PROGRESS_CHANGED, 142 | payload: { 143 | uid, 144 | inProgress: isInProgress, 145 | }, 146 | }; 147 | } 148 | 149 | // Signs that a particular loader (`uid`) has just started refetching its data (or executing its operation). 150 | function loaderIsRefetchingChanged(uid: symbol, isRefetching: boolean) { 151 | return { 152 | type: ContainerActionKind.LOADER_IS_REFETCHING_CHANGED, 153 | payload: { 154 | uid, 155 | isRefetching, 156 | }, 157 | }; 158 | } 159 | 160 | // Signs that a particular loader (`uid`) has just fetched its data (`pageInfo`) successfully. 161 | function loaderLoaded(uid: symbol, data: any, pageInfo?: any) { 162 | return { 163 | type: ContainerActionKind.LOADER_LOADED, 164 | payload: { 165 | uid, 166 | data, 167 | pageInfo, 168 | }, 169 | }; 170 | } 171 | 172 | // Signs that a particular loader (`uid`) has has an `error` during its operation. 173 | function loaderError(uid: symbol, error: any) { 174 | return { 175 | type: ContainerActionKind.LOADER_ERROR, 176 | payload: { 177 | uid, 178 | error, 179 | }, 180 | }; 181 | } 182 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/CompoundComponent.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React, { forwardRef, isValidElement, useMemo } from "react"; 2 | import { composeRefs } from "@radix-ui/react-compose-refs"; 3 | 4 | import type { ComponentDef } from "../abstractions/ComponentDefs"; 5 | import type { ContainerWrapperDef } from "./rendering/ContainerWrapper"; 6 | import type { CollectedDeclarations } from "./script-runner/ScriptingSourceTree"; 7 | import type { RendererContext } from "../abstractions/RendererDefs"; 8 | 9 | import { useEvent } from "./utils/misc"; 10 | import { useShallowCompareMemoize } from "./utils/hooks"; 11 | import { isArray, isObject } from "lodash-es"; 12 | import { EMPTY_ARRAY } from "./constants"; 13 | import { mergeProps } from "./utils/mergeProps"; 14 | 15 | type CompoundComponentProps = { 16 | // Definition of the `component` part of the compound component 17 | compound: ComponentDef; 18 | // The API of the compound component 19 | api?: Record<string, string>; 20 | scriptCollected?: CollectedDeclarations; 21 | } & RendererContext; 22 | 23 | // Acts as a bridge between a compound component definition and its renderer. 24 | export const CompoundComponent = forwardRef( 25 | ( 26 | { 27 | node, 28 | lookupSyncCallback, 29 | lookupEventHandler, 30 | compound, 31 | api, 32 | scriptCollected, 33 | renderChild, 34 | extractValue, 35 | layoutContext, 36 | uid, 37 | updateState, 38 | registerComponentApi, 39 | extractResourceUrl, 40 | appContext, 41 | state, 42 | lookupAction, 43 | ...restProps 44 | }: CompoundComponentProps, 45 | forwardedRef: React.ForwardedRef<any>, 46 | ) => { 47 | // --- Extract property values (resolve binding expressions) 48 | const resolvedPropsInner = useMemo(() => { 49 | const resolvedProps: any = {}; 50 | if (node.props) { 51 | Object.entries(node.props).forEach(([key, value]) => { 52 | const extractedProp = extractValue(value, true); 53 | if (extractedProp?._ARROW_EXPR_) { 54 | // --- Ensure arrow functions are called synchronously 55 | resolvedProps[key] = lookupSyncCallback(extractedProp); 56 | } else { 57 | resolvedProps[key] = extractedProp; 58 | } 59 | }); 60 | } 61 | return resolvedProps; 62 | }, [extractValue, lookupSyncCallback, node.props]); 63 | 64 | const resolvedProps = useShallowCompareMemoize(resolvedPropsInner); 65 | 66 | // --- Wrap the `component` part with a container that manages the 67 | const containerNode: ContainerWrapperDef = useMemo(() => { 68 | const { loaders, vars, functions, scriptError, ...rest } = compound; 69 | return { 70 | type: "Container", 71 | uses: EMPTY_ARRAY, 72 | api, 73 | scriptCollected, 74 | loaders: loaders, 75 | vars, 76 | functions: functions, 77 | scriptError: scriptError, 78 | containerUid: uid, 79 | props: { 80 | debug: (compound.props as any)?.debug, 81 | }, 82 | children: [rest], 83 | }; 84 | }, [api, compound, scriptCollected, uid]); 85 | 86 | const emitEvent = useEvent((eventName, ...args) => { 87 | const handler = lookupEventHandler(eventName); 88 | 89 | if (handler) { 90 | return handler(...args); 91 | } 92 | }); 93 | 94 | const hasEventHandler = useEvent((eventName) => !!lookupEventHandler(eventName)); 95 | 96 | const vars = useMemo(() => { 97 | return { 98 | $props: resolvedProps, 99 | ...containerNode.vars, 100 | emitEvent, 101 | hasEventHandler, 102 | updateState, 103 | }; 104 | }, [containerNode.vars, emitEvent, hasEventHandler, resolvedProps, updateState]); 105 | const stableVars = useShallowCompareMemoize(vars); 106 | 107 | // --- Inject implicit variable into the container of the compound component 108 | const nodeWithPropsAndEventsInner = useMemo(() => { 109 | return { 110 | ...containerNode, 111 | vars: stableVars, 112 | }; 113 | }, [containerNode, stableVars]); 114 | 115 | const nodeWithPropsAndEvents = useShallowCompareMemoize(nodeWithPropsAndEventsInner); 116 | 117 | const hasTemplateProps = useMemo(() => { 118 | return Object.entries(node.props).some(([key, value]) => { 119 | return ( 120 | //TODO this is a hack, we should have a better way to detect template props 121 | key.endsWith("Template") || 122 | (isObject(value) && (value as any).type !== undefined) || 123 | (isArray(value) && (value as any)[0]?.type !== undefined) 124 | ); 125 | }); 126 | }, [node.props]); 127 | 128 | const memoedParentRenderContext = useMemo(() => { 129 | if (!hasTemplateProps && (!node.children || node.children.length === 0)) { 130 | return undefined; 131 | } 132 | return { 133 | renderChild, 134 | props: node.props, 135 | children: node.children, 136 | }; 137 | }, [hasTemplateProps, node.children, node.props, renderChild]); 138 | 139 | //we remove the wrapChild prop from layout context, because that wrapping already happened for the compound component instance 140 | const safeLayoutContext = layoutContext 141 | ? { ...layoutContext, wrapChild: undefined } 142 | : layoutContext; 143 | const ret = renderChild(nodeWithPropsAndEvents, safeLayoutContext, memoedParentRenderContext); 144 | if (forwardedRef && ret && isValidElement(ret)) { 145 | return React.cloneElement(ret, { 146 | ref: composeRefs(forwardedRef, (ret as any).ref), 147 | ...mergeProps( 148 | ret.props, 149 | restProps 150 | ), 151 | } as any); 152 | } 153 | return React.isValidElement(ret) ? ret : <>{ret}</>; 154 | }, 155 | ); 156 | 157 | // --- Display a name for the component in developer tools 158 | CompoundComponent.displayName = "CompoundComponent"; 159 | ```