This is page 30 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 │ │ │ ├── rss.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ └── 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 │ ├── components-with-options.md │ ├── containers.md │ ├── data-operations.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 │ ├── 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 │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ └── SelectNative.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 │ │ │ ├── base64-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/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { 2 | StandaloneAppDescription, 3 | StandaloneJsonConfig, 4 | } from "./components-core/abstractions/standalone"; 5 | import type { ApiInterceptorDefinition } from "./components-core/interception/abstractions"; 6 | import StandaloneApp, { startApp } from "./components-core/StandaloneApp"; 7 | import type { 8 | ComponentDef, 9 | ComponentLike, 10 | ComponentMetadata, 11 | CompoundComponentDef, 12 | PropertyValueDescription, 13 | } from "./abstractions/ComponentDefs"; 14 | import { AppRoot } from "./components-core/rendering/AppRoot"; 15 | import { 16 | createComponentRenderer, 17 | createUserDefinedComponentRenderer, 18 | } from "./components-core/renderers"; 19 | import type { TreeNode } from "./components-core/abstractions/treeAbstractions"; 20 | import { Icon } from "./components/Icon/IconNative"; 21 | import { ErrorBoundary } from "./components-core/rendering/ErrorBoundary"; 22 | import { Stack } from "./components/Stack/StackNative"; 23 | import { Button } from "./components/Button/ButtonNative"; 24 | import { Splitter } from "./components/Splitter/SplitterNative"; 25 | import { useTheme, useThemes } from "./components-core/theming/ThemeContext"; 26 | import { toCssVar } from "./parsers/style-parser/StyleParser"; 27 | import { getColor } from "./components-core/utils/css-utils"; 28 | import { useColors } from "./components-core/utils/hooks"; 29 | import type { 30 | ComponentRendererDef, 31 | RegisterComponentApiFn, 32 | RendererContext, 33 | } from "./abstractions/RendererDefs"; 34 | import { parseScssVar } from "./components-core/theming/themeVars"; 35 | import StandaloneExtensionManager from "./components-core/StandaloneExtensionManager"; 36 | import type { ThemeDefinition, ThemeTone } from "./abstractions/ThemingDefs"; 37 | import { useDevTools } from "./components-core/InspectorContext"; 38 | import { useLogger } from "./logging/LoggerContext"; 39 | import { TabItemComponent } from "./components/Tabs/TabItemNative"; 40 | import { Tabs } from "./components/Tabs/TabsNative"; 41 | import { errReportComponent, xmlUiMarkupToComponent } from "./components-core/xmlui-parser"; 42 | import { ApiInterceptorProvider } from "./components-core/interception/ApiInterceptorProvider"; 43 | import { Spinner } from "./components/Spinner/SpinnerNative"; 44 | import type { XmlUiNode } from "./parsers/xmlui-parser"; 45 | import { XmlUiHelper } from "./parsers/xmlui-parser"; 46 | import { Text } from "./components/Text/TextNative"; 47 | import { TextBox } from "./components/TextBox/TextBoxNative"; 48 | import { NestedApp } from "./components/NestedApp/NestedAppNative"; 49 | import { builtInThemes } from "./components-core/theming/ThemeProvider"; 50 | import { VisuallyHidden } from "./components/VisuallyHidden"; 51 | import { LinkNative } from "./components/Link/LinkNative"; 52 | import { Breakout } from "./components/Breakout/BreakoutNative"; 53 | import { ToneChangerButton } from "./components/ToneChangerButton/ToneChangerButton"; 54 | import { Logo } from "./components/Logo/LogoNative"; 55 | import { Theme } from "./components/Theme/ThemeNative"; 56 | import { useSearchContextContent } from "./components/App/SearchContext"; 57 | import { useAppLayoutContext } from "./components/App/AppLayoutContext"; 58 | import { StyleProvider } from "./components-core/theming/StyleContext"; 59 | import { StyleRegistry } from "./components-core/theming/StyleRegistry"; 60 | import { useEvent } from "./components-core/utils/misc"; 61 | import { 62 | createMetadata, 63 | d, 64 | dComponent, 65 | dAutoFocus, 66 | dClick, 67 | dCollapse, 68 | dDidChange, 69 | dDidClose, 70 | dDidOpen, 71 | dEnabled, 72 | dFocus, 73 | dEndIcon, 74 | dEndText, 75 | dExpanded, 76 | dExpand, 77 | dGotFocus, 78 | dIndeterminate, 79 | dInitialValue, 80 | dInternal, 81 | dLabel, 82 | dLabelBreak, 83 | dLabelPosition, 84 | dLabelWidth, 85 | dLostFocus, 86 | dMaxLength, 87 | dMulti, 88 | dOrientation, 89 | dPlaceholder, 90 | dReadonly, 91 | dRequired, 92 | dStartIcon, 93 | dStartText, 94 | dSetValueApi, 95 | dTriggerTemplate, 96 | dValidationStatus, 97 | dValue, 98 | dValueApi, 99 | } from "./components/metadata-helpers"; 100 | import StandaloneComponent from "./components-core/rendering/StandaloneComponent"; 101 | 102 | export type { 103 | ThemeDefinition, 104 | ComponentDef, 105 | ComponentRendererDef, 106 | CompoundComponentDef, 107 | PropertyValueDescription, 108 | ComponentLike, 109 | StandaloneAppDescription, 110 | StandaloneJsonConfig, 111 | ApiInterceptorDefinition, 112 | RegisterComponentApiFn, 113 | //TODO review from here (added for playground) 114 | TreeNode, //TODO REMOVE 115 | RendererContext, 116 | ComponentMetadata, 117 | ThemeTone, 118 | XmlUiNode, 119 | }; 120 | export { 121 | StandaloneApp, 122 | StandaloneExtensionManager, 123 | createComponentRenderer, 124 | createUserDefinedComponentRenderer, 125 | createMetadata, 126 | d, 127 | dComponent, 128 | dAutoFocus, 129 | dClick, 130 | dCollapse, 131 | dDidChange, 132 | dDidClose, 133 | dDidOpen, 134 | dEnabled, 135 | dFocus, 136 | dEndIcon, 137 | dEndText, 138 | dExpanded, 139 | dExpand, 140 | dGotFocus, 141 | dIndeterminate, 142 | dInitialValue, 143 | dInternal, 144 | dLabel, 145 | dLabelBreak, 146 | dLabelPosition, 147 | dLabelWidth, 148 | dLostFocus, 149 | dMaxLength, 150 | dMulti, 151 | dOrientation, 152 | dPlaceholder, 153 | dReadonly, 154 | dRequired, 155 | dStartIcon, 156 | dStartText, 157 | dSetValueApi, 158 | dTriggerTemplate, 159 | dValidationStatus, 160 | dValue, 161 | dValueApi, 162 | parseScssVar, 163 | startApp, 164 | useTheme, 165 | AppRoot, 166 | ErrorBoundary, 167 | Icon, 168 | Stack, 169 | Button, 170 | Splitter, 171 | getColor, 172 | TabItemComponent as TabItem, 173 | Tabs, 174 | useColors, 175 | toCssVar, 176 | useDevTools, 177 | useLogger, 178 | errReportComponent, 179 | xmlUiMarkupToComponent, 180 | ApiInterceptorProvider, 181 | Spinner, 182 | useThemes, 183 | builtInThemes, 184 | XmlUiHelper, 185 | Text, 186 | TextBox, 187 | NestedApp, 188 | VisuallyHidden, 189 | LinkNative, 190 | ToneChangerButton, 191 | Logo, 192 | Breakout, 193 | useSearchContextContent, 194 | useAppLayoutContext, 195 | StyleProvider, 196 | StyleRegistry, 197 | useEvent, 198 | StandaloneComponent, 199 | Theme, 200 | }; 201 | ``` -------------------------------------------------------------------------------- /docs/content/components/Splitter.md: -------------------------------------------------------------------------------- ```markdown 1 | # Splitter [#splitter] 2 | 3 | `Splitter` component divides a container into two resizable sections. These are are identified by their names: primary and secondary. They have a draggable bar between them. 4 | 5 | Most properties of the component focus on the primary section (e.g. sizing). 6 | 7 | See also: [HSplitter](/components/HSplitter), [VSplitter](/components/VSplitter). 8 | 9 | ## Properties [#properties] 10 | 11 | ### `floating` (default: false) [#floating-default-false] 12 | 13 | Toggles whether the resizer is visible (`false`) or not (`true`) when not hovered or dragged. The default value is `false`, meaning the resizer is visible all the time. 14 | 15 | ```xmlui-pg copy display name="Example: floating" 16 | <App> 17 | <Splitter height="200px" floating="true"> 18 | <Stack backgroundColor="lightblue" height="100%" /> 19 | <Stack backgroundColor="darksalmon" height="100%" /> 20 | </Splitter> 21 | </App> 22 | ``` 23 | 24 | ### `initialPrimarySize` (default: "50%") [#initialprimarysize-default-50-] 25 | 26 | This optional number property sets the initial size of the primary section. The unit of the size value is in pixels or percentages. 27 | 28 | ```xmlui-pg copy display name="Example: initialPrimarySize" 29 | <App> 30 | <Splitter height="200px" initialPrimarySize="40%"> 31 | <Stack backgroundColor="lightblue" height="100%" /> 32 | <Stack backgroundColor="darksalmon" height="100%" /> 33 | </Splitter> 34 | </App> 35 | ``` 36 | 37 | ### `maxPrimarySize` (default: "100%") [#maxprimarysize-default-100-] 38 | 39 | This property sets the maximum size the primary section can have. The unit of the size value is in pixels or percentages. 40 | 41 | ```xmlui-pg copy display name="Example: maxPrimarySize" 42 | <App> 43 | <Splitter height="200px" maxPrimarySize="80%"> 44 | <Stack backgroundColor="lightblue" height="100%" /> 45 | <Stack backgroundColor="darksalmon" height="100%" /> 46 | </Splitter> 47 | </App> 48 | ``` 49 | 50 | ### `minPrimarySize` (default: "0%") [#minprimarysize-default-0-] 51 | 52 | This property sets the minimum size the primary section can have. The unit of the size value is in pixels or percentages. 53 | 54 | ```xmlui-pg copy display name="Example: minPrimarySize" 55 | <App> 56 | <Splitter height="200px" minPrimarySize="40px"> 57 | <Stack backgroundColor="lightblue" height="100%" /> 58 | <Stack backgroundColor="darksalmon" height="100%" /> 59 | </Splitter> 60 | </App> 61 | ``` 62 | 63 | ### `orientation` (default: "vertical") [#orientation-default-vertical] 64 | 65 | Sets whether the `Splitter` divides the container horizontally and lays out the section on top of each other (`vertical`), or vertically by placing the sections next to each other (`horizontal`). 66 | 67 | Available values: `horizontal`, `vertical` **(default)** 68 | 69 | ```xmlui-pg copy display name="Example: orientation" 70 | <App> 71 | <Splitter height="200px" orientation="horizontal"> 72 | <Stack backgroundColor="lightblue" height="100%" /> 73 | <Stack backgroundColor="darksalmon" height="100%" /> 74 | </Splitter> 75 | </App> 76 | ``` 77 | 78 | ### `splitterTemplate` [#splittertemplate] 79 | 80 | The divider can be customized using XMLUI components via this property. 81 | 82 | ```xmlui-pg copy {2-4} display name="Example: splitterTemplate" 83 | <App> 84 | <Splitter height="200px"> 85 | <property name="splitterTemplate"> 86 | <ContentSeparator backgroundColor="green" height="4px" /> 87 | </property> 88 | <Stack backgroundColor="lightblue" height="100%" /> 89 | <Stack backgroundColor="darksalmon" height="100%" /> 90 | </Splitter> 91 | </App> 92 | ``` 93 | 94 | ### `swapped` (default: false) [#swapped-default-false] 95 | 96 | This optional booelan property indicates whether the `Splitter` sections are layed out as primary and secondary (`false`) or secondary and primary (`true`) from left to right. 97 | 98 | ```xmlui-pg copy display name="Example: swapped" 99 | <App> 100 | <Splitter height="200px" swapped="true"> 101 | <Stack backgroundColor="lightblue" height="100%" /> 102 | <Stack backgroundColor="darksalmon" height="100%" /> 103 | </Splitter> 104 | </App> 105 | ``` 106 | 107 | ## Events [#events] 108 | 109 | ### `resize` [#resize] 110 | 111 | This event fires when the component is resized. 112 | 113 | ```xmlui-pg copy {2} display name="Example: resize" 114 | <App height="200px" var.counter="{0}"> 115 | <Splitter onResize="counter++"> 116 | <Stack backgroundColor="lightblue" height="100%"> 117 | <Text value="Resize event called {counter} number of times" /> 118 | </Stack> 119 | <Stack backgroundColor="darksalmon" height="100%" /> 120 | </Splitter> 121 | </App> 122 | ``` 123 | 124 | ## Exposed Methods [#exposed-methods] 125 | 126 | This component does not expose any methods. 127 | 128 | ## Styling [#styling] 129 | 130 | ### Theme Variables [#theme-variables] 131 | 132 | | Variable | Default Value (Light) | Default Value (Dark) | 133 | | --- | --- | --- | 134 | | [backgroundColor](../styles-and-themes/common-units/#color)-resizer-Splitter | $color-surface-100 | $color-surface-100 | 135 | | [backgroundColor](../styles-and-themes/common-units/#color)-Splitter | *none* | *none* | 136 | | [border](../styles-and-themes/common-units/#border)-Splitter | *none* | *none* | 137 | | [borderColor](../styles-and-themes/common-units/#color)-Splitter | *none* | *none* | 138 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-Splitter | *none* | *none* | 139 | | [borderStyle](../styles-and-themes/common-units/#border-style)-Splitter | *none* | *none* | 140 | | [borderWidth](../styles-and-themes/common-units/#size)-Splitter | *none* | *none* | 141 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-Splitter | *none* | *none* | 142 | | [cursor](../styles-and-themes/common-units/#cursor)-resizer-horizontal-Splitter | ew-resize | ew-resize | 143 | | [cursor](../styles-and-themes/common-units/#cursor)-resizer-vertical-Splitter | ns-resize | ns-resize | 144 | | [thickness](../styles-and-themes/common-units/#size)-resizer-Splitter | 5px | 5px | 145 | ``` -------------------------------------------------------------------------------- /docs/content/components/APICall.md: -------------------------------------------------------------------------------- ```markdown 1 | # APICall [#apicall] 2 | 3 | `APICall` creates, updates or deletes data on the backend, versus [`DataSource`](/components/DataSource) which fetches data. Unlike DataSource, APICall doesn't automatically execute - you must trigger it manually with the `execute()` method, typically from form submissions or button clicks. 4 | 5 | **Key characteristics:** 6 | - **Manual execution**: Call `execute()` method to trigger the API request 7 | - **Form integration**: Commonly used in `<event name="submit">` handlers for forms 8 | - **Parameter passing**: Pass data to the API call via `execute()` parameters 9 | - **Built-in notifications**: Supports automatic progress, success, and error messages 10 | 11 | **Context variables available during execution:** 12 | 13 | - `$error`: Error details (available in `errorNotificationMessage`) 14 | - `$param`: The first parameter passed to `execute()` method 15 | - `$params`: Array of all parameters passed to `execute()` method (access with `$params[0]`, `$params[1]`, etc.) 16 | - `$result`: Response data (available in `completedNotificationMessage`) 17 | 18 | ## Properties [#properties] 19 | 20 | ### `body` [#body] 21 | 22 | This optional property sets the request body. Use to pass an object that will be serialized as a JSON string. If you have an object that is already serialized as a JSON string, use `rawBody` instead. 23 | 24 | ### `completedNotificationMessage` [#completednotificationmessage] 25 | 26 | This property defines the message to display automatically when the operation has been completed. When this property is not defined, the completion does not display any message. 27 | 28 | This property customizes the success message displayed in a toast after the finished API invocation. The `$result` context variable can refer to the response body. For example, you can use the following code snippet to display the first 100 characters in the completed operation's response body: 29 | 30 | ```xmlui copy 31 | <APICall 32 | id="ds" 33 | method="post" 34 | url="/api/shopping-list" 35 | completedNotificationMessage="Result: {JSON.stringify($result).substring(0, 100)}" /> 36 | ``` 37 | 38 | ### `confirmButtonLabel` [#confirmbuttonlabel] 39 | 40 | This optional string property enables the customization of the submit button in the confirmation dialog that is displayed before the `APICall` is executed. 41 | 42 | ### `confirmMessage` [#confirmmessage] 43 | 44 | This optional string sets the message in the confirmation dialog that is displayed before the `APICall` is executed. 45 | 46 | ### `confirmTitle` [#confirmtitle] 47 | 48 | This optional string sets the title in the confirmation dialog that is displayed before the `APICall` is executed. 49 | 50 | ### `errorNotificationMessage` [#errornotificationmessage] 51 | 52 | This property defines the message to display automatically when the operation results in an error. You can use the `$error` context value in an expression to refer to the original error message. 53 | 54 | This property customizes the message displayed in a toast when the API invocation results in an error. The `$error.statusCode` context variable can refer to the response's status code, while `$error. details` to the response body. For example, you can use the following code snippet to display the status code and the details: 55 | 56 | ```xmlui copy 57 | <APICall 58 | id="ds" 59 | method="post" 60 | url="/api/shopping-list" 61 | errorNotificationMessage="${error.statusCode}, {JSON.stringify($error.details)}" /> 62 | ``` 63 | 64 | ### `headers` [#headers] 65 | 66 | You can optionally define request header values as key-value pairs, where the key is the ID of the particular header and the value is that header's corresponding value. 67 | 68 | ### `inProgressNotificationMessage` [#inprogressnotificationmessage] 69 | 70 | This property customizes the message that is displayed in a toast while the API operation is in progress. If not defined, no progress message is displayed. 71 | 72 | ### `method` (default: "get") [#method-default-get] 73 | 74 | The method of data manipulation can be done via setting this property. 75 | 76 | Available values: `get` **(default)**, `post`, `put`, `delete`, `patch`, `head`, `options`, `trace`, `connect` 77 | 78 | ### `queryParams` [#queryparams] 79 | 80 | This optional property sets the query parameters for the request. The object you pass here will be serialized to a query string and appended to the request URL. You can specify key and value pairs where the key is the name of a particular query parameter and the value is that parameter's value. 81 | 82 | ### `rawBody` [#rawbody] 83 | 84 | This optional property sets the request body to the value provided here without any conversion. Use the * `body` property if you want the object sent in JSON. When you define `body` and `rawBody`, the latest one prevails. 85 | 86 | ### `url` (required) [#url-required] 87 | 88 | Use this property to set the URL to which data will be sent. If not provided, an empty URL is used. 89 | 90 | ## Events [#events] 91 | 92 | ### `beforeRequest` [#beforerequest] 93 | 94 | This event fires before the request is sent. Returning an explicit boolean`false` value will prevent the request from being sent. 95 | 96 | ### `error` [#error] 97 | 98 | This event fires when a request results in an error. 99 | 100 | ### `success` [#success] 101 | 102 | This event fires when a request results in a success. 103 | 104 | ## Exposed Methods [#exposed-methods] 105 | 106 | ### `execute` [#execute] 107 | 108 | This method triggers the invocation of the API. You can pass an arbitrary number of parameters to the method. In the `APICall` instance, you can access those with the `$param` and `$params` context values. 109 | 110 | **Signature**: `execute(...params: any[])` 111 | 112 | - `params`: An arbitrary number of parameters that can be used in the API call. 113 | 114 | ## Styling [#styling] 115 | 116 | This component does not have any styles. 117 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Charts/RadarChart/RadarChartNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | RadarChart as RRadarChart, 3 | Radar, 4 | PolarGrid, 5 | PolarAngleAxis, 6 | PolarRadiusAxis, 7 | ResponsiveContainer, 8 | Tooltip, 9 | Legend as RLegend, 10 | } from "recharts"; 11 | 12 | import type { ReactNode} from "react"; 13 | import { useEffect, useRef, useState, useCallback } from "react"; 14 | import { useMemo } from "react"; 15 | import ChartProvider, { useChartContextValue } from "../utils/ChartProvider"; 16 | import { TooltipContent } from "../Tooltip/TooltipContent"; 17 | import { useTheme } from "../../../components-core/theming/ThemeContext"; 18 | 19 | export type RadarChartProps = { 20 | data: any[]; 21 | nameKey: string; 22 | dataKeys?: string[]; 23 | className?: string; 24 | hideGrid?: boolean; 25 | hideAngleAxis?: boolean; 26 | hideRadiusAxis?: boolean; 27 | hideTooltip?: boolean; 28 | children?: ReactNode; 29 | showLegend?: boolean; 30 | filled?: boolean; 31 | strokeWidth?: number; 32 | fillOpacity?: number; 33 | tooltipRenderer?: (tooltipData: any) => ReactNode; 34 | }; 35 | 36 | export const defaultProps: Pick< 37 | RadarChartProps, 38 | | "hideGrid" 39 | | "hideAngleAxis" 40 | | "hideRadiusAxis" 41 | | "hideTooltip" 42 | | "showLegend" 43 | | "filled" 44 | | "strokeWidth" 45 | | "fillOpacity" 46 | > = { 47 | hideGrid: false, 48 | hideAngleAxis: false, 49 | hideRadiusAxis: false, 50 | hideTooltip: false, 51 | showLegend: false, 52 | filled: true, 53 | strokeWidth: 2, 54 | fillOpacity: 0.3, 55 | }; 56 | 57 | export function RadarChart({ 58 | data = [], 59 | nameKey, 60 | dataKeys = [], 61 | hideGrid = defaultProps.hideGrid, 62 | hideAngleAxis = defaultProps.hideAngleAxis, 63 | hideRadiusAxis = defaultProps.hideRadiusAxis, 64 | hideTooltip = defaultProps.hideTooltip, 65 | className, 66 | children, 67 | showLegend = defaultProps.showLegend, 68 | filled = defaultProps.filled, 69 | strokeWidth = defaultProps.strokeWidth, 70 | fillOpacity = defaultProps.fillOpacity, 71 | tooltipRenderer, 72 | }: RadarChartProps) { 73 | // Validate and normalize data 74 | const validData = Array.isArray(data) ? data : []; 75 | const { getThemeVar } = useTheme(); 76 | 77 | const colorValues = useMemo(() => { 78 | return [ 79 | getThemeVar("color-primary-500"), 80 | getThemeVar("color-primary-300"), 81 | getThemeVar("color-success-500"), 82 | getThemeVar("color-success-300"), 83 | getThemeVar("color-warn-500"), 84 | getThemeVar("color-warn-300"), 85 | getThemeVar("color-danger-500"), 86 | getThemeVar("color-danger-300"), 87 | getThemeVar("color-info-500"), 88 | getThemeVar("color-info-300"), 89 | getThemeVar("color-secondary-500"), 90 | getThemeVar("color-secondary-300"), 91 | ]; 92 | }, [getThemeVar]); 93 | 94 | const config = useMemo(() => { 95 | return Object.assign( 96 | {}, 97 | ...dataKeys.map((key, index) => { 98 | return { 99 | [key]: { 100 | label: key, 101 | color: colorValues[index % colorValues.length], 102 | }, 103 | }; 104 | }), 105 | ); 106 | }, [colorValues, dataKeys]); 107 | 108 | const chartContextValue = useChartContextValue({ dataKeys, nameKey }); 109 | 110 | // Process data and create radar elements based on dataKeys 111 | const radarElements = useMemo(() => { 112 | return dataKeys.map((key, index) => { 113 | const color = colorValues[index % colorValues.length]; 114 | 115 | return ( 116 | <Radar 117 | key={key} 118 | name={key} 119 | dataKey={key} 120 | stroke={color} 121 | fill={filled ? color : "none"} 122 | fillOpacity={filled ? fillOpacity : 0} 123 | strokeWidth={strokeWidth} 124 | /> 125 | ); 126 | }); 127 | }, [dataKeys, colorValues, filled, fillOpacity, strokeWidth]); 128 | 129 | // Handle responsive behavior 130 | const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); 131 | const containerRef = useRef<HTMLDivElement>(null); 132 | 133 | useEffect(() => { 134 | const updateSize = () => { 135 | if (containerRef.current) { 136 | const { width, height } = containerRef.current.getBoundingClientRect(); 137 | setContainerSize({ width, height }); 138 | } 139 | }; 140 | 141 | updateSize(); 142 | window.addEventListener('resize', updateSize); 143 | return () => window.removeEventListener('resize', updateSize); 144 | }, []); 145 | 146 | // Determine if we're in mini mode (very small container) 147 | const isMiniMode = containerSize.height < 150; 148 | 149 | const safeTooltipRenderer = useCallback( 150 | (props: any) => { 151 | if (!tooltipRenderer) return <TooltipContent {...props} />; 152 | 153 | const payloadObject: Record<string, any> = {}; 154 | 155 | if (props.payload && props.payload.length > 0 && props.payload[0].payload) { 156 | Object.assign(payloadObject, props.payload[0].payload); 157 | } 158 | 159 | // Extract tooltip data from Recharts props 160 | const tooltipData = { 161 | label: props.label, 162 | payload: payloadObject, 163 | active: props.active, 164 | }; 165 | 166 | return tooltipRenderer(tooltipData); 167 | }, 168 | [tooltipRenderer], 169 | ); 170 | 171 | return ( 172 | <ChartProvider value={chartContextValue}> 173 | <div ref={containerRef} className={className}> 174 | <ResponsiveContainer width="100%" height="100%"> 175 | <RRadarChart data={validData}> 176 | {!hideGrid && <PolarGrid />} 177 | {!hideAngleAxis && ( 178 | <PolarAngleAxis 179 | dataKey={nameKey} 180 | hide={isMiniMode} 181 | /> 182 | )} 183 | {!hideRadiusAxis && ( 184 | <PolarRadiusAxis 185 | hide={isMiniMode} 186 | /> 187 | )} 188 | {!isMiniMode && !hideTooltip && ( 189 | <Tooltip content={safeTooltipRenderer} /> 190 | )} 191 | {showLegend && !isMiniMode && <RLegend />} 192 | {radarElements} 193 | {children} 194 | </RRadarChart> 195 | </ResponsiveContainer> 196 | </div> 197 | </ChartProvider> 198 | ); 199 | } 200 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/interception/request-param-converter.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it } from "vitest"; 2 | import { convertRequestParamPart } from "../../../src/components-core/utils/request-params" 3 | 4 | describe("Request parameter converter", () => { 5 | it("string to integer #1", () => { 6 | // --- Arrange 7 | const data = { 8 | a: "123", 9 | b: true, 10 | c: 345 11 | } 12 | 13 | // --- Act 14 | const types = { 15 | a: "integer" 16 | } 17 | const res = convertRequestParamPart(data, types) 18 | 19 | // --- Assert 20 | expect(res).deep.equal({ 21 | a: 123, 22 | b: true, 23 | c: 345 24 | }) 25 | }); 26 | 27 | it("string to integer #2", () => { 28 | // --- Arrange 29 | const data = { 30 | a: "123", 31 | b: "-444", 32 | c: 345 33 | } 34 | 35 | // --- Act 36 | const types = { 37 | a: "integer", 38 | b: "integer" 39 | } 40 | const res = convertRequestParamPart(data, types) 41 | 42 | // --- Assert 43 | expect(res).deep.equal({ 44 | a: 123, 45 | b: -444, 46 | c: 345 47 | }) 48 | }); 49 | 50 | it("string to float #1", () => { 51 | // --- Arrange 52 | const data = { 53 | a: "123.25", 54 | b: true, 55 | c: 345 56 | } 57 | 58 | // --- Act 59 | const types = { 60 | a: "float" 61 | } 62 | const res = convertRequestParamPart(data, types) 63 | 64 | // --- Assert 65 | expect(res).deep.equal({ 66 | a: 123.25, 67 | b: true, 68 | c: 345 69 | }) 70 | }); 71 | 72 | it("string to float #2", () => { 73 | // --- Arrange 74 | const data = { 75 | a: "123.25", 76 | b: "234.5", 77 | c: 345 78 | } 79 | 80 | // --- Act 81 | const types = { 82 | a: "float", 83 | b: "real" 84 | } 85 | const res = convertRequestParamPart(data, types) 86 | 87 | // --- Assert 88 | expect(res).deep.equal({ 89 | a: 123.25, 90 | b: 234.5, 91 | c: 345 92 | }) 93 | }); 94 | 95 | it("string to float #3", () => { 96 | // --- Arrange 97 | const data = { 98 | a: "123.25", 99 | b: "234.5", 100 | c: 345 101 | } 102 | 103 | // --- Act 104 | const types = { 105 | a: "double", 106 | b: "real" 107 | } 108 | const res = convertRequestParamPart(data, types) 109 | 110 | // --- Assert 111 | expect(res).deep.equal({ 112 | a: 123.25, 113 | b: 234.5, 114 | c: 345 115 | }) 116 | }); 117 | 118 | const stringToBoolCases = [ 119 | { s: "true", exp: true}, 120 | { s: "yes", exp: true}, 121 | { s: "on", exp: true}, 122 | { s: "false", exp: false}, 123 | { s: "no", exp: false}, 124 | { s: "off", exp: false}, 125 | ] 126 | 127 | stringToBoolCases.forEach((tc, idx) => it(`string to bool #${idx + 1}`, () => { 128 | // --- Arrange 129 | const data = { 130 | a: tc.s, 131 | b: "234.5", 132 | c: 345 133 | } 134 | 135 | // --- Act 136 | const types = { 137 | a: "boolean", 138 | b: "real" 139 | } 140 | const res = convertRequestParamPart(data, types) 141 | 142 | // --- Assert 143 | expect(res).deep.equal({ 144 | a: tc.exp, 145 | b: 234.5, 146 | c: 345 147 | }) 148 | })) 149 | 150 | it("number to integer #1", () => { 151 | // --- Arrange 152 | const data = { 153 | a: 123.25, 154 | b: true, 155 | c: 345 156 | } 157 | 158 | // --- Act 159 | const types = { 160 | a: "integer" 161 | } 162 | const res = convertRequestParamPart(data, types) 163 | 164 | // --- Assert 165 | expect(res).deep.equal({ 166 | a: 123, 167 | b: true, 168 | c: 345 169 | }) 170 | }); 171 | 172 | it("number to integer #2", () => { 173 | // --- Arrange 174 | const data = { 175 | a: 123.25, 176 | b: 234, 177 | c: 345.5 178 | } 179 | 180 | // --- Act 181 | const types = { 182 | a: "integer", 183 | b: "integer", 184 | c: "integer" 185 | } 186 | const res = convertRequestParamPart(data, types) 187 | 188 | // --- Assert 189 | expect(res).deep.equal({ 190 | a: 123, 191 | b: 234, 192 | c: 346 193 | }) 194 | }); 195 | 196 | it("number to bool", () => { 197 | // --- Arrange 198 | const data = { 199 | a: 123.25, 200 | b: true, 201 | c: 0 202 | } 203 | 204 | // --- Act 205 | const types = { 206 | a: "boolean", 207 | c: "boolean" 208 | } 209 | const res = convertRequestParamPart(data, types) 210 | 211 | // --- Assert 212 | expect(res).deep.equal({ 213 | a: true, 214 | b: true, 215 | c: false 216 | }) 217 | }); 218 | 219 | it("boolean to integer", () => { 220 | // --- Arrange 221 | const data = { 222 | a: true, 223 | b: "123", 224 | c: false 225 | } 226 | 227 | // --- Act 228 | const types = { 229 | a: "integer", 230 | c: "integer" 231 | } 232 | const res = convertRequestParamPart(data, types) 233 | 234 | // --- Assert 235 | expect(res).deep.equal({ 236 | a: 1, 237 | b: "123", 238 | c: 0 239 | }) 240 | }); 241 | 242 | it("boolean to real", () => { 243 | // --- Arrange 244 | const data = { 245 | a: true, 246 | b: true, 247 | c: false 248 | } 249 | 250 | // --- Act 251 | const types = { 252 | a: "real", 253 | b: "double", 254 | c: "float" 255 | } 256 | const res = convertRequestParamPart(data, types) 257 | 258 | // --- Assert 259 | expect(res).deep.equal({ 260 | a: 1, 261 | b: 1, 262 | c: 0 263 | }) 264 | }); 265 | 266 | 267 | }); ``` -------------------------------------------------------------------------------- /docs/public/pages/scoping.md: -------------------------------------------------------------------------------- ```markdown 1 | # Scoping 2 | 3 | ## Variables 4 | 5 | A variable declared in a Main.xmlui component is visible to built-in child components (e.g. `Text`) at any level. 6 | 7 | ```xmlui-pg 8 | ---app display filename="Main.xmlui" /grandparent/ /parent/ /child/ 9 | <App var.message="Hello from App"> 10 | <Card id="grandparent"> 11 | <Text>Message: {message}</Text> 12 | <Card id="parent" var.message2="Hello from Card"> 13 | <Text>Message: {message}</Text> 14 | <Text>Message2: {message2}</Text> 15 | <Card id="child"> 16 | <Text>Message: {message}</Text> 17 | <Text>Message2: {message2}</Text> 18 | </Card> 19 | </Card> 20 | </Card> 21 | </App> 22 | ``` 23 | 24 | A variable declared in a Main.xmlui component is not automatically visible to a user-defined child component. 25 | 26 | ```xmlui-pg 27 | ---app display filename="Main.xmlui" 28 | <App var.message="Hello from App"> 29 | <Card> 30 | <Text>Message: {message}</Text> 31 | </Card> 32 | <MyCard /> 33 | </App> 34 | ---comp display filename="MyCard" 35 | <Component name="MyCard"> 36 | <Card> 37 | <Text>Message: {message}</Text> 38 | </Card> 39 | </Component> 40 | ``` 41 | 42 | The variable can be passed into a user-defined component. 43 | 44 | ```xmlui-pg 45 | ---app display filename="Main.xmlui" 46 | <App var.message="Hello from App"> 47 | <Card> 48 | <Text>Message: {message}</Text> 49 | </Card> 50 | <MyCard message="{message}" /> 51 | </App> 52 | ---comp display filename="MyCard" 53 | <Component name="MyCard"> 54 | <Card> 55 | <Text>Message: {$props.message}</Text> 56 | </Card> 57 | </Component> 58 | ``` 59 | 60 | 61 | Or the variable can be transposed into the user-defined component by means of the [Slot](/components/Slot) mechanism. The `Slot` content evaluates in the parent's scope, so it can see parent vars and IDs, but renders inside the child’s layout. 62 | 63 | ```xmlui-pg 64 | ---app display filename="Main.xmlui" 65 | <App var.message="Hello from App"> 66 | <Card> 67 | <Text>Message: {message}</Text> 68 | </Card> 69 | <MyCard> 70 | <Text>Message: {message}</Text> 71 | </MyCard> 72 | </App> 73 | ---comp display filename="MyCard.xmlui" 74 | <Component name="MyCard"> 75 | <Card> 76 | <Slot/> 77 | </Card> 78 | </Component> 79 | ``` 80 | 81 | A child component can redeclare a variable. 82 | 83 | ```xmlui-pg 84 | ---app display filename="Main.xmlui" /grandparent/ /parent/ /child/ 85 | <App var.message="Hello from App"> 86 | <Card id="grandparent"> 87 | <Text>Message: {message}</Text> 88 | <Card id="parent" var.message="Hello from parent Card"> 89 | <Text>Message: {message}</Text> 90 | <Card id="child" var.message="Hello from child Card"> 91 | <Text>Message: {message}</Text> 92 | </Card> 93 | </Card> 94 | </Card> 95 | </App> 96 | ``` 97 | 98 | 99 | All these rules apply within a user-defined component defined in a file like `MyComponent.xmlui`. 100 | 101 | ```xmlui-pg 102 | ---app display filename="Main.xmlui" 103 | <App> 104 | <MyComponent /> 105 | </App> 106 | ---comp display filename="MyComponent.xmlui" 107 | <Component name="MyComponent" var.message="Hello from MyComponent"> 108 | <Card id="grandparent"> 109 | <Text>Message: {message}</Text> 110 | <Card id="parent" var.message2="Hello from Card"> 111 | <Text>Message: {message}</Text> 112 | <Text>Message2: {message2}</Text> 113 | <Card id="child"> 114 | <Text>Message: {message}</Text> 115 | <Text>Message2: {message2}</Text> 116 | </Card> 117 | </Card> 118 | </Card> 119 | </Component> 120 | ``` 121 | 122 | A variable declared in a user-defined component can be passed into another user-defined component. 123 | 124 | 125 | ```xmlui-pg 126 | ---app display filename="Main.xmlui" 127 | <App> 128 | <MyComponent /> 129 | </App> 130 | ---comp display filename="MyComponent.xmlui" 131 | <Component name="MyComponent" var.message="Hello from MyComponent"> 132 | <Card> 133 | <Text>Message: {message}</Text> 134 | </Card> 135 | <MyOtherComponent message="{message}" /> 136 | </Component> 137 | ---comp display filename="MyOtherComponent.xmlui" 138 | <Component name="MyOtherComponent"> 139 | <Card> 140 | <Text>Passed message: {$props.message}</Text> 141 | </Card> 142 | </Component> 143 | ``` 144 | 145 | 146 | 147 | 148 | ## Component IDs 149 | 150 | A component ID declared on a Main.xmlui component is visible to built-in child components (e.g. `Text`) at any level. 151 | 152 | ```xmlui-pg 153 | ---app display filename="Main.xmlui" /parent/ /child/ /textBox/ 154 | <App var.message="Hello from App"> 155 | <TextBox id="textBox" initialValue="{message}" /> 156 | <Card id="parent"> 157 | <Text> 158 | { textBox.value } 159 | </Text> 160 | <Card id="child"> 161 | <Text> 162 | { textBox.value } 163 | </Text> 164 | </Card> 165 | </Card> 166 | </App> 167 | ``` 168 | 169 | A component ID declared on a Main.xmlui component is not automatically visible to a user-defined child component. 170 | 171 | 172 | ```xmlui-pg 173 | ---app display filename="Main.xmlui" 174 | <App var.message="Hello from App"> 175 | <TextBox id="textBox" initialValue="{message}" /> 176 | <MyCard /> 177 | </App> 178 | ---comp display filename="MyCard.xmlui" 179 | <Component name="MyCard"> 180 | <Card> 181 | <Text>Message: {textBox.value}</Text> 182 | </Card> 183 | </Component> 184 | ``` 185 | 186 | The id can be passed into a user-defined component. 187 | 188 | ```xmlui-pg 189 | ---app display filename="Main.xmlui" 190 | <App var.message="Hello from App"> 191 | <TextBox id="textBox" initialValue="{message}" /> 192 | <MyCard textBox="{textBox}" /> 193 | </App> 194 | ---comp display filename="MyCard.xmlui" 195 | <Component name="MyCard"> 196 | <Card> 197 | <Text>Message: {$props.textBox.value}</Text> 198 | </Card> 199 | </Component> 200 | ``` 201 | 202 | Or the component ID can be transposed into the user-defined component by means of the [Slot](/components/Slot) mechanism. 203 | 204 | ```xmlui-pg 205 | ---app display filename="Main.xmlui" 206 | <App var.message="Hello from App"> 207 | <TextBox id="textBox" initialValue="{message}" /> 208 | <MyCard> 209 | <Text>Message: {textBox.value}</Text> 210 | </MyCard> 211 | </App> 212 | ---comp display filename="MyCard.xmlui" 213 | <Component name="MyCard"> 214 | <Card> 215 | <Slot/> 216 | </Card> 217 | </Component> 218 | ``` 219 | 220 | All these rules apply within a user-defined component defined in a file like `MyComponent.xmlui`. 221 | ``` -------------------------------------------------------------------------------- /docs/public/pages/tutorial-06.md: -------------------------------------------------------------------------------- ```markdown 1 | # Slider 2 | 3 | The `Dashboard` page continues with a chart of daily revenue that uses a [Slider](/components/Slider) to control both ends of a date range. 4 | 5 | Here is a simplified version of that mechanism. Try using both slider handles to adjust the date range and corresponding total revenue. 6 | 7 | ```xmlui-pg noHeader 8 | ---app display 9 | <App> 10 | <SliderDemo /> 11 | </App> 12 | ---comp 13 | <Component name="SliderDemo"> 14 | <variable name="startDate" value="2022-06-01" /> 15 | <variable name="endDate" value="2022-06-30" /> 16 | 17 | <variable name="dailyData" value="{[ 18 | {date: '2022-06-01', total: 1200}, 19 | {date: '2022-06-02', total: 1850}, 20 | {date: '2022-06-03', total: 0}, 21 | {date: '2022-06-04', total: 950}, 22 | {date: '2022-06-05', total: 1650}, 23 | {date: '2022-06-06', total: 2200}, 24 | {date: '2022-06-07', total: 1400}, 25 | {date: '2022-06-08', total: 0}, 26 | {date: '2022-06-09', total: 1750}, 27 | {date: '2022-06-10', total: 1300}, 28 | {date: '2022-06-11', total: 1900}, 29 | {date: '2022-06-12', total: 2350}, 30 | {date: '2022-06-13', total: 0}, 31 | {date: '2022-06-14', total: 1800}, 32 | {date: '2022-06-15', total: 2150}, 33 | {date: '2022-06-16', total: 1450}, 34 | {date: '2022-06-17', total: 0}, 35 | {date: '2022-06-18', total: 2000}, 36 | {date: '2022-06-19', total: 1250}, 37 | {date: '2022-06-20', total: 1950}, 38 | {date: '2022-06-21', total: 0}, 39 | {date: '2022-06-22', total: 1600}, 40 | {date: '2022-06-23', total: 1850}, 41 | {date: '2022-06-24', total: 2250}, 42 | {date: '2022-06-25', total: 0}, 43 | {date: '2022-06-26', total: 1750}, 44 | {date: '2022-06-27', total: 2050}, 45 | {date: '2022-06-28', total: 1500}, 46 | {date: '2022-06-29', total: 0}, 47 | {date: '2022-06-30', total: 2200} 48 | ]}" /> 49 | 50 | <variable name="filteredData" value="{ 51 | dailyData.filter(item => item.date >= startDate && item.date <= endDate) 52 | }" /> 53 | 54 | <VStack> 55 | <H1>Slider Demo</H1> 56 | 57 | <Text>Selected records: {filteredData.length}</Text> 58 | 59 | <Slider 60 | id="dateSlider" 61 | label="Date range" 62 | minValue="{1}" 63 | maxValue="{30}" 64 | initialValue="{[1, 30]}" 65 | step="{1}" 66 | onDidChange="{ 67 | startDate = window.sliderValueToDate(dateSlider.value[0]); 68 | endDate = window.sliderValueToDate(dateSlider.value[1]); 69 | }" 70 | valueFormat="{ (value) => { 71 | const result = window.sliderValueToDate(value); 72 | return result; 73 | } 74 | }" 75 | /> 76 | 77 | <Text>Total Revenue: ${filteredData.reduce((sum, item) => sum + item.total, 0)}</Text> 78 | 79 | </VStack> 80 | </Component> 81 | ``` 82 | 83 | Here's `SliderDemo`. 84 | 85 | ```xmlui /filteredData/ /startDate/ /endDate/ /sliderValueToDate/ 86 | <Component name="SliderDemo"> 87 | <variable name="startDate" value="2022-06-01" /> 88 | <variable name="endDate" value="2022-06-30" /> 89 | 90 | <variable name="dailyData" value="{[ 91 | {date: '2022-06-01', total: 1200}, 92 | {date: '2022-06-02', total: 1850}, 93 | ... 94 | {date: '2022-06-29', total: 0}, 95 | {date: '2022-06-30', total: 2200} 96 | ]}" /> 97 | 98 | <variable name="filteredData" value="{ 99 | dailyData.filter(item => item.date >= startDate && item.date <= endDate) 100 | }" /> 101 | 102 | <VStack> 103 | <H1>Slider Demo</H1> 104 | 105 | <Text>Selected records: {filteredData.length}</Text> 106 | 107 | <Slider 108 | id="dateSlider" 109 | label="Date range" 110 | minValue="{1}" 111 | maxValue="{30}" 112 | initialValue="{[1, 30]}" 113 | step="{1}" 114 | onDidChange="{ 115 | startDate = window.sliderValueToDate(dateSlider.value[0]); 116 | endDate = window.sliderValueToDate(dateSlider.value[1]); 117 | }" 118 | valueFormat="{ (value) => { 119 | const result = window.sliderValueToDate(value); 120 | return result; 121 | } 122 | }" 123 | /> 124 | 125 | <Text> 126 | Total Revenue: ${filteredData.reduce((sum, item) => sum + item.total, 0)} 127 | </Text> 128 | 129 | </VStack> 130 | </Component> 131 | ``` 132 | 133 | When the handles move, the slider's `onDidChange` event updates `startDate` and `endDate` using a function, `sliderValueToDate`, that translates the slider position to a date in the range of dates. In the Invoices app those variables form part of a `DataSource` URL that fires when there's a change; here they update the `filteredData` variable to simulate the real `DataSource`. 134 | 135 | The slider's `valueFormat` property uses the same function to report the new `startDate` and `endDate`. 136 | 137 | ## A custom Slider 138 | 139 | The Invoices app encapsulates this behavior in a custom component called `DateRangeSlider`. 140 | 141 | ```xmlui /updateState/ 142 | <Component name="DateRangeSlider"> 143 | <variable name="originalStartDate" value="{ $props.minDate }"/> 144 | <variable name="maxEndDate" value="{ $props.maxDate }"/> 145 | <variable name="startDate" value="{ originalStartDate }"/> 146 | <variable name="endDate" value="{ maxEndDate }"/> 147 | <variable 148 | name="totalDays" 149 | value="{ window.daysBetween(originalStartDate, maxEndDate)}"/> 150 | 151 | <ChangeListener 152 | listenTo="{slider.value}" 153 | onDidChange="{() => { 154 | // Update the start and end dates based on slider values 155 | updateState({ 156 | value: { 157 | startDate: window.sliderValueToDate(slider.value[0], originalStartDate), 158 | endDate: window.sliderValueToDate(slider.value[1], originalStartDate) 159 | } 160 | }); 161 | }}" 162 | /> 163 | 164 | <Slider 165 | id="slider" 166 | label="dateRange" 167 | minValue="{0}" 168 | maxValue="{ totalDays }" 169 | initialValue="{ [0, totalDays] }" 170 | step="10" 171 | valueFormat="{ (value) => { 172 | const date = window.sliderValueToDate(value, originalStartDate); 173 | return date; 174 | }}" 175 | /> 176 | </Component> 177 | ``` 178 | 179 | The `updateState` method, available in all components, is a merge operation that can set multiple variables. 180 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Tabs/Tabs.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "../../components-core/theming/themes" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $component: "Tabs"; 5 | $themeVars: (); 6 | @function createThemeVar($componentVariable) { 7 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 8 | @return t.getThemeVar($themeVars, $componentVariable); 9 | } 10 | 11 | $themeVars: t.composePaddingVars($themeVars, "trigger-Tabs"); 12 | 13 | $backgroundColor-Tabs: createThemeVar("backgroundColor-Tabs"); 14 | $borderColor-Tabs: createThemeVar("borderColor-Tabs"); 15 | $borderWidth-Tabs: createThemeVar("borderWidth-Tabs"); 16 | $borderColor-active-Tabs: createThemeVar("borderColor-active-Tabs"); 17 | $backgroundColor-trigger-Tabs: createThemeVar("backgroundColor-trigger-Tabs"); 18 | $borderRadius-trigger-Tabs: createThemeVar("borderRadius-trigger-Tabs"); 19 | $border-trigger-Tabs: createThemeVar("border-trigger-Tabs"); 20 | $textColor-trigger-Tabs: createThemeVar("textColor-trigger-Tabs"); 21 | $backgroundColor-trigger-Tabs--hover: createThemeVar("backgroundColor-trigger-Tabs--hover"); 22 | $backgroundColor-trigger-Tabs--active: createThemeVar("backgroundColor-trigger-Tabs--active"); 23 | $backgroundColor-list-Tabs: createThemeVar("backgroundColor-list-Tabs"); 24 | $borderRadius-list-Tabs: createThemeVar("borderRadius-list-Tabs"); 25 | $border-list-Tabs: createThemeVar("border-list-Tabs"); 26 | 27 | @layer components { 28 | .tabs { 29 | display: flex; 30 | width: 100%; 31 | background-color: $backgroundColor-Tabs; 32 | overflow: hidden; 33 | &[data-orientation="vertical"] { 34 | flex-direction: row; 35 | } 36 | 37 | &[data-orientation="horizontal"] { 38 | flex-direction: column; 39 | } 40 | } 41 | 42 | .filler { 43 | flex: 1 1 auto; 44 | 45 | &[data-orientation="vertical"] { 46 | border-right-width: $borderWidth-Tabs; 47 | border-right-style: solid; 48 | border-right-color: $borderColor-Tabs; 49 | } 50 | 51 | &[data-orientation="horizontal"] { 52 | border-bottom-width: $borderWidth-Tabs; 53 | border-bottom-style: solid; 54 | border-bottom-color: $borderColor-Tabs; 55 | } 56 | } 57 | 58 | .tabTrigger { 59 | @include t.paddingVars($themeVars, "trigger-Tabs"); 60 | &.distributeEvenly { 61 | flex: 1 1 auto; 62 | } 63 | 64 | color: $textColor-trigger-Tabs; 65 | background-color: $backgroundColor-trigger-Tabs; 66 | border-radius: $borderRadius-trigger-Tabs; 67 | display: flex; 68 | align-items: center; 69 | justify-content: center; 70 | font-size: t.$fontSize-base; 71 | line-height: 1; 72 | user-select: none; 73 | //border-color: transparent; 74 | border: $border-trigger-Tabs; 75 | 76 | &:hover { 77 | background-color: $backgroundColor-trigger-Tabs--hover; 78 | } 79 | 80 | &[data-orientation="vertical"] { 81 | border-right-width: $borderWidth-Tabs; 82 | border-right-style: solid; 83 | border-right-color: $borderColor-Tabs; 84 | 85 | &[data-state="active"] { 86 | border-right-width: $borderWidth-Tabs; 87 | border-right-style: solid; 88 | border-right-color: $borderColor-active-Tabs; 89 | background-color: $backgroundColor-trigger-Tabs--active; 90 | } 91 | } 92 | 93 | &[data-orientation="horizontal"] { 94 | border-bottom-width: $borderWidth-Tabs; 95 | border-bottom-style: solid; 96 | border-bottom-color: $borderColor-Tabs; 97 | 98 | &[data-state="active"] { 99 | border-bottom-width: $borderWidth-Tabs; 100 | border-bottom-style: solid; 101 | border-bottom-color: $borderColor-active-Tabs; 102 | background-color: $backgroundColor-trigger-Tabs--active; 103 | } 104 | } 105 | 106 | &:hover { 107 | cursor: pointer; 108 | } 109 | } 110 | 111 | .tabsList { 112 | background-color: $backgroundColor-list-Tabs; 113 | border-radius: $borderRadius-list-Tabs; 114 | border: $border-list-Tabs; 115 | position: relative; 116 | z-index: 99; 117 | overflow: hidden; 118 | display: flex; 119 | flex-shrink: 0; 120 | scrollbar-width: thin; 121 | 122 | &[data-orientation="vertical"] { 123 | flex-direction: column; 124 | } 125 | 126 | &[data-orientation="horizontal"] { 127 | flex-direction: row; 128 | } 129 | 130 | &.alignStart { 131 | justify-content: flex-start; 132 | } 133 | 134 | &.alignEnd { 135 | justify-content: flex-end; 136 | } 137 | 138 | &.alignCenter { 139 | justify-content: center; 140 | } 141 | 142 | &.alignStretch { 143 | justify-content: stretch; 144 | } 145 | } 146 | 147 | .tabsList:hover { 148 | overflow: auto; 149 | } 150 | 151 | .tabsContent { 152 | flex-grow: 1; 153 | outline: none; 154 | } 155 | 156 | // Accordion view styles 157 | .accordionView { 158 | width: 100%; 159 | } 160 | 161 | .accordionRoot { 162 | display: flex; 163 | flex-direction: column !important; 164 | width: 100%; 165 | } 166 | 167 | .accordionInterleaved { 168 | display: flex; 169 | flex-direction: column; 170 | width: 100%; 171 | } 172 | 173 | .accordionList { 174 | display: contents; // Makes children act as direct children of parent 175 | background-color: transparent; 176 | border: none; 177 | border-radius: 0; 178 | overflow: visible; 179 | } 180 | 181 | .accordionItem { 182 | display: flex; 183 | flex-direction: column; 184 | width: 100%; 185 | } 186 | 187 | .accordionTrigger { 188 | width: 100%; 189 | justify-content: flex-start; 190 | border-bottom-width: $borderWidth-Tabs; 191 | border-bottom-style: solid; 192 | border-bottom-color: $borderColor-Tabs; 193 | border-right: none !important; 194 | 195 | &[data-state="active"] { 196 | border-bottom-width: $borderWidth-Tabs; 197 | border-bottom-style: solid; 198 | border-bottom-color: $borderColor-active-Tabs; 199 | background-color: $backgroundColor-trigger-Tabs--active; 200 | border-right: none !important; 201 | } 202 | } 203 | 204 | // Style for accordion content to appear inline with headers 205 | .accordionInterleaved .tabsContent { 206 | width: 100%; 207 | order: inherit; 208 | } 209 | } 210 | 211 | // --- We export the theme variables to add them to the component renderer 212 | :export { 213 | themeVars: t.json-stringify($themeVars); 214 | } 215 | ``` -------------------------------------------------------------------------------- /docs/content/components/FlowLayout.md: -------------------------------------------------------------------------------- ```markdown 1 | # FlowLayout [#flowlayout] 2 | 3 | `FlowLayout` positions content in rows with automatic wrapping. When items exceed the available horizontal space, they automatically wrap to a new line. 4 | 5 | For details on how to work with \`FlowLayout\` (like sizing children), see [this guide](/layout#flowlayout). 6 | 7 | ## Using `SpaceFiller` with `FlowLayout` [#using-spacefiller-with-flowlayout] 8 | 9 | The `SpaceFiller` component can be used as a line break. 10 | See the [reference docs](/components/SpaceFiller) for details. 11 | 12 | ## Properties [#properties] 13 | 14 | ### `columnGap` (default: "$gap-normal") [#columngap-default-gap-normal] 15 | 16 | The `columnGap` property specifies the space between items in a single row; it overrides the `gap` value. 17 | 18 | The `columnGap` property specifies the space between items in a single row; it overrides the `gap` value. 19 | 20 | ```xmlui-pg copy display name="Example: columnGap" 21 | ---app copy display 22 | <App> 23 | <FlowLayout columnGap="$space-8"> 24 | <Stack width="25%" height="32px" backgroundColor="red" /> 25 | <Stack width="25%" height="32px" backgroundColor="blue" /> 26 | <Stack width="25%" height="32px" backgroundColor="green" /> 27 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 28 | <Stack width="25%" height="32px" backgroundColor="maroon" /> 29 | <Stack width="25%" height="32px" backgroundColor="teal" /> 30 | <Stack width="25%" height="32px" backgroundColor="seagreen" /> 31 | <Stack width="25%" height="32px" backgroundColor="olive" /> 32 | </FlowLayout> 33 | </App> 34 | ---desc 35 | You can observe no gap between the rows of the `FlowLayout`, as `columnGap` keeps the space between rows intact: 36 | ``` 37 | 38 | ### `gap` (default: "$gap-normal") [#gap-default-gap-normal] 39 | 40 | This property defines the gap between items in the same row and between rows. The FlowLayout component creates a new row when an item is about to overflow the current row. 41 | 42 | The `gap` property defines the gap between items in the same row and between rows. The `FlowLayout` component creates a new row when an item is about to overflow the current row. 43 | 44 | ```xmlui-pg copy display name="Example: gap" 45 | ---app copy display 46 | <App> 47 | <FlowLayout gap="$space-12"> 48 | <Stack width="25%" height="32px" backgroundColor="red" /> 49 | <Stack width="25%" height="32px" backgroundColor="blue" /> 50 | <Stack width="25%" height="32px" backgroundColor="green" /> 51 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 52 | <Stack width="25%" height="32px" backgroundColor="maroon" /> 53 | <Stack width="25%" height="32px" backgroundColor="teal" /> 54 | <Stack width="25%" height="32px" backgroundColor="seagreen" /> 55 | <Stack width="25%" height="32px" backgroundColor="olive" /> 56 | </FlowLayout> 57 | </App> 58 | ---desc 59 | In this markup, only four items fit in a single row. 60 | The `gap` property sets the same gaps within and between rows. 61 | ``` 62 | 63 | This markup demonstrates different `gap` values: 64 | 65 | ```xmlui-pg copy display name="Example: different size units" 66 | ---app copy display 67 | <App> 68 | <FlowLayout> 69 | <Stack width="25%" height="32px" backgroundColor="red" /> 70 | <Stack width="25%" height="32px" backgroundColor="blue" /> 71 | <Stack width="25%" height="32px" backgroundColor="green" /> 72 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 73 | </FlowLayout> 74 | <FlowLayout gap="10px"> 75 | <Stack width="25%" height="32px" backgroundColor="red" /> 76 | <Stack width="25%" height="32px" backgroundColor="blue" /> 77 | <Stack width="25%" height="32px" backgroundColor="green" /> 78 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 79 | </FlowLayout> 80 | <FlowLayout gap="1rem"> 81 | <Stack width="25%" height="32px" backgroundColor="red" /> 82 | <Stack width="25%" height="32px" backgroundColor="blue" /> 83 | <Stack width="25%" height="32px" backgroundColor="green" /> 84 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 85 | </FlowLayout> 86 | <FlowLayout gap="4ch"> 87 | <Stack width="25%" height="32px" backgroundColor="red" /> 88 | <Stack width="25%" height="32px" backgroundColor="blue" /> 89 | <Stack width="25%" height="32px" backgroundColor="green" /> 90 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 91 | </FlowLayout> 92 | </App> 93 | ---desc 94 | All items within a `FlowLayout` instance fit in a single row, so `gap` affects only the space between items. The space between rows comes from the outermost `Stack`. 95 | ``` 96 | 97 | ### `rowGap` (default: "$gap-normal") [#rowgap-default-gap-normal] 98 | 99 | The `rowGap` property specifies the space between the FlowLayout rows; it overrides the `gap` value. 100 | 101 | The `rowGap` property specifies the space between the `FlowLayout` rows; it overrides the `gap` value. 102 | 103 | ```xmlui-pg copy display name="Example: rowGap" 104 | ---app copy display 105 | <App> 106 | <FlowLayout rowGap="2px"> 107 | <Stack width="25%" height="32px" backgroundColor="red" /> 108 | <Stack width="25%" height="32px" backgroundColor="blue" /> 109 | <Stack width="25%" height="32px" backgroundColor="green" /> 110 | <Stack width="25%" height="32px" backgroundColor="yellow" /> 111 | <Stack width="25%" height="32px" backgroundColor="maroon" /> 112 | <Stack width="25%" height="32px" backgroundColor="teal" /> 113 | <Stack width="25%" height="32px" backgroundColor="seagreen" /> 114 | <Stack width="25%" height="32px" backgroundColor="olive" /> 115 | </FlowLayout> 116 | </App> 117 | ---desc 118 | You can observe no gap between the items in a single row of the `FlowLayout`, as `rowGap` keeps the gap within a row intact: 119 | ``` 120 | 121 | ## Events [#events] 122 | 123 | This component does not have any events. 124 | 125 | ## Exposed Methods [#exposed-methods] 126 | 127 | This component does not expose any methods. 128 | 129 | ## Styling [#styling] 130 | 131 | This component does not have any styles. 132 | ``` -------------------------------------------------------------------------------- /docs/content/components/MenuItem.md: -------------------------------------------------------------------------------- ```markdown 1 | # MenuItem [#menuitem] 2 | 3 | `MenuItem` represents individual clickable items within dropdown menus and other menu components. Each menu item can display text, icons, and respond to clicks with either navigation or custom actions, making it the building block for interactive menu systems. 4 | 5 | **Key features:** 6 | - **Action handling**: Support both navigation (`to` property) and custom click handlers 7 | - **Visual feedback**: Built-in active, hover, and disabled states for clear user interaction 8 | - **Icon support**: Optional icons with flexible positioning (start or end) 9 | - **Menu integration**: Designed to work seamlessly within `DropdownMenu` and `SubMenuItem` hierarchies 10 | 11 | **Usage pattern:** 12 | Always used within menu containers like `DropdownMenu`. Use `to` for navigation or `onClick` for custom actions. For complex menu structures, combine with `MenuSeparator` and `SubMenuItem` components. 13 | 14 | ## Properties [#properties] 15 | 16 | ### `active` (default: false) [#active-default-false] 17 | 18 | This property indicates if the specified menu item is active. 19 | 20 | ```xmlui-pg copy display name="Example: active" height="200px" 21 | <App> 22 | <DropdownMenu label="DropdownMenu"> 23 | <MenuItem icon="drive" active="true">Item 1</MenuItem> 24 | <MenuItem icon="trash">Item 2</MenuItem> 25 | <MenuItem icon="email">Item 3</MenuItem> 26 | </DropdownMenu> 27 | </App> 28 | ``` 29 | 30 | ### `enabled` (default: true) [#enabled-default-true] 31 | 32 | This boolean property value indicates whether the component responds to user events (`true`) or not (`false`). 33 | 34 | ### `icon` [#icon] 35 | 36 | This property names an optional icon to display with the menu item. You can use component-specific icons in the format "iconName:MenuItem". 37 | 38 | ```xmlui-pg copy display name="Example: icon" height="200px" 39 | <App> 40 | <DropdownMenu label="DropdownMenu"> 41 | <MenuItem icon="drive">Item 1</MenuItem> 42 | <MenuItem icon="trash">Item 2</MenuItem> 43 | <MenuItem icon="email">Item 3</MenuItem> 44 | </DropdownMenu> 45 | </App> 46 | ``` 47 | 48 | ### `iconPosition` (default: "start") [#iconposition-default-start] 49 | 50 | This property allows you to determine the position of the icon displayed in the menu item. 51 | 52 | Available values: 53 | 54 | | Value | Description | 55 | | --- | --- | 56 | | `start` | The icon will appear at the start (left side when the left-to-right direction is set) **(default)** | 57 | | `end` | The icon will appear at the end (right side when the left-to-right direction is set) | 58 | 59 | ```xmlui-pg copy display name="Example: iconPosition" height="200px" 60 | <App> 61 | <DropdownMenu label="DropdownMenu"> 62 | <MenuItem icon="drive" iconPosition="start">Item 1</MenuItem> 63 | <MenuItem icon="trash" iconPosition="end">Item 2</MenuItem> 64 | <MenuItem icon="email">Item 3</MenuItem> 65 | </DropdownMenu> 66 | </App> 67 | ``` 68 | 69 | ### `label` [#label] 70 | 71 | This property sets the label of the component. If not set, the component will not display a label. 72 | 73 | ### `to` [#to] 74 | 75 | This property defines the URL of the menu item. If this property is defined (and the `click` event does not have an event handler), clicking the menu item navigates to this link. 76 | 77 | ## Events [#events] 78 | 79 | ### `click` [#click] 80 | 81 | This event is triggered when the MenuItem is clicked. 82 | 83 | This event is fired when the user clicks the menu item. With an event handler, you can define how to respond to the user's click. If this event does not have an associated event handler but the `to` property has a value, clicking the component navigates the URL set in `to`. 84 | 85 | If both properties are defined, `click` takes precedence. 86 | 87 | ```xmlui-pg copy display name="Example: click" height="200px" 88 | <DropdownMenu label="DropdownMenu"> 89 | <MenuItem onClick="toast('Item 1 clicked')">Item 1</MenuItem> 90 | <MenuItem onClick="toast('Item 2 clicked')">Item 2</MenuItem> 91 | <MenuItem onClick="toast('Item 3 clicked')">Item 3</MenuItem> 92 | </DropdownMenu> 93 | ``` 94 | 95 | ## Exposed Methods [#exposed-methods] 96 | 97 | This component does not expose any methods. 98 | 99 | ## Styling [#styling] 100 | 101 | ### Theme Variables [#theme-variables] 102 | 103 | | Variable | Default Value (Light) | Default Value (Dark) | 104 | | --- | --- | --- | 105 | | [backgroundColor](../styles-and-themes/common-units/#color)-MenuItem | $backgroundColor-dropdown-item | $backgroundColor-dropdown-item | 106 | | [backgroundColor](../styles-and-themes/common-units/#color)-MenuItem--active | $backgroundColor-dropdown-item--active | $backgroundColor-dropdown-item--active | 107 | | [backgroundColor](../styles-and-themes/common-units/#color)-MenuItem--active--hover | *none* | *none* | 108 | | [backgroundColor](../styles-and-themes/common-units/#color)-MenuItem--hover | $backgroundColor-dropdown-item--hover | $backgroundColor-dropdown-item--hover | 109 | | [color](../styles-and-themes/common-units/#color)-MenuItem | $textColor-primary | $textColor-primary | 110 | | [color](../styles-and-themes/common-units/#color)-MenuItem--active | $color-primary | $color-primary | 111 | | [color](../styles-and-themes/common-units/#color)-MenuItem--active--hover | *none* | *none* | 112 | | [color](../styles-and-themes/common-units/#color)-MenuItem--disabled | $textColor--disabled | $textColor--disabled | 113 | | [color](../styles-and-themes/common-units/#color)-MenuItem--hover | inherit | inherit | 114 | | [fontFamily](../styles-and-themes/common-units/#fontFamily)-MenuItem | $fontFamily | $fontFamily | 115 | | [fontSize](../styles-and-themes/common-units/#size)-MenuItem | $fontSize-sm | $fontSize-sm | 116 | | [gap](../styles-and-themes/common-units/#size)-MenuItem | $space-2 | $space-2 | 117 | | [paddingHorizontal](../styles-and-themes/common-units/#size)-MenuItem | $space-3 | $space-3 | 118 | | [paddingVertical](../styles-and-themes/common-units/#size)-MenuItem | $space-2 | $space-2 | 119 | ``` -------------------------------------------------------------------------------- /xmlui/dev-docs/next/logging-consistency-implementation-summary.md: -------------------------------------------------------------------------------- ```markdown 1 | # Logging Consistency Implementation Summary 2 | 3 | ## Overview 4 | Successfully implemented logging consistency across the XMLUI documentation generation scripts to provide standardized logging patterns, better debugging capabilities, and consistent message formatting. 5 | 6 | ## Changes Made 7 | 8 | ### 1. Created Standardized Logging Infrastructure 9 | 10 | #### `logging-standards.mjs` - New Centralized Logging Module 11 | - **Logging Patterns**: Predefined message templates for common operations 12 | - **Standard Logger**: Wrapper functions for consistent logging calls 13 | - **Scoped Logger**: Context-aware logging with module/operation prefixes 14 | - **Log Levels**: Standardized severity levels (INFO, WARN, ERROR) 15 | 16 | #### Enhanced `logger.mjs` 17 | - Added `warn()` method as alias for `warning()` to support both naming conventions 18 | - Maintains backward compatibility while providing flexibility 19 | 20 | ### 2. Key Features of the New Logging System 21 | 22 | #### Predefined Message Patterns 23 | ```javascript 24 | LOGGING_PATTERNS = { 25 | OPERATION_START: (operation) => `Starting ${operation}...`, 26 | OPERATION_COMPLETE: (operation) => `Completed ${operation}`, 27 | FILE_PROCESSING: (filePath) => `Processing file: ${filePath}`, 28 | COMPONENT_PROCESSING: (componentName) => `Processing component: ${componentName}`, 29 | // ... and many more standardized patterns 30 | } 31 | ``` 32 | 33 | #### Scoped Logging 34 | ```javascript 35 | const logger = createScopedLogger("ThemeGenerator"); 36 | logger.operationStart("theme file generation"); 37 | // Output: [ThemeGenerator] Starting theme file generation... 38 | ``` 39 | 40 | #### Consistent Method Names 41 | - `logger.info()` - Informational messages 42 | - `logger.warn()` - Warning messages (standardized from `warning()`) 43 | - `logger.error()` - Error messages 44 | 45 | ### 3. Updated Scripts with Consistent Logging 46 | 47 | #### `create-theme-files.mjs` 48 | - **Before**: Mixed console.log and basic logger calls 49 | - **After**: 50 | - Uses scoped logger: `createScopedLogger("ThemeGenerator")` 51 | - Standardized operation tracking: `operationStart()`, `operationComplete()` 52 | - Consistent component processing: `componentProcessing()` 53 | - Standardized file operations: `fileWritten()` 54 | 55 | #### `get-docs.mjs` 56 | - **Before**: Mixed logger.info/warning calls 57 | - **After**: 58 | - Multiple scoped loggers for different operations 59 | - `createScopedLogger("DocsGenerator")` for main operations 60 | - `createScopedLogger("FolderCleaner")` for cleanup operations 61 | - `createScopedLogger("PackageLoader")` for package operations 62 | - Standardized package operations: `packageLoaded()`, `packageSkipped()` 63 | 64 | #### `build-pages-map.mjs` 65 | - **Before**: Used `logger.warning()` directly 66 | - **After**: 67 | - Uses scoped logger: `createScopedLogger("PagesMapBuilder")` 68 | - Standardized operation tracking 69 | - Consistent warning messages with `warn()` 70 | 71 | #### `build-downloads-map.mjs` 72 | - **Before**: Used `logger.warning()` directly 73 | - **After**: 74 | - Uses scoped logger: `createScopedLogger("DownloadsMapBuilder")` 75 | - Standardized operation tracking 76 | - Consistent warning messages with `warn()` 77 | 78 | ### 4. Logging Consistency Benefits 79 | 80 | #### Standardized Message Formats 81 | - All operation messages follow consistent patterns 82 | - Scoped messages provide clear context about which module is logging 83 | - Predictable message structure for easier parsing and monitoring 84 | 85 | #### Better Debugging 86 | - Clear operation boundaries with start/complete logging 87 | - Context-aware messages help identify the source of issues 88 | - Consistent formatting makes logs easier to read and filter 89 | 90 | #### Maintainability 91 | - Centralized logging patterns reduce code duplication 92 | - Easy to modify message formats across all scripts 93 | - Standard operations (file processing, component handling) use shared patterns 94 | 95 | #### Flexibility 96 | - Supports both `warn()` and `warning()` methods for backward compatibility 97 | - Scoped loggers can be easily created for new modules 98 | - Extensible pattern system for new operation types 99 | 100 | ## Before/After Examples 101 | 102 | ### Before (Inconsistent) 103 | ```javascript 104 | console.log(`Processing component: ${key}`); 105 | logger.info(LOG_MESSAGES.GENERATING_EXTENSION_DOCS); 106 | logger.warning(`Duplicate entries found...`); 107 | ``` 108 | 109 | ### After (Consistent) 110 | ```javascript 111 | const logger = createScopedLogger("ThemeGenerator"); 112 | logger.componentProcessing(key); 113 | logger.operationStart("extension documentation generation"); 114 | logger.warn(`Duplicate entries found...`); 115 | ``` 116 | 117 | ## Files Modified 118 | - ✅ `scripts/generate-docs/logging-standards.mjs` (new) 119 | - ✅ `scripts/generate-docs/logger.mjs` (enhanced with warn() alias) 120 | - ✅ `scripts/generate-docs/create-theme-files.mjs` (standardized) 121 | - ✅ `scripts/generate-docs/get-docs.mjs` (standardized) 122 | - ✅ `scripts/generate-docs/build-pages-map.mjs` (standardized) 123 | - ✅ `scripts/generate-docs/build-downloads-map.mjs` (standardized) 124 | 125 | ## Standards Established 126 | 127 | ### 1. **Scoped Logging**: All modules use scoped loggers for context 128 | ### 2. **Operation Tracking**: Start/complete logging for major operations 129 | ### 3. **Consistent Method Names**: Standardized on `info()`, `warn()`, `error()` 130 | ### 4. **Message Patterns**: Predefined templates for common operations 131 | ### 5. **Context Awareness**: Module names in log messages for easy identification 132 | 133 | ## Next Steps 134 | This logging standardization provides: 135 | - A solid foundation for adding structured logging and monitoring 136 | - Consistent patterns that can be extended to other scripts 137 | - Better debugging capabilities for development and production 138 | - Framework for adding log levels, filtering, and external logging systems 139 | 140 | The logging system is now professional-grade, consistent, and maintainable across the entire documentation generation pipeline. 141 | ``` -------------------------------------------------------------------------------- /docs/scripts/generate-rss.js: -------------------------------------------------------------------------------- ```javascript 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | // Extract blog posts data from Main.xmlui's var.posts definition 5 | function extractBlogPosts() { 6 | const mainXmluiPath = path.join(__dirname, '../src/Main.xmlui'); 7 | const content = fs.readFileSync(mainXmluiPath, 'utf8'); 8 | 9 | try { 10 | // Find the var.posts definition - extract content between backticks 11 | const postsMatch = content.match(/var\.posts\s*=\s*`\{\{([^`]*)\}\}`/s); 12 | if (!postsMatch) { 13 | console.warn('No var.posts found in Main.xmlui'); 14 | return []; 15 | } 16 | 17 | const postsContent = postsMatch[1].trim(); 18 | const blogPosts = []; 19 | 20 | // Parse each post entry (p1: {...}, p2: {...}, etc.) 21 | const postMatches = postsContent.match(/(\w+):\s*\{([^}]*)\}/g); 22 | 23 | if (postMatches) { 24 | postMatches.forEach(postMatch => { 25 | const [, postKey] = postMatch.match(/(\w+):/); 26 | 27 | // Extract properties from the post object 28 | const titleMatch = postMatch.match(/title:\s*"([^"]*)"/); 29 | const slugMatch = postMatch.match(/slug:\s*"([^"]*)"/); 30 | const authorMatch = postMatch.match(/author:\s*"([^"]*)"/); 31 | const dateMatch = postMatch.match(/date:\s*"([^"]*)"/); 32 | 33 | if (titleMatch && slugMatch && authorMatch && dateMatch) { 34 | blogPosts.push({ 35 | key: postKey, 36 | title: titleMatch[1], 37 | slug: slugMatch[1], 38 | author: authorMatch[1], 39 | date: dateMatch[1] 40 | }); 41 | } 42 | }); 43 | } 44 | 45 | return blogPosts; 46 | } catch (error) { 47 | console.error('Error extracting blog posts:', error.message); 48 | return []; 49 | } 50 | } 51 | 52 | // Read blog post content and extract description 53 | function getPostDescription(slug) { 54 | try { 55 | const postPath = path.join(__dirname, '../public/blog', `${slug}.md`); 56 | const content = fs.readFileSync(postPath, 'utf8'); 57 | 58 | // Strip markdown formatting for description 59 | let plainText = content 60 | .replace(/^#+\s+/gm, '') // Remove headers 61 | .replace(/\*\*(.*?)\*\*/g, '$1') // Remove bold 62 | .replace(/\*(.*?)\*/g, '$1') // Remove italic 63 | .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') // Remove links, keep text 64 | .replace(/!\[([^\]]*)\]\([^)]*\)/g, '') // Remove images 65 | .replace(/`([^`]*)`/g, '$1') // Remove inline code 66 | .replace(/```[\s\S]*?```/g, '') // Remove code blocks 67 | .replace(/\n+/g, ' ') // Replace newlines with spaces 68 | .replace(/\s+/g, ' ') // Normalize whitespace 69 | .trim(); 70 | 71 | // Take first 250 characters for description 72 | if (plainText.length > 250) { 73 | return plainText.substring(0, 250).trim() + '...'; 74 | } 75 | 76 | return plainText; 77 | } catch (error) { 78 | console.warn(`Warning: Could not read post content for ${slug}:`, error.message); 79 | return 'Blog post content not available.'; 80 | } 81 | } 82 | 83 | // Convert date string to RFC 822 format for RSS 84 | function formatDate(dateStr) { 85 | const date = new Date(dateStr); 86 | return date.toUTCString(); 87 | } 88 | 89 | // Generate RSS XML 90 | function generateRSS(blogPosts) { 91 | const now = new Date().toUTCString(); 92 | 93 | let rss = `<?xml version="1.0" encoding="UTF-8"?> 94 | <rss version="2.0"> 95 | <channel> 96 | <title>XMLUI Blog</title> 97 | <link>https://docs.xmlui.org</link> 98 | <description>Latest updates, tutorials, and insights for XMLUI - the declarative UI framework. Stay informed about new features, best practices, and community highlights.</description> 99 | <language>en</language> 100 | <lastBuildDate>${now}</lastBuildDate> 101 | `; 102 | 103 | // Sort posts by date (newest first) 104 | blogPosts.sort((a, b) => new Date(b.date) - new Date(a.date)); 105 | 106 | blogPosts.forEach(post => { 107 | const pubDate = formatDate(post.date); 108 | const postUrl = `https://docs.xmlui.org/${post.slug}`; 109 | const description = getPostDescription(post.slug); 110 | 111 | rss += ` 112 | <item> 113 | <title>${escapeXml(post.title)}</title> 114 | <link>${postUrl}</link> 115 | <description>${escapeXml(description)}</description> 116 | <pubDate>${pubDate}</pubDate> 117 | <author>[email protected] (${escapeXml(post.author)})</author> 118 | <guid>${postUrl}</guid> 119 | </item>`; 120 | }); 121 | 122 | rss += ` 123 | </channel> 124 | </rss>`; 125 | 126 | return rss; 127 | } 128 | 129 | // Escape XML special characters 130 | function escapeXml(text) { 131 | if (typeof text !== 'string') { 132 | text = String(text); 133 | } 134 | return text 135 | .replace(/&/g, '&') 136 | .replace(/</g, '<') 137 | .replace(/>/g, '>') 138 | .replace(/"/g, '"') 139 | .replace(/'/g, '''); 140 | } 141 | 142 | // Main execution 143 | function main() { 144 | try { 145 | console.log('Generating RSS feed...'); 146 | 147 | const blogPosts = extractBlogPosts(); 148 | console.log(`Found ${blogPosts.length} blog post(s)`); 149 | 150 | if (blogPosts.length === 0) { 151 | console.warn('No blog posts found, creating empty RSS feed'); 152 | } else { 153 | blogPosts.forEach(post => { 154 | console.log(` - "${post.title}" by ${post.author} (${post.date})`); 155 | }); 156 | } 157 | 158 | const rssContent = generateRSS(blogPosts); 159 | const outputPath = path.join(__dirname, '../public/feed.rss'); 160 | 161 | // Ensure the public directory exists 162 | const publicDir = path.dirname(outputPath); 163 | if (!fs.existsSync(publicDir)) { 164 | fs.mkdirSync(publicDir, { recursive: true }); 165 | } 166 | 167 | fs.writeFileSync(outputPath, rssContent); 168 | console.log(`RSS feed generated successfully: ${outputPath}`); 169 | console.log(`Feed will be available at: https://docs.xmlui.org/feed.rss`); 170 | 171 | } catch (error) { 172 | console.error('Error generating RSS feed:', error); 173 | process.exit(1); 174 | } 175 | } 176 | 177 | // Run if called directly 178 | if (require.main === module) { 179 | main(); 180 | } 181 | 182 | module.exports = { extractBlogPosts, generateRSS, getPostDescription }; 183 | ```