This is page 12 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 │ └── cool-queens-look.md ├── .eslintrc.cjs ├── .github │ ├── build-checklist.png │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── deploy-blog.yml │ ├── deploy-docs-optimized.yml │ ├── deploy-docs.yml │ ├── prepare-versions.yml │ ├── release-packages.yml │ ├── run-all-tests.yml │ └── run-smoke-tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── blog │ ├── .gitignore │ ├── .gitkeep │ ├── CHANGELOG.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── blog │ │ │ ├── images │ │ │ │ ├── blog-page-component.png │ │ │ │ ├── blog-scrabble.png │ │ │ │ ├── integrated-blog-search.png │ │ │ │ └── lorem-ipsum.png │ │ │ ├── lorem-ipsum.md │ │ │ ├── newest-post.md │ │ │ ├── older-post.md │ │ │ └── welcome-to-the-xmlui-blog.md │ │ ├── mockServiceWorker.js │ │ ├── netlify.toml │ │ ├── resources │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ └── for-download │ │ │ │ └── xmlui │ │ │ │ └── xmlui-standalone.umd.js │ │ │ ├── github.svg │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ └── PageNotFound.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── CONTRIBUTING.md ├── docs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── ComponentRefLinks.txt │ ├── content │ │ ├── _meta.json │ │ ├── components │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── APICall.md │ │ │ ├── App.md │ │ │ ├── AppHeader.md │ │ │ ├── AppState.md │ │ │ ├── AutoComplete.md │ │ │ ├── Avatar.md │ │ │ ├── Backdrop.md │ │ │ ├── Badge.md │ │ │ ├── BarChart.md │ │ │ ├── Bookmark.md │ │ │ ├── Breakout.md │ │ │ ├── Button.md │ │ │ ├── Card.md │ │ │ ├── Carousel.md │ │ │ ├── ChangeListener.md │ │ │ ├── Checkbox.md │ │ │ ├── CHStack.md │ │ │ ├── ColorPicker.md │ │ │ ├── Column.md │ │ │ ├── ContentSeparator.md │ │ │ ├── CVStack.md │ │ │ ├── DataSource.md │ │ │ ├── DateInput.md │ │ │ ├── DatePicker.md │ │ │ ├── DonutChart.md │ │ │ ├── DropdownMenu.md │ │ │ ├── EmojiSelector.md │ │ │ ├── ExpandableItem.md │ │ │ ├── FileInput.md │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FlowLayout.md │ │ │ ├── Footer.md │ │ │ ├── Form.md │ │ │ ├── FormItem.md │ │ │ ├── FormSection.md │ │ │ ├── Fragment.md │ │ │ ├── H1.md │ │ │ ├── H2.md │ │ │ ├── H3.md │ │ │ ├── H4.md │ │ │ ├── H5.md │ │ │ ├── H6.md │ │ │ ├── Heading.md │ │ │ ├── HSplitter.md │ │ │ ├── HStack.md │ │ │ ├── Icon.md │ │ │ ├── IFrame.md │ │ │ ├── Image.md │ │ │ ├── Items.md │ │ │ ├── LabelList.md │ │ │ ├── Legend.md │ │ │ ├── LineChart.md │ │ │ ├── Link.md │ │ │ ├── List.md │ │ │ ├── Logo.md │ │ │ ├── Markdown.md │ │ │ ├── MenuItem.md │ │ │ ├── MenuSeparator.md │ │ │ ├── ModalDialog.md │ │ │ ├── NavGroup.md │ │ │ ├── NavLink.md │ │ │ ├── NavPanel.md │ │ │ ├── NoResult.md │ │ │ ├── NumberBox.md │ │ │ ├── Option.md │ │ │ ├── Page.md │ │ │ ├── PageMetaTitle.md │ │ │ ├── Pages.md │ │ │ ├── Pagination.md │ │ │ ├── PasswordInput.md │ │ │ ├── PieChart.md │ │ │ ├── ProgressBar.md │ │ │ ├── Queue.md │ │ │ ├── RadioGroup.md │ │ │ ├── RealTimeAdapter.md │ │ │ ├── Redirect.md │ │ │ ├── Select.md │ │ │ ├── Slider.md │ │ │ ├── Slot.md │ │ │ ├── SpaceFiller.md │ │ │ ├── Spinner.md │ │ │ ├── Splitter.md │ │ │ ├── Stack.md │ │ │ ├── StickyBox.md │ │ │ ├── SubMenuItem.md │ │ │ ├── Switch.md │ │ │ ├── TabItem.md │ │ │ ├── Table.md │ │ │ ├── TableOfContents.md │ │ │ ├── Tabs.md │ │ │ ├── Text.md │ │ │ ├── TextArea.md │ │ │ ├── TextBox.md │ │ │ ├── Theme.md │ │ │ ├── TimeInput.md │ │ │ ├── Timer.md │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneSwitch.md │ │ │ ├── Tooltip.md │ │ │ ├── Tree.md │ │ │ ├── VSplitter.md │ │ │ ├── VStack.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ ├── xmlui-spreadsheet │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Spreadsheet.md │ │ │ └── xmlui-website-blocks │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Carousel.md │ │ │ ├── HelloMd.md │ │ │ ├── HeroSection.md │ │ │ └── ScrollToTop.md │ │ └── extensions │ │ ├── _meta.json │ │ ├── xmlui-animations │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ ├── Animation.md │ │ │ ├── FadeAnimation.md │ │ │ ├── FadeInAnimation.md │ │ │ ├── FadeOutAnimation.md │ │ │ ├── ScaleAnimation.md │ │ │ └── SlideInAnimation.md │ │ └── xmlui-website-blocks │ │ ├── _meta.json │ │ ├── _overview.md │ │ ├── Carousel.md │ │ ├── HelloMd.md │ │ ├── HeroSection.md │ │ └── ScrollToTop.md │ ├── extensions.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── public │ │ ├── feed.rss │ │ ├── mockServiceWorker.js │ │ ├── pages │ │ │ ├── _meta.json │ │ │ ├── app-structure.md │ │ │ ├── build-editor-component.md │ │ │ ├── build-hello-world-component.md │ │ │ ├── components-intro.md │ │ │ ├── context-variables.md │ │ │ ├── forms.md │ │ │ ├── globals.md │ │ │ ├── glossary.md │ │ │ ├── helper-tags.md │ │ │ ├── hosted-deployment.md │ │ │ ├── howto │ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md │ │ │ │ ├── chain-a-refetch.md │ │ │ │ ├── debug-a-component.md │ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md │ │ │ │ ├── delegate-a-method.md │ │ │ │ ├── do-custom-form-validation.md │ │ │ │ ├── expose-a-method-from-a-component.md │ │ │ │ ├── filter-and-transform-data-from-an-api.md │ │ │ │ ├── group-items-in-list-by-a-property.md │ │ │ │ ├── handle-background-operations.md │ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md │ │ │ │ ├── make-a-set-of-equal-width-cards.md │ │ │ │ ├── make-a-table-responsive.md │ │ │ │ ├── modify-a-value-reported-in-a-column.md │ │ │ │ ├── paginate-a-list.md │ │ │ │ ├── pass-data-to-a-modal-dialog.md │ │ │ │ ├── react-to-button-click-not-keystrokes.md │ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md │ │ │ │ ├── share-a-modaldialog-across-components.md │ │ │ │ ├── sync-selections-between-table-and-list-views.md │ │ │ │ ├── update-ui-optimistically.md │ │ │ │ ├── use-built-in-form-validation.md │ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md │ │ │ ├── howto.md │ │ │ ├── intro.md │ │ │ ├── layout.md │ │ │ ├── markup.md │ │ │ ├── mcp.md │ │ │ ├── modal-dialogs.md │ │ │ ├── news-and-reviews.md │ │ │ ├── reactive-intro.md │ │ │ ├── refactoring.md │ │ │ ├── routing-and-links.md │ │ │ ├── samples │ │ │ │ ├── color-palette.xmlui │ │ │ │ ├── color-values.xmlui │ │ │ │ ├── shadow-sizes.xmlui │ │ │ │ ├── spacing-sizes.xmlui │ │ │ │ ├── swatch.xmlui │ │ │ │ ├── theme-gallery-brief.xmlui │ │ │ │ └── theme-gallery.xmlui │ │ │ ├── scoping.md │ │ │ ├── scripting.md │ │ │ ├── styles-and-themes │ │ │ │ ├── common-units.md │ │ │ │ ├── layout-props.md │ │ │ │ ├── theme-variable-defaults.md │ │ │ │ ├── theme-variables.md │ │ │ │ └── themes.md │ │ │ ├── template-properties.md │ │ │ ├── test.md │ │ │ ├── tutorial-01.md │ │ │ ├── tutorial-02.md │ │ │ ├── tutorial-03.md │ │ │ ├── tutorial-04.md │ │ │ ├── tutorial-05.md │ │ │ ├── tutorial-06.md │ │ │ ├── tutorial-07.md │ │ │ ├── tutorial-08.md │ │ │ ├── tutorial-09.md │ │ │ ├── tutorial-10.md │ │ │ ├── tutorial-11.md │ │ │ ├── tutorial-12.md │ │ │ ├── universal-properties.md │ │ │ ├── user-defined-components.md │ │ │ ├── vscode.md │ │ │ ├── working-with-markdown.md │ │ │ ├── working-with-text.md │ │ │ ├── xmlui-animations │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── Animation.md │ │ │ │ ├── FadeAnimation.md │ │ │ │ ├── FadeInAnimation.md │ │ │ │ ├── FadeOutAnimation.md │ │ │ │ ├── ScaleAnimation.md │ │ │ │ └── SlideInAnimation.md │ │ │ ├── xmlui-charts │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ ├── BarChart.md │ │ │ │ ├── DonutChart.md │ │ │ │ ├── LabelList.md │ │ │ │ ├── Legend.md │ │ │ │ ├── LineChart.md │ │ │ │ └── PieChart.md │ │ │ ├── xmlui-pdf │ │ │ │ ├── _meta.json │ │ │ │ ├── _overview.md │ │ │ │ └── Pdf.md │ │ │ └── xmlui-spreadsheet │ │ │ ├── _meta.json │ │ │ ├── _overview.md │ │ │ └── Spreadsheet.md │ │ ├── resources │ │ │ ├── devdocs │ │ │ │ ├── debug-proxy-object-2.png │ │ │ │ ├── debug-proxy-object.png │ │ │ │ ├── table_editor_01.png │ │ │ │ ├── table_editor_02.png │ │ │ │ ├── table_editor_03.png │ │ │ │ ├── table_editor_04.png │ │ │ │ ├── table_editor_05.png │ │ │ │ ├── table_editor_06.png │ │ │ │ ├── table_editor_07.png │ │ │ │ ├── table_editor_08.png │ │ │ │ ├── table_editor_09.png │ │ │ │ ├── table_editor_10.png │ │ │ │ ├── table_editor_11.png │ │ │ │ ├── table-editor-01.png │ │ │ │ ├── table-editor-02.png │ │ │ │ ├── table-editor-03.png │ │ │ │ ├── table-editor-04.png │ │ │ │ ├── table-editor-06.png │ │ │ │ ├── table-editor-07.png │ │ │ │ ├── table-editor-08.png │ │ │ │ ├── table-editor-09.png │ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png │ │ │ ├── favicon.ico │ │ │ ├── files │ │ │ │ ├── clients.json │ │ │ │ ├── daily-revenue.json │ │ │ │ ├── dashboard-stats.json │ │ │ │ ├── demo.xmlui │ │ │ │ ├── demo.xmlui.xs │ │ │ │ ├── downloads │ │ │ │ │ └── downloads.json │ │ │ │ ├── for-download │ │ │ │ │ ├── index-with-api.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── mockApi.js │ │ │ │ │ ├── start-darwin.sh │ │ │ │ │ ├── start-linux.sh │ │ │ │ │ ├── start.bat │ │ │ │ │ └── xmlui │ │ │ │ │ └── xmlui-standalone.umd.js │ │ │ │ ├── getting-started │ │ │ │ │ ├── cl-tutorial-final.zip │ │ │ │ │ ├── cl-tutorial.zip │ │ │ │ │ ├── cl-tutorial2.zip │ │ │ │ │ ├── cl-tutorial3.zip │ │ │ │ │ ├── cl-tutorial4.zip │ │ │ │ │ ├── cl-tutorial5.zip │ │ │ │ │ ├── cl-tutorial6.zip │ │ │ │ │ ├── getting-started.zip │ │ │ │ │ ├── hello-xmlui.zip │ │ │ │ │ ├── xmlui-empty.zip │ │ │ │ │ └── xmlui-starter.zip │ │ │ │ ├── howto │ │ │ │ │ └── component-icons │ │ │ │ │ └── up-arrow.svg │ │ │ │ ├── invoices.json │ │ │ │ ├── monthly-status.json │ │ │ │ ├── news-and-reviews.json │ │ │ │ ├── products.json │ │ │ │ ├── releases.json │ │ │ │ ├── tutorials │ │ │ │ │ ├── datasource │ │ │ │ │ │ └── api.ts │ │ │ │ │ └── p2do │ │ │ │ │ ├── api.ts │ │ │ │ │ └── todo-logo.svg │ │ │ │ └── xmlui.json │ │ │ ├── github.svg │ │ │ ├── images │ │ │ │ ├── apiaction-tutorial │ │ │ │ │ ├── add-success.png │ │ │ │ │ ├── apiaction-param.png │ │ │ │ │ ├── change-completed.png │ │ │ │ │ ├── change-in-progress.png │ │ │ │ │ ├── confirm-delete.png │ │ │ │ │ ├── data-error.png │ │ │ │ │ ├── data-progress.png │ │ │ │ │ ├── data-success.png │ │ │ │ │ ├── display-1.png │ │ │ │ │ ├── item-deleted.png │ │ │ │ │ ├── item-updated.png │ │ │ │ │ ├── missing-api-key.png │ │ │ │ │ ├── new-item-added.png │ │ │ │ │ └── test-message.png │ │ │ │ ├── chat-api │ │ │ │ │ └── domain-model.svg │ │ │ │ ├── components │ │ │ │ │ ├── image │ │ │ │ │ │ └── breakfast.jpg │ │ │ │ │ ├── markdown │ │ │ │ │ │ └── colors.png │ │ │ │ │ └── modal │ │ │ │ │ ├── deep_link_dialog_1.jpg │ │ │ │ │ └── deep_link_dialog_2.jpg │ │ │ │ ├── create-apps │ │ │ │ │ ├── collapsed-vertical.png │ │ │ │ │ ├── using-forms-warning-dialog.png │ │ │ │ │ └── using-forms.png │ │ │ │ ├── datasource-tutorial │ │ │ │ │ ├── data-with-header.png │ │ │ │ │ ├── filtered-data.png │ │ │ │ │ ├── filtered-items.png │ │ │ │ │ ├── initial-page-items.png │ │ │ │ │ ├── list-items.png │ │ │ │ │ ├── next-page-items.png │ │ │ │ │ ├── no-data.png │ │ │ │ │ ├── pagination-1.jpg │ │ │ │ │ ├── pagination-1.png │ │ │ │ │ ├── polling-1.png │ │ │ │ │ ├── refetch-data.png │ │ │ │ │ ├── slow-loading.png │ │ │ │ │ ├── test-message.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── unconventional-data.png │ │ │ │ │ └── unfiltered-items.png │ │ │ │ ├── flower.jpg │ │ │ │ ├── get-started │ │ │ │ │ ├── add-new-contact.png │ │ │ │ │ ├── app-modified.png │ │ │ │ │ ├── app-start.png │ │ │ │ │ ├── app-with-boxes.png │ │ │ │ │ ├── app-with-toast.png │ │ │ │ │ ├── boilerplate-structure.png │ │ │ │ │ ├── cl-initial.png │ │ │ │ │ ├── cl-start.png │ │ │ │ │ ├── contact-counts.png │ │ │ │ │ ├── contact-dialog-title.png │ │ │ │ │ ├── contact-dialog.png │ │ │ │ │ ├── contact-menus.png │ │ │ │ │ ├── contact-predicates.png │ │ │ │ │ ├── context-menu.png │ │ │ │ │ ├── dashboard-numbers.png │ │ │ │ │ ├── default-contact-list.png │ │ │ │ │ ├── delete-contact.png │ │ │ │ │ ├── delete-task.png │ │ │ │ │ ├── detailed-template.png │ │ │ │ │ ├── edit-contact-details.png │ │ │ │ │ ├── edited-contact-saved.png │ │ │ │ │ ├── empty-sections.png │ │ │ │ │ ├── filter-completed.png │ │ │ │ │ ├── fullwidth-desktop.png │ │ │ │ │ ├── fullwidth-mobile.png │ │ │ │ │ ├── initial-table.png │ │ │ │ │ ├── items-and-badges.png │ │ │ │ │ ├── loading-message.png │ │ │ │ │ ├── new-contact-button.png │ │ │ │ │ ├── new-contact-saved.png │ │ │ │ │ ├── no-empty-sections.png │ │ │ │ │ ├── personal-todo-initial.png │ │ │ │ │ ├── piechart.png │ │ │ │ │ ├── review-today.png │ │ │ │ │ ├── rudimentary-dashboard.png │ │ │ │ │ ├── section-collapsed.png │ │ │ │ │ ├── sectioned-items.png │ │ │ │ │ ├── sections-ordered.png │ │ │ │ │ ├── spacex-list-with-links.png │ │ │ │ │ ├── spacex-list.png │ │ │ │ │ ├── start-personal-todo-1.png │ │ │ │ │ ├── submit-new-contact.png │ │ │ │ │ ├── submit-new-task.png │ │ │ │ │ ├── syntax-highlighting.png │ │ │ │ │ ├── table-with-badge.png │ │ │ │ │ ├── template-with-card.png │ │ │ │ │ ├── test-emulated-api.png │ │ │ │ │ ├── Thumbs.db │ │ │ │ │ ├── todo-logo.png │ │ │ │ │ └── xmlui-tools.png │ │ │ │ ├── HelloApp.png │ │ │ │ ├── HelloApp2.png │ │ │ │ ├── logos │ │ │ │ │ ├── xmlui1.svg │ │ │ │ │ ├── xmlui2.svg │ │ │ │ │ ├── xmlui3.svg │ │ │ │ │ ├── xmlui4.svg │ │ │ │ │ ├── xmlui5.svg │ │ │ │ │ ├── xmlui6.svg │ │ │ │ │ └── xmlui7.svg │ │ │ │ ├── pdf │ │ │ │ │ └── dummy-pdf.jpg │ │ │ │ ├── rendering-engine │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ ├── Component.svg │ │ │ │ │ ├── CompoundComponent.svg │ │ │ │ │ ├── RootComponent.svg │ │ │ │ │ └── tree-with-containers.svg │ │ │ │ ├── reviewers-guide │ │ │ │ │ ├── AppEngine-flow.svg │ │ │ │ │ └── incbutton-in-action.png │ │ │ │ ├── tools │ │ │ │ │ └── boilerplate-structure.png │ │ │ │ ├── try.svg │ │ │ │ ├── tutorial │ │ │ │ │ ├── app-chat-history.png │ │ │ │ │ ├── app-content-placeholder.png │ │ │ │ │ ├── app-header-and-content.png │ │ │ │ │ ├── app-links-channel-selected.png │ │ │ │ │ ├── app-links-click.png │ │ │ │ │ ├── app-navigation.png │ │ │ │ │ ├── finished-ex01.png │ │ │ │ │ ├── finished-ex02.png │ │ │ │ │ ├── hello.png │ │ │ │ │ ├── splash-screen-advanced.png │ │ │ │ │ ├── splash-screen-after-click.png │ │ │ │ │ ├── splash-screen-centered.png │ │ │ │ │ ├── splash-screen-events.png │ │ │ │ │ ├── splash-screen-expression.png │ │ │ │ │ ├── splash-screen-reuse-after.png │ │ │ │ │ ├── splash-screen-reuse-before.png │ │ │ │ │ └── splash-screen.png │ │ │ │ └── tutorial-01.png │ │ │ ├── llms.txt │ │ │ ├── logo-dark.svg │ │ │ ├── logo.svg │ │ │ ├── pg-popout.svg │ │ │ └── xmlui-logo.svg │ │ ├── serve.json │ │ └── web.config │ ├── scripts │ │ ├── download-latest-xmlui.js │ │ ├── generate-rss.js │ │ ├── get-releases.js │ │ └── utils.js │ ├── src │ │ ├── components │ │ │ ├── BlogOverview.xmlui │ │ │ ├── BlogPage.xmlui │ │ │ ├── Boxes.xmlui │ │ │ ├── Breadcrumb.xmlui │ │ │ ├── ChangeLog.xmlui │ │ │ ├── ColorPalette.xmlui │ │ │ ├── DocumentLinks.xmlui │ │ │ ├── DocumentPage.xmlui │ │ │ ├── DocumentPageNoTOC.xmlui │ │ │ ├── Icons.xmlui │ │ │ ├── IncButton.xmlui │ │ │ ├── IncButton2.xmlui │ │ │ ├── NameValue.xmlui │ │ │ ├── PageNotFound.xmlui │ │ │ ├── PaletteItem.xmlui │ │ │ ├── Palettes.xmlui │ │ │ ├── SectionHeader.xmlui │ │ │ ├── TBD.xmlui │ │ │ ├── Test.xmlui │ │ │ ├── ThemesIntro.xmlui │ │ │ ├── ThousandThemes.xmlui │ │ │ ├── TubeStops.xmlui │ │ │ ├── TubeStops.xmlui.xs │ │ │ └── TwoColumnCode.xmlui │ │ ├── config.ts │ │ ├── Main.xmlui │ │ └── themes │ │ ├── docs-theme.ts │ │ ├── earthtone.ts │ │ ├── xmlui-gray-on-default.ts │ │ ├── xmlui-green-on-default.ts │ │ └── xmlui-orange-on-default.ts │ └── tsconfig.json ├── LICENSE ├── package-lock.json ├── package.json ├── packages │ ├── xmlui-animations │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Animation.tsx │ │ │ ├── AnimationNative.tsx │ │ │ ├── FadeAnimation.tsx │ │ │ ├── FadeInAnimation.tsx │ │ │ ├── FadeOutAnimation.tsx │ │ │ ├── index.tsx │ │ │ ├── ScaleAnimation.tsx │ │ │ └── SlideInAnimation.tsx │ │ └── tsconfig.json │ ├── xmlui-devtools │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── devtools │ │ │ │ ├── DevTools.tsx │ │ │ │ ├── DevToolsNative.module.scss │ │ │ │ ├── DevToolsNative.tsx │ │ │ │ ├── ModalDialog.module.scss │ │ │ │ ├── ModalDialog.tsx │ │ │ │ ├── ModalVisibilityContext.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── editor │ │ │ │ └── Editor.tsx │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── vite.config-overrides.ts │ ├── xmlui-hello-world │ │ ├── .gitignore │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── HelloWorld.module.scss │ │ │ ├── HelloWorld.tsx │ │ │ ├── HelloWorldNative.tsx │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── xmlui-os-frames │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── IPhoneFrame.module.scss │ │ │ ├── IPhoneFrame.tsx │ │ │ ├── MacOSAppFrame.module.scss │ │ │ ├── MacOSAppFrame.tsx │ │ │ ├── WindowsAppFrame.module.scss │ │ │ └── WindowsAppFrame.tsx │ │ └── tsconfig.json │ ├── xmlui-pdf │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ ├── components │ │ │ │ └── Pdf.xmlui │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── LazyPdfNative.tsx │ │ │ ├── Pdf.module.scss │ │ │ └── Pdf.tsx │ │ └── tsconfig.json │ ├── xmlui-playground │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── usePlayground.ts │ │ │ │ └── useToast.ts │ │ │ ├── index.tsx │ │ │ ├── playground │ │ │ │ ├── Box.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── CodeSelector.tsx │ │ │ │ ├── ConfirmationDialog.module.scss │ │ │ │ ├── ConfirmationDialog.tsx │ │ │ │ ├── Editor.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundContent.module.scss │ │ │ │ ├── PlaygroundContent.tsx │ │ │ │ ├── PlaygroundNative.module.scss │ │ │ │ ├── PlaygroundNative.tsx │ │ │ │ ├── Preview.module.scss │ │ │ │ ├── Preview.tsx │ │ │ │ ├── Select.module.scss │ │ │ │ ├── StandalonePlayground.tsx │ │ │ │ ├── StandalonePlaygroundNative.module.scss │ │ │ │ ├── StandalonePlaygroundNative.tsx │ │ │ │ ├── ThemeSwitcher.module.scss │ │ │ │ ├── ThemeSwitcher.tsx │ │ │ │ ├── ToneSwitcher.tsx │ │ │ │ ├── Tooltip.module.scss │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── utils.ts │ │ │ ├── providers │ │ │ │ ├── Toast.module.scss │ │ │ │ └── ToastProvider.tsx │ │ │ ├── state │ │ │ │ └── store.ts │ │ │ ├── themes │ │ │ │ └── theme.ts │ │ │ └── utils │ │ │ └── helpers.ts │ │ └── tsconfig.json │ ├── xmlui-search │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Search.module.scss │ │ │ └── Search.tsx │ │ └── tsconfig.json │ ├── xmlui-spreadsheet │ │ ├── .gitignore │ │ ├── demo │ │ │ └── Main.xmlui │ │ ├── index.html │ │ ├── index.ts │ │ ├── meta │ │ │ └── componentsMetadata.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── Spreadsheet.tsx │ │ │ └── SpreadsheetNative.tsx │ │ └── tsconfig.json │ └── xmlui-website-blocks │ ├── .gitignore │ ├── CHANGELOG.md │ ├── demo │ │ ├── components │ │ │ ├── HeroBackgroundBreakoutPage.xmlui │ │ │ ├── HeroBackgroundsPage.xmlui │ │ │ ├── HeroContentsPage.xmlui │ │ │ ├── HeroTextAlignPage.xmlui │ │ │ ├── HeroTextPage.xmlui │ │ │ └── HeroTonesPage.xmlui │ │ ├── Main.xmlui │ │ └── themes │ │ └── default.ts │ ├── index.html │ ├── index.ts │ ├── meta │ │ └── componentsMetadata.ts │ ├── package.json │ ├── public │ │ └── resources │ │ ├── building.jpg │ │ └── xmlui-logo.svg │ ├── src │ │ ├── Carousel │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ └── CarouselNative.tsx │ │ ├── FancyButton │ │ │ ├── FancyButton.module.scss │ │ │ ├── FancyButton.tsx │ │ │ └── FancyButton.xmlui │ │ ├── Hello │ │ │ ├── Hello.tsx │ │ │ ├── Hello.xmlui │ │ │ └── Hello.xmlui.xs │ │ ├── HeroSection │ │ │ ├── HeroSection.module.scss │ │ │ ├── HeroSection.tsx │ │ │ └── HeroSectionNative.tsx │ │ ├── index.tsx │ │ ├── ScrollToTop │ │ │ ├── ScrollToTop.module.scss │ │ │ ├── ScrollToTop.tsx │ │ │ └── ScrollToTopNative.tsx │ │ └── vite-env.d.ts │ └── tsconfig.json ├── README.md ├── tools │ ├── codefence │ │ └── xmlui-code-fence-docs.md │ ├── create-app │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── create-app.ts │ │ ├── helpers │ │ │ ├── copy.ts │ │ │ ├── get-pkg-manager.ts │ │ │ ├── git.ts │ │ │ ├── install.ts │ │ │ ├── is-folder-empty.ts │ │ │ ├── is-writeable.ts │ │ │ ├── make-dir.ts │ │ │ └── validate-pkg.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── templates │ │ │ ├── default │ │ │ │ └── ts │ │ │ │ ├── gitignore │ │ │ │ ├── index.html │ │ │ │ ├── index.ts │ │ │ │ ├── public │ │ │ │ │ ├── mockServiceWorker.js │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ └── xmlui-logo.svg │ │ │ │ │ └── serve.json │ │ │ │ └── src │ │ │ │ ├── components │ │ │ │ │ ├── ApiAware.xmlui │ │ │ │ │ ├── Home.xmlui │ │ │ │ │ ├── IncButton.xmlui │ │ │ │ │ └── PagePanel.xmlui │ │ │ │ ├── config.ts │ │ │ │ └── Main.xmlui │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── create-xmlui-hello-world │ │ ├── index.js │ │ └── package.json │ └── vscode │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── build.sh │ ├── CHANGELOG.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── formatter-docs.md │ ├── generate-test-sample.sh │ ├── LICENSE.md │ ├── package-lock.json │ ├── package.json │ ├── README.md │ ├── resources │ │ ├── xmlui-logo.png │ │ └── xmlui-markup-syntax-highlighting.png │ ├── src │ │ ├── extension.ts │ │ └── server.ts │ ├── syntaxes │ │ └── xmlui.tmLanguage.json │ ├── test-samples │ │ └── sample.xmlui │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── turbo.json └── xmlui ├── .gitignore ├── bin │ ├── bootstrap.js │ ├── build-lib.ts │ ├── build.ts │ ├── index.ts │ ├── preview.ts │ ├── start.ts │ ├── vite-xmlui-plugin.ts │ └── viteConfig.ts ├── CHANGELOG.md ├── conventions │ ├── component-qa-checklist.md │ ├── copilot-conventions.md │ ├── create-xmlui-components.md │ ├── mermaid.md │ ├── testing-conventions.md │ └── xmlui-in-a-nutshell.md ├── dev-docs │ ├── accessibility.md │ ├── build-system.md │ ├── build-xmlui.md │ ├── component-behaviors.md │ ├── containers.md │ ├── glossary.md │ ├── index.md │ ├── next │ │ ├── component-dev-guide.md │ │ ├── configuration-management-enhancement-summary.md │ │ ├── documentation-scripts-refactoring-complete-summary.md │ │ ├── documentation-scripts-refactoring-plan.md │ │ ├── duplicate-pattern-extraction-summary.md │ │ ├── error-handling-standardization-summary.md │ │ ├── generating-component-reference.md │ │ ├── index.md │ │ ├── logging-consistency-implementation-summary.md │ │ ├── project-build.md │ │ ├── project-structure.md │ │ ├── theme-context.md │ │ ├── tiptap-design-considerations.md │ │ ├── working-with-code.md │ │ ├── xmlui-runtime-architecture │ │ └── xmlui-wcag-accessibility-report.md │ ├── react-fundamentals.md │ ├── release-method.md │ ├── standalone-app.md │ ├── state-management.md │ └── xmlui-repo.md ├── package.json ├── playwright.config.ts ├── scripts │ ├── coverage-only.js │ ├── e2e-test-summary.js │ ├── generate-docs │ │ ├── build-downloads-map.mjs │ │ ├── build-pages-map.mjs │ │ ├── components-config.json │ │ ├── configuration-management.mjs │ │ ├── constants.mjs │ │ ├── create-theme-files.mjs │ │ ├── DocsGenerator.mjs │ │ ├── error-handling.mjs │ │ ├── extensions-config.json │ │ ├── folders.mjs │ │ ├── generate-summary-files.mjs │ │ ├── get-docs.mjs │ │ ├── input-handler.mjs │ │ ├── logger.mjs │ │ ├── logging-standards.mjs │ │ ├── MetadataProcessor.mjs │ │ ├── pattern-utilities.mjs │ │ └── utils.mjs │ ├── get-langserver-metadata.mjs │ ├── inline-links.mjs │ └── README-e2e-summary.md ├── src │ ├── abstractions │ │ ├── _conventions.md │ │ ├── ActionDefs.ts │ │ ├── AppContextDefs.ts │ │ ├── ComponentDefs.ts │ │ ├── ContainerDefs.ts │ │ ├── ExtensionDefs.ts │ │ ├── FunctionDefs.ts │ │ ├── RendererDefs.ts │ │ ├── scripting │ │ │ ├── BlockScope.ts │ │ │ ├── Compilation.ts │ │ │ ├── LogicalThread.ts │ │ │ ├── LoopScope.ts │ │ │ ├── modules.ts │ │ │ ├── ScriptParserError.ts │ │ │ ├── Token.ts │ │ │ ├── TryScope.ts │ │ │ └── TryScopeExp.ts │ │ └── ThemingDefs.ts │ ├── components │ │ ├── _conventions.md │ │ ├── abstractions.ts │ │ ├── Accordion │ │ │ ├── Accordion.md │ │ │ ├── Accordion.module.scss │ │ │ ├── Accordion.spec.ts │ │ │ ├── Accordion.tsx │ │ │ ├── AccordionContext.tsx │ │ │ ├── AccordionItem.tsx │ │ │ ├── AccordionItemNative.tsx │ │ │ └── AccordionNative.tsx │ │ ├── Animation │ │ │ └── AnimationNative.tsx │ │ ├── APICall │ │ │ ├── APICall.md │ │ │ ├── APICall.spec.ts │ │ │ ├── APICall.tsx │ │ │ └── APICallNative.tsx │ │ ├── App │ │ │ ├── App.md │ │ │ ├── App.module.scss │ │ │ ├── App.spec.ts │ │ │ ├── App.tsx │ │ │ ├── AppLayoutContext.ts │ │ │ ├── AppNative.tsx │ │ │ ├── AppStateContext.ts │ │ │ ├── doc-resources │ │ │ │ ├── condensed-sticky.xmlui │ │ │ │ ├── condensed.xmlui │ │ │ │ ├── horizontal-sticky.xmlui │ │ │ │ ├── horizontal.xmlui │ │ │ │ ├── vertical-full-header.xmlui │ │ │ │ ├── vertical-sticky.xmlui │ │ │ │ └── vertical.xmlui │ │ │ ├── IndexerContext.ts │ │ │ ├── LinkInfoContext.ts │ │ │ ├── SearchContext.tsx │ │ │ ├── Sheet.module.scss │ │ │ └── Sheet.tsx │ │ ├── AppHeader │ │ │ ├── AppHeader.md │ │ │ ├── AppHeader.module.scss │ │ │ ├── AppHeader.spec.ts │ │ │ ├── AppHeader.tsx │ │ │ └── AppHeaderNative.tsx │ │ ├── AppState │ │ │ ├── AppState.md │ │ │ ├── AppState.spec.ts │ │ │ ├── AppState.tsx │ │ │ └── AppStateNative.tsx │ │ ├── AutoComplete │ │ │ ├── AutoComplete.md │ │ │ ├── AutoComplete.module.scss │ │ │ ├── AutoComplete.spec.ts │ │ │ ├── AutoComplete.tsx │ │ │ ├── AutoCompleteContext.tsx │ │ │ └── AutoCompleteNative.tsx │ │ ├── Avatar │ │ │ ├── Avatar.md │ │ │ ├── Avatar.module.scss │ │ │ ├── Avatar.spec.ts │ │ │ ├── Avatar.tsx │ │ │ └── AvatarNative.tsx │ │ ├── Backdrop │ │ │ ├── Backdrop.md │ │ │ ├── Backdrop.module.scss │ │ │ ├── Backdrop.spec.ts │ │ │ ├── Backdrop.tsx │ │ │ └── BackdropNative.tsx │ │ ├── Badge │ │ │ ├── Badge.md │ │ │ ├── Badge.module.scss │ │ │ ├── Badge.spec.ts │ │ │ ├── Badge.tsx │ │ │ └── BadgeNative.tsx │ │ ├── Bookmark │ │ │ ├── Bookmark.md │ │ │ ├── Bookmark.module.scss │ │ │ ├── Bookmark.spec.ts │ │ │ ├── Bookmark.tsx │ │ │ └── BookmarkNative.tsx │ │ ├── Breakout │ │ │ ├── Breakout.module.scss │ │ │ ├── Breakout.spec.ts │ │ │ ├── Breakout.tsx │ │ │ └── BreakoutNative.tsx │ │ ├── Button │ │ │ ├── Button-style.spec.ts │ │ │ ├── Button.md │ │ │ ├── Button.module.scss │ │ │ ├── Button.spec.ts │ │ │ ├── Button.tsx │ │ │ └── ButtonNative.tsx │ │ ├── Card │ │ │ ├── Card.md │ │ │ ├── Card.module.scss │ │ │ ├── Card.spec.ts │ │ │ ├── Card.tsx │ │ │ └── CardNative.tsx │ │ ├── Carousel │ │ │ ├── Carousel.md │ │ │ ├── Carousel.module.scss │ │ │ ├── Carousel.spec.ts │ │ │ ├── Carousel.tsx │ │ │ ├── CarouselContext.tsx │ │ │ ├── CarouselItem.tsx │ │ │ ├── CarouselItemNative.tsx │ │ │ └── CarouselNative.tsx │ │ ├── ChangeListener │ │ │ ├── ChangeListener.md │ │ │ ├── ChangeListener.spec.ts │ │ │ ├── ChangeListener.tsx │ │ │ └── ChangeListenerNative.tsx │ │ ├── chart-color-schemes.ts │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── AreaChart.md │ │ │ │ ├── AreaChart.spec.ts │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── AreaChartNative.tsx │ │ │ ├── BarChart │ │ │ │ ├── BarChart.md │ │ │ │ ├── BarChart.module.scss │ │ │ │ ├── BarChart.spec.ts │ │ │ │ ├── BarChart.tsx │ │ │ │ └── BarChartNative.tsx │ │ │ ├── DonutChart │ │ │ │ ├── DonutChart.spec.ts │ │ │ │ └── DonutChart.tsx │ │ │ ├── LabelList │ │ │ │ ├── LabelList.spec.ts │ │ │ │ ├── LabelList.tsx │ │ │ │ ├── LabelListNative.module.scss │ │ │ │ └── LabelListNative.tsx │ │ │ ├── Legend │ │ │ │ ├── Legend.spec.ts │ │ │ │ ├── Legend.tsx │ │ │ │ └── LegendNative.tsx │ │ │ ├── LineChart │ │ │ │ ├── LineChart.md │ │ │ │ ├── LineChart.module.scss │ │ │ │ ├── LineChart.spec.ts │ │ │ │ ├── LineChart.tsx │ │ │ │ └── LineChartNative.tsx │ │ │ ├── PieChart │ │ │ │ ├── PieChart.md │ │ │ │ ├── PieChart.spec.ts │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── PieChartNative.module.scss │ │ │ │ └── PieChartNative.tsx │ │ │ ├── RadarChart │ │ │ │ ├── RadarChart.md │ │ │ │ ├── RadarChart.spec.ts │ │ │ │ ├── RadarChart.tsx │ │ │ │ └── RadarChartNative.tsx │ │ │ ├── Tooltip │ │ │ │ ├── TooltipContent.module.scss │ │ │ │ ├── TooltipContent.spec.ts │ │ │ │ └── TooltipContent.tsx │ │ │ └── utils │ │ │ ├── abstractions.ts │ │ │ └── ChartProvider.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.md │ │ │ ├── Checkbox.spec.ts │ │ │ └── Checkbox.tsx │ │ ├── CodeBlock │ │ │ ├── CodeBlock.module.scss │ │ │ ├── CodeBlock.spec.ts │ │ │ ├── CodeBlock.tsx │ │ │ ├── CodeBlockNative.tsx │ │ │ └── highlight-code.ts │ │ ├── collectedComponentMetadata.ts │ │ ├── ColorPicker │ │ │ ├── ColorPicker.md │ │ │ ├── ColorPicker.module.scss │ │ │ ├── ColorPicker.spec.ts │ │ │ ├── ColorPicker.tsx │ │ │ └── ColorPickerNative.tsx │ │ ├── Column │ │ │ ├── Column.md │ │ │ ├── Column.tsx │ │ │ ├── ColumnNative.tsx │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ └── TableContext.tsx │ │ ├── component-utils.ts │ │ ├── ComponentProvider.tsx │ │ ├── ComponentRegistryContext.tsx │ │ ├── container-helpers.tsx │ │ ├── ContentSeparator │ │ │ ├── ContentSeparator.md │ │ │ ├── ContentSeparator.module.scss │ │ │ ├── ContentSeparator.spec.ts │ │ │ ├── ContentSeparator.tsx │ │ │ └── ContentSeparatorNative.tsx │ │ ├── DataSource │ │ │ ├── DataSource.md │ │ │ └── DataSource.tsx │ │ ├── DateInput │ │ │ ├── DateInput.md │ │ │ ├── DateInput.module.scss │ │ │ ├── DateInput.spec.ts │ │ │ ├── DateInput.tsx │ │ │ └── DateInputNative.tsx │ │ ├── DatePicker │ │ │ ├── DatePicker.md │ │ │ ├── DatePicker.module.scss │ │ │ ├── DatePicker.spec.ts │ │ │ ├── DatePicker.tsx │ │ │ └── DatePickerNative.tsx │ │ ├── DropdownMenu │ │ │ ├── DropdownMenu.md │ │ │ ├── DropdownMenu.module.scss │ │ │ ├── DropdownMenu.spec.ts │ │ │ ├── DropdownMenu.tsx │ │ │ ├── DropdownMenuNative.tsx │ │ │ ├── MenuItem.md │ │ │ └── SubMenuItem.md │ │ ├── EmojiSelector │ │ │ ├── EmojiSelector.md │ │ │ ├── EmojiSelector.spec.ts │ │ │ ├── EmojiSelector.tsx │ │ │ └── EmojiSelectorNative.tsx │ │ ├── ExpandableItem │ │ │ ├── ExpandableItem.module.scss │ │ │ ├── ExpandableItem.spec.ts │ │ │ ├── ExpandableItem.tsx │ │ │ └── ExpandableItemNative.tsx │ │ ├── FileInput │ │ │ ├── FileInput.md │ │ │ ├── FileInput.module.scss │ │ │ ├── FileInput.spec.ts │ │ │ ├── FileInput.tsx │ │ │ └── FileInputNative.tsx │ │ ├── FileUploadDropZone │ │ │ ├── FileUploadDropZone.md │ │ │ ├── FileUploadDropZone.module.scss │ │ │ ├── FileUploadDropZone.spec.ts │ │ │ ├── FileUploadDropZone.tsx │ │ │ └── FileUploadDropZoneNative.tsx │ │ ├── FlowLayout │ │ │ ├── FlowLayout.md │ │ │ ├── FlowLayout.module.scss │ │ │ ├── FlowLayout.spec.ts │ │ │ ├── FlowLayout.spec.ts-snapshots │ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png │ │ │ ├── FlowLayout.tsx │ │ │ └── FlowLayoutNative.tsx │ │ ├── Footer │ │ │ ├── Footer.md │ │ │ ├── Footer.module.scss │ │ │ ├── Footer.spec.ts │ │ │ ├── Footer.tsx │ │ │ └── FooterNative.tsx │ │ ├── Form │ │ │ ├── Form.md │ │ │ ├── Form.module.scss │ │ │ ├── Form.spec.ts │ │ │ ├── Form.tsx │ │ │ ├── formActions.ts │ │ │ ├── FormContext.ts │ │ │ └── FormNative.tsx │ │ ├── FormItem │ │ │ ├── FormItem.md │ │ │ ├── FormItem.module.scss │ │ │ ├── FormItem.spec.ts │ │ │ ├── FormItem.tsx │ │ │ ├── FormItemNative.tsx │ │ │ ├── HelperText.module.scss │ │ │ ├── HelperText.tsx │ │ │ ├── ItemWithLabel.tsx │ │ │ └── Validations.ts │ │ ├── FormSection │ │ │ ├── FormSection.md │ │ │ ├── FormSection.ts │ │ │ └── FormSection.xmlui │ │ ├── Fragment │ │ │ ├── Fragment.spec.ts │ │ │ └── Fragment.tsx │ │ ├── Heading │ │ │ ├── abstractions.ts │ │ │ ├── H1.md │ │ │ ├── H1.spec.ts │ │ │ ├── H2.md │ │ │ ├── H2.spec.ts │ │ │ ├── H3.md │ │ │ ├── H3.spec.ts │ │ │ ├── H4.md │ │ │ ├── H4.spec.ts │ │ │ ├── H5.md │ │ │ ├── H5.spec.ts │ │ │ ├── H6.md │ │ │ ├── H6.spec.ts │ │ │ ├── Heading.md │ │ │ ├── Heading.module.scss │ │ │ ├── Heading.spec.ts │ │ │ ├── Heading.tsx │ │ │ └── HeadingNative.tsx │ │ ├── HoverCard │ │ │ ├── HoverCard.tsx │ │ │ └── HovercardNative.tsx │ │ ├── HtmlTags │ │ │ ├── HtmlTags.module.scss │ │ │ ├── HtmlTags.spec.ts │ │ │ └── HtmlTags.tsx │ │ ├── Icon │ │ │ ├── AdmonitionDanger.tsx │ │ │ ├── AdmonitionInfo.tsx │ │ │ ├── AdmonitionNote.tsx │ │ │ ├── AdmonitionTip.tsx │ │ │ ├── AdmonitionWarning.tsx │ │ │ ├── ApiIcon.tsx │ │ │ ├── ArrowDropDown.module.scss │ │ │ ├── ArrowDropDown.tsx │ │ │ ├── ArrowDropUp.module.scss │ │ │ ├── ArrowDropUp.tsx │ │ │ ├── ArrowLeft.module.scss │ │ │ ├── ArrowLeft.tsx │ │ │ ├── ArrowRight.module.scss │ │ │ ├── ArrowRight.tsx │ │ │ ├── Attach.tsx │ │ │ ├── Binding.module.scss │ │ │ ├── Binding.tsx │ │ │ ├── BoardIcon.tsx │ │ │ ├── BoxIcon.tsx │ │ │ ├── CheckIcon.tsx │ │ │ ├── ChevronDownIcon.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── ChevronUpIcon.tsx │ │ │ ├── CodeFileIcon.tsx │ │ │ ├── CodeSandbox.tsx │ │ │ ├── CompactListIcon.tsx │ │ │ ├── ContentCopyIcon.tsx │ │ │ ├── DarkToLightIcon.tsx │ │ │ ├── DatabaseIcon.module.scss │ │ │ ├── DatabaseIcon.tsx │ │ │ ├── DocFileIcon.tsx │ │ │ ├── DocIcon.tsx │ │ │ ├── DotMenuHorizontalIcon.tsx │ │ │ ├── DotMenuIcon.tsx │ │ │ ├── EmailIcon.tsx │ │ │ ├── EmptyFolderIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── ExpressionIcon.tsx │ │ │ ├── FillPlusCricleIcon.tsx │ │ │ ├── FilterIcon.tsx │ │ │ ├── FolderIcon.tsx │ │ │ ├── GlobeIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── HyperLinkIcon.tsx │ │ │ ├── Icon.md │ │ │ ├── Icon.module.scss │ │ │ ├── Icon.spec.ts │ │ │ ├── Icon.tsx │ │ │ ├── IconNative.tsx │ │ │ ├── ImageFileIcon.tsx │ │ │ ├── Inspect.tsx │ │ │ ├── LightToDark.tsx │ │ │ ├── LinkIcon.tsx │ │ │ ├── ListIcon.tsx │ │ │ ├── LooseListIcon.tsx │ │ │ ├── MoonIcon.tsx │ │ │ ├── MoreOptionsIcon.tsx │ │ │ ├── NoSortIcon.tsx │ │ │ ├── PDFIcon.tsx │ │ │ ├── PenIcon.tsx │ │ │ ├── PhoneIcon.tsx │ │ │ ├── PhotoIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShareIcon.tsx │ │ │ ├── SortAscendingIcon.tsx │ │ │ ├── SortDescendingIcon.tsx │ │ │ ├── StarsIcon.tsx │ │ │ ├── SunIcon.tsx │ │ │ ├── svg │ │ │ │ ├── admonition_danger.svg │ │ │ │ ├── admonition_info.svg │ │ │ │ ├── admonition_note.svg │ │ │ │ ├── admonition_tip.svg │ │ │ │ ├── admonition_warning.svg │ │ │ │ ├── api.svg │ │ │ │ ├── arrow-dropdown.svg │ │ │ │ ├── arrow-left.svg │ │ │ │ ├── arrow-right.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ ├── attach.svg │ │ │ │ ├── binding.svg │ │ │ │ ├── box.svg │ │ │ │ ├── bulb.svg │ │ │ │ ├── code-file.svg │ │ │ │ ├── code-sandbox.svg │ │ │ │ ├── dark_to_light.svg │ │ │ │ ├── database.svg │ │ │ │ ├── doc.svg │ │ │ │ ├── empty-folder.svg │ │ │ │ ├── expression.svg │ │ │ │ ├── eye-closed.svg │ │ │ │ ├── eye-dark.svg │ │ │ │ ├── eye.svg │ │ │ │ ├── file-text.svg │ │ │ │ ├── filter.svg │ │ │ │ ├── folder.svg │ │ │ │ ├── img.svg │ │ │ │ ├── inspect.svg │ │ │ │ ├── light_to_dark.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── pdf.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── share.svg │ │ │ │ ├── stars.svg │ │ │ │ ├── sun.svg │ │ │ │ ├── trending-down.svg │ │ │ │ ├── trending-level.svg │ │ │ │ ├── trending-up.svg │ │ │ │ ├── txt.svg │ │ │ │ ├── unknown-file.svg │ │ │ │ ├── unlink.svg │ │ │ │ └── xls.svg │ │ │ ├── TableDeleteColumnIcon.tsx │ │ │ ├── TableDeleteRowIcon.tsx │ │ │ ├── TableInsertColumnIcon.tsx │ │ │ ├── TableInsertRowIcon.tsx │ │ │ ├── TrashIcon.tsx │ │ │ ├── TrendingDownIcon.tsx │ │ │ ├── TrendingLevelIcon.tsx │ │ │ ├── TrendingUpIcon.tsx │ │ │ ├── TxtIcon.tsx │ │ │ ├── UnknownFileIcon.tsx │ │ │ ├── UnlinkIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ ├── WarningIcon.tsx │ │ │ └── XlsIcon.tsx │ │ ├── IconProvider.tsx │ │ ├── IconRegistryContext.tsx │ │ ├── IFrame │ │ │ ├── IFrame.md │ │ │ ├── IFrame.module.scss │ │ │ ├── IFrame.spec.ts │ │ │ ├── IFrame.tsx │ │ │ └── IFrameNative.tsx │ │ ├── Image │ │ │ ├── Image.md │ │ │ ├── Image.module.scss │ │ │ ├── Image.spec.ts │ │ │ ├── Image.tsx │ │ │ └── ImageNative.tsx │ │ ├── Input │ │ │ ├── index.ts │ │ │ ├── InputAdornment.module.scss │ │ │ ├── InputAdornment.tsx │ │ │ ├── InputDivider.module.scss │ │ │ ├── InputDivider.tsx │ │ │ ├── InputLabel.module.scss │ │ │ ├── InputLabel.tsx │ │ │ ├── PartialInput.module.scss │ │ │ └── PartialInput.tsx │ │ ├── InspectButton │ │ │ ├── InspectButton.module.scss │ │ │ └── InspectButton.tsx │ │ ├── Items │ │ │ ├── Items.md │ │ │ ├── Items.spec.ts │ │ │ ├── Items.tsx │ │ │ └── ItemsNative.tsx │ │ ├── Link │ │ │ ├── Link.md │ │ │ ├── Link.module.scss │ │ │ ├── Link.spec.ts │ │ │ ├── Link.tsx │ │ │ └── LinkNative.tsx │ │ ├── List │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── List.md │ │ │ ├── List.module.scss │ │ │ ├── List.spec.ts │ │ │ ├── List.tsx │ │ │ └── ListNative.tsx │ │ ├── Logo │ │ │ ├── doc-resources │ │ │ │ └── xmlui-logo.svg │ │ │ ├── Logo.md │ │ │ ├── Logo.tsx │ │ │ └── LogoNative.tsx │ │ ├── Markdown │ │ │ ├── CodeText.module.scss │ │ │ ├── CodeText.tsx │ │ │ ├── Markdown.md │ │ │ ├── Markdown.module.scss │ │ │ ├── Markdown.spec.ts │ │ │ ├── Markdown.tsx │ │ │ ├── MarkdownNative.tsx │ │ │ ├── parse-binding-expr.ts │ │ │ └── utils.ts │ │ ├── metadata-helpers.ts │ │ ├── ModalDialog │ │ │ ├── ConfirmationModalContextProvider.tsx │ │ │ ├── Dialog.module.scss │ │ │ ├── Dialog.tsx │ │ │ ├── ModalDialog.md │ │ │ ├── ModalDialog.module.scss │ │ │ ├── ModalDialog.spec.ts │ │ │ ├── ModalDialog.tsx │ │ │ ├── ModalDialogNative.tsx │ │ │ └── ModalVisibilityContext.tsx │ │ ├── NavGroup │ │ │ ├── NavGroup.md │ │ │ ├── NavGroup.module.scss │ │ │ ├── NavGroup.spec.ts │ │ │ ├── NavGroup.tsx │ │ │ ├── NavGroupContext.ts │ │ │ └── NavGroupNative.tsx │ │ ├── NavLink │ │ │ ├── NavLink.md │ │ │ ├── NavLink.module.scss │ │ │ ├── NavLink.spec.ts │ │ │ ├── NavLink.tsx │ │ │ └── NavLinkNative.tsx │ │ ├── NavPanel │ │ │ ├── NavPanel.md │ │ │ ├── NavPanel.module.scss │ │ │ ├── NavPanel.spec.ts │ │ │ ├── NavPanel.tsx │ │ │ └── NavPanelNative.tsx │ │ ├── NestedApp │ │ │ ├── AppWithCodeView.module.scss │ │ │ ├── AppWithCodeView.tsx │ │ │ ├── AppWithCodeViewNative.tsx │ │ │ ├── defaultProps.tsx │ │ │ ├── logo.svg │ │ │ ├── NestedApp.module.scss │ │ │ ├── NestedApp.tsx │ │ │ ├── NestedAppNative.tsx │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.tsx │ │ │ └── utils.ts │ │ ├── NoResult │ │ │ ├── NoResult.md │ │ │ ├── NoResult.module.scss │ │ │ ├── NoResult.spec.ts │ │ │ ├── NoResult.tsx │ │ │ └── NoResultNative.tsx │ │ ├── NumberBox │ │ │ ├── numberbox-abstractions.ts │ │ │ ├── NumberBox.md │ │ │ ├── NumberBox.module.scss │ │ │ ├── NumberBox.spec.ts │ │ │ ├── NumberBox.tsx │ │ │ └── NumberBoxNative.tsx │ │ ├── Option │ │ │ ├── Option.md │ │ │ ├── Option.spec.ts │ │ │ ├── Option.tsx │ │ │ ├── OptionNative.tsx │ │ │ └── OptionTypeProvider.tsx │ │ ├── PageMetaTitle │ │ │ ├── PageMetaTilteNative.tsx │ │ │ ├── PageMetaTitle.md │ │ │ ├── PageMetaTitle.spec.ts │ │ │ └── PageMetaTitle.tsx │ │ ├── Pages │ │ │ ├── Page.md │ │ │ ├── Pages.md │ │ │ ├── Pages.module.scss │ │ │ ├── Pages.tsx │ │ │ └── PagesNative.tsx │ │ ├── Pagination │ │ │ ├── Pagination.md │ │ │ ├── Pagination.module.scss │ │ │ ├── Pagination.spec.ts │ │ │ ├── Pagination.tsx │ │ │ └── PaginationNative.tsx │ │ ├── PositionedContainer │ │ │ ├── PositionedContainer.module.scss │ │ │ ├── PositionedContainer.tsx │ │ │ └── PositionedContainerNative.tsx │ │ ├── ProfileMenu │ │ │ ├── ProfileMenu.module.scss │ │ │ └── ProfileMenu.tsx │ │ ├── ProgressBar │ │ │ ├── ProgressBar.md │ │ │ ├── ProgressBar.module.scss │ │ │ ├── ProgressBar.spec.ts │ │ │ ├── ProgressBar.tsx │ │ │ └── ProgressBarNative.tsx │ │ ├── Queue │ │ │ ├── Queue.md │ │ │ ├── Queue.spec.ts │ │ │ ├── Queue.tsx │ │ │ ├── queueActions.ts │ │ │ └── QueueNative.tsx │ │ ├── RadioGroup │ │ │ ├── RadioGroup.md │ │ │ ├── RadioGroup.module.scss │ │ │ ├── RadioGroup.spec.ts │ │ │ ├── RadioGroup.tsx │ │ │ ├── RadioGroupNative.tsx │ │ │ ├── RadioItem.tsx │ │ │ └── RadioItemNative.tsx │ │ ├── RealTimeAdapter │ │ │ ├── RealTimeAdapter.tsx │ │ │ └── RealTimeAdapterNative.tsx │ │ ├── Redirect │ │ │ ├── Redirect.md │ │ │ ├── Redirect.spec.ts │ │ │ └── Redirect.tsx │ │ ├── ResponsiveBar │ │ │ ├── README.md │ │ │ ├── ResponsiveBar.md │ │ │ ├── ResponsiveBar.module.scss │ │ │ ├── ResponsiveBar.spec.ts │ │ │ ├── ResponsiveBar.tsx │ │ │ └── ResponsiveBarNative.tsx │ │ ├── Select │ │ │ ├── HiddenOption.tsx │ │ │ ├── MultiSelectOption.tsx │ │ │ ├── OptionContext.ts │ │ │ ├── Select.md │ │ │ ├── Select.module.scss │ │ │ ├── Select.spec.ts │ │ │ ├── Select.tsx │ │ │ ├── SelectContext.tsx │ │ │ ├── SelectNative.tsx │ │ │ ├── SelectOption.tsx │ │ │ └── SimpleSelect.tsx │ │ ├── SelectionStore │ │ │ ├── SelectionStore.md │ │ │ ├── SelectionStore.tsx │ │ │ └── SelectionStoreNative.tsx │ │ ├── Slider │ │ │ ├── Slider.md │ │ │ ├── Slider.module.scss │ │ │ ├── Slider.spec.ts │ │ │ ├── Slider.tsx │ │ │ └── SliderNative.tsx │ │ ├── Slot │ │ │ ├── Slot.md │ │ │ ├── Slot.spec.ts │ │ │ └── Slot.ts │ │ ├── SlotItem.tsx │ │ ├── SpaceFiller │ │ │ ├── SpaceFiller.md │ │ │ ├── SpaceFiller.module.scss │ │ │ ├── SpaceFiller.spec.ts │ │ │ ├── SpaceFiller.tsx │ │ │ └── SpaceFillerNative.tsx │ │ ├── Spinner │ │ │ ├── Spinner.md │ │ │ ├── Spinner.module.scss │ │ │ ├── Spinner.spec.ts │ │ │ ├── Spinner.tsx │ │ │ └── SpinnerNative.tsx │ │ ├── Splitter │ │ │ ├── HSplitter.md │ │ │ ├── HSplitter.spec.ts │ │ │ ├── Splitter.md │ │ │ ├── Splitter.module.scss │ │ │ ├── Splitter.spec.ts │ │ │ ├── Splitter.tsx │ │ │ ├── SplitterNative.tsx │ │ │ ├── utils.ts │ │ │ ├── VSplitter.md │ │ │ └── VSplitter.spec.ts │ │ ├── Stack │ │ │ ├── CHStack.md │ │ │ ├── CHStack.spec.ts │ │ │ ├── CVStack.md │ │ │ ├── CVStack.spec.ts │ │ │ ├── HStack.md │ │ │ ├── HStack.spec.ts │ │ │ ├── Stack.md │ │ │ ├── Stack.module.scss │ │ │ ├── Stack.spec.ts │ │ │ ├── Stack.tsx │ │ │ ├── StackNative.tsx │ │ │ ├── VStack.md │ │ │ └── VStack.spec.ts │ │ ├── StickyBox │ │ │ ├── StickyBox.md │ │ │ ├── StickyBox.module.scss │ │ │ ├── StickyBox.tsx │ │ │ └── StickyBoxNative.tsx │ │ ├── Switch │ │ │ ├── Switch.md │ │ │ ├── Switch.spec.ts │ │ │ └── Switch.tsx │ │ ├── Table │ │ │ ├── doc-resources │ │ │ │ └── list-component-data.js │ │ │ ├── react-table-config.d.ts │ │ │ ├── Table.md │ │ │ ├── Table.module.scss │ │ │ ├── Table.spec.ts │ │ │ ├── Table.tsx │ │ │ ├── TableNative.tsx │ │ │ └── useRowSelection.tsx │ │ ├── TableOfContents │ │ │ ├── TableOfContents.module.scss │ │ │ ├── TableOfContents.spec.ts │ │ │ ├── TableOfContents.tsx │ │ │ └── TableOfContentsNative.tsx │ │ ├── Tabs │ │ │ ├── TabContext.tsx │ │ │ ├── TabItem.md │ │ │ ├── TabItem.tsx │ │ │ ├── TabItemNative.tsx │ │ │ ├── Tabs.md │ │ │ ├── Tabs.module.scss │ │ │ ├── Tabs.spec.ts │ │ │ ├── Tabs.tsx │ │ │ └── TabsNative.tsx │ │ ├── Text │ │ │ ├── Text.md │ │ │ ├── Text.module.scss │ │ │ ├── Text.spec.ts │ │ │ ├── Text.tsx │ │ │ └── TextNative.tsx │ │ ├── TextArea │ │ │ ├── TextArea.md │ │ │ ├── TextArea.module.scss │ │ │ ├── TextArea.spec.ts │ │ │ ├── TextArea.tsx │ │ │ ├── TextAreaNative.tsx │ │ │ ├── TextAreaResizable.tsx │ │ │ └── useComposedRef.ts │ │ ├── TextBox │ │ │ ├── TextBox.md │ │ │ ├── TextBox.module.scss │ │ │ ├── TextBox.spec.ts │ │ │ ├── TextBox.tsx │ │ │ └── TextBoxNative.tsx │ │ ├── Theme │ │ │ ├── NotificationToast.tsx │ │ │ ├── Theme.md │ │ │ ├── Theme.module.scss │ │ │ ├── Theme.spec.ts │ │ │ ├── Theme.tsx │ │ │ └── ThemeNative.tsx │ │ ├── TimeInput │ │ │ ├── TimeInput.md │ │ │ ├── TimeInput.module.scss │ │ │ ├── TimeInput.spec.ts │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeInputNative.tsx │ │ │ └── utils.ts │ │ ├── Timer │ │ │ ├── Timer.md │ │ │ ├── Timer.spec.ts │ │ │ ├── Timer.tsx │ │ │ └── TimerNative.tsx │ │ ├── Toggle │ │ │ ├── Toggle.module.scss │ │ │ └── Toggle.tsx │ │ ├── ToneChangerButton │ │ │ ├── ToneChangerButton.md │ │ │ ├── ToneChangerButton.spec.ts │ │ │ └── ToneChangerButton.tsx │ │ ├── ToneSwitch │ │ │ ├── ToneSwitch.md │ │ │ ├── ToneSwitch.module.scss │ │ │ ├── ToneSwitch.spec.ts │ │ │ ├── ToneSwitch.tsx │ │ │ └── ToneSwitchNative.tsx │ │ ├── Tooltip │ │ │ ├── Tooltip.md │ │ │ ├── Tooltip.module.scss │ │ │ ├── Tooltip.spec.ts │ │ │ ├── Tooltip.tsx │ │ │ └── TooltipNative.tsx │ │ ├── Tree │ │ │ ├── testData.ts │ │ │ ├── Tree-dynamic.spec.ts │ │ │ ├── Tree-icons.spec.ts │ │ │ ├── Tree.md │ │ │ ├── Tree.spec.ts │ │ │ ├── TreeComponent.module.scss │ │ │ ├── TreeComponent.tsx │ │ │ └── TreeNative.tsx │ │ ├── TreeDisplay │ │ │ ├── TreeDisplay.md │ │ │ ├── TreeDisplay.module.scss │ │ │ ├── TreeDisplay.tsx │ │ │ └── TreeDisplayNative.tsx │ │ ├── ValidationSummary │ │ │ ├── ValidationSummary.module.scss │ │ │ └── ValidationSummary.tsx │ │ └── VisuallyHidden.tsx │ ├── components-core │ │ ├── abstractions │ │ │ ├── ComponentRenderer.ts │ │ │ ├── LoaderRenderer.ts │ │ │ ├── standalone.ts │ │ │ └── treeAbstractions.ts │ │ ├── action │ │ │ ├── actions.ts │ │ │ ├── APICall.tsx │ │ │ ├── FileDownloadAction.tsx │ │ │ ├── FileUploadAction.tsx │ │ │ ├── NavigateAction.tsx │ │ │ └── TimedAction.tsx │ │ ├── ApiBoundComponent.tsx │ │ ├── appContext │ │ │ ├── date-functions.ts │ │ │ ├── math-function.ts │ │ │ └── misc-utils.ts │ │ ├── AppContext.tsx │ │ ├── behaviors │ │ │ ├── Behavior.tsx │ │ │ └── CoreBehaviors.tsx │ │ ├── component-hooks.ts │ │ ├── ComponentDecorator.tsx │ │ ├── ComponentViewer.tsx │ │ ├── CompoundComponent.tsx │ │ ├── constants.ts │ │ ├── DebugViewProvider.tsx │ │ ├── descriptorHelper.ts │ │ ├── devtools │ │ │ ├── InspectorDialog.module.scss │ │ │ ├── InspectorDialog.tsx │ │ │ └── InspectorDialogVisibilityContext.tsx │ │ ├── EngineError.ts │ │ ├── event-handlers.ts │ │ ├── InspectorButton.module.scss │ │ ├── InspectorContext.tsx │ │ ├── interception │ │ │ ├── abstractions.ts │ │ │ ├── ApiInterceptor.ts │ │ │ ├── ApiInterceptorProvider.tsx │ │ │ ├── apiInterceptorWorker.ts │ │ │ ├── Backend.ts │ │ │ ├── Errors.ts │ │ │ ├── IndexedDb.ts │ │ │ ├── initMock.ts │ │ │ ├── InMemoryDb.ts │ │ │ ├── ReadonlyCollection.ts │ │ │ └── useApiInterceptorContext.tsx │ │ ├── loader │ │ │ ├── ApiLoader.tsx │ │ │ ├── DataLoader.tsx │ │ │ ├── ExternalDataLoader.tsx │ │ │ ├── Loader.tsx │ │ │ ├── MockLoaderRenderer.tsx │ │ │ └── PageableLoader.tsx │ │ ├── LoaderComponent.tsx │ │ ├── markup-check.ts │ │ ├── parts.ts │ │ ├── renderers.ts │ │ ├── rendering │ │ │ ├── AppContent.tsx │ │ │ ├── AppRoot.tsx │ │ │ ├── AppWrapper.tsx │ │ │ ├── buildProxy.ts │ │ │ ├── collectFnVarDeps.ts │ │ │ ├── ComponentAdapter.tsx │ │ │ ├── ComponentWrapper.tsx │ │ │ ├── Container.tsx │ │ │ ├── containers.ts │ │ │ ├── ContainerWrapper.tsx │ │ │ ├── ErrorBoundary.module.scss │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── InvalidComponent.module.scss │ │ │ ├── InvalidComponent.tsx │ │ │ ├── nodeUtils.ts │ │ │ ├── reducer.ts │ │ │ ├── renderChild.tsx │ │ │ ├── StandaloneComponent.tsx │ │ │ ├── StateContainer.tsx │ │ │ ├── UnknownComponent.module.scss │ │ │ ├── UnknownComponent.tsx │ │ │ └── valueExtractor.ts │ │ ├── reportEngineError.ts │ │ ├── RestApiProxy.ts │ │ ├── script-runner │ │ │ ├── asyncProxy.ts │ │ │ ├── AttributeValueParser.ts │ │ │ ├── bannedFunctions.ts │ │ │ ├── BindingTreeEvaluationContext.ts │ │ │ ├── eval-tree-async.ts │ │ │ ├── eval-tree-common.ts │ │ │ ├── eval-tree-sync.ts │ │ │ ├── ParameterParser.ts │ │ │ ├── process-statement-async.ts │ │ │ ├── process-statement-common.ts │ │ │ ├── process-statement-sync.ts │ │ │ ├── ScriptingSourceTree.ts │ │ │ ├── simplify-expression.ts │ │ │ ├── statement-queue.ts │ │ │ └── visitors.ts │ │ ├── StandaloneApp.tsx │ │ ├── StandaloneExtensionManager.ts │ │ ├── TableOfContentsContext.tsx │ │ ├── theming │ │ │ ├── _themes.scss │ │ │ ├── component-layout-resolver.ts │ │ │ ├── extendThemeUtils.ts │ │ │ ├── hvar.ts │ │ │ ├── layout-resolver.ts │ │ │ ├── parse-layout-props.ts │ │ │ ├── StyleContext.tsx │ │ │ ├── StyleRegistry.ts │ │ │ ├── ThemeContext.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ ├── themes │ │ │ │ ├── base-utils.ts │ │ │ │ ├── palette.ts │ │ │ │ ├── root.ts │ │ │ │ ├── solid.ts │ │ │ │ ├── theme-colors.ts │ │ │ │ └── xmlui.ts │ │ │ ├── themeVars.module.scss │ │ │ ├── themeVars.ts │ │ │ ├── transformThemeVars.ts │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── actionUtils.ts │ │ │ ├── audio-utils.ts │ │ │ ├── compound-utils.ts │ │ │ ├── css-utils.ts │ │ │ ├── DataLoaderQueryKeyGenerator.ts │ │ │ ├── date-utils.ts │ │ │ ├── extractParam.ts │ │ │ ├── hooks.tsx │ │ │ ├── LruCache.ts │ │ │ ├── mergeProps.ts │ │ │ ├── misc.ts │ │ │ ├── request-params.ts │ │ │ ├── statementUtils.ts │ │ │ └── treeUtils.ts │ │ └── xmlui-parser.ts │ ├── index-standalone.ts │ ├── index.scss │ ├── index.ts │ ├── language-server │ │ ├── server-common.ts │ │ ├── server-web-worker.ts │ │ ├── server.ts │ │ ├── services │ │ │ ├── common │ │ │ │ ├── docs-generation.ts │ │ │ │ ├── lsp-utils.ts │ │ │ │ ├── metadata-utils.ts │ │ │ │ └── syntax-node-utilities.ts │ │ │ ├── completion.ts │ │ │ ├── diagnostic.ts │ │ │ ├── format.ts │ │ │ └── hover.ts │ │ └── xmlui-metadata-generated.mjs │ ├── logging │ │ ├── LoggerContext.tsx │ │ ├── LoggerInitializer.tsx │ │ ├── LoggerService.ts │ │ └── xmlui.ts │ ├── logo.svg │ ├── parsers │ │ ├── common │ │ │ ├── GenericToken.ts │ │ │ ├── InputStream.ts │ │ │ └── utils.ts │ │ ├── scripting │ │ │ ├── code-behind-collect.ts │ │ │ ├── Lexer.ts │ │ │ ├── modules.ts │ │ │ ├── Parser.ts │ │ │ ├── ParserError.ts │ │ │ ├── ScriptingNodeTypes.ts │ │ │ ├── TokenTrait.ts │ │ │ ├── TokenType.ts │ │ │ └── tree-visitor.ts │ │ ├── style-parser │ │ │ ├── errors.ts │ │ │ ├── source-tree.ts │ │ │ ├── StyleInputStream.ts │ │ │ ├── StyleLexer.ts │ │ │ ├── StyleParser.ts │ │ │ └── tokens.ts │ │ └── xmlui-parser │ │ ├── CharacterCodes.ts │ │ ├── diagnostics.ts │ │ ├── fileExtensions.ts │ │ ├── index.ts │ │ ├── lint.ts │ │ ├── parser.ts │ │ ├── ParserError.ts │ │ ├── scanner.ts │ │ ├── syntax-kind.ts │ │ ├── syntax-node.ts │ │ ├── transform.ts │ │ ├── utils.ts │ │ ├── xmlui-serializer.ts │ │ └── xmlui-tree.ts │ ├── react-app-env.d.ts │ ├── syntax │ │ ├── monaco │ │ │ ├── grammar.monacoLanguage.ts │ │ │ ├── index.ts │ │ │ ├── xmlui-dark.ts │ │ │ ├── xmlui-light.ts │ │ │ └── xmluiscript.monacoLanguage.ts │ │ └── textMate │ │ ├── index.ts │ │ ├── xmlui-dark.json │ │ ├── xmlui-light.json │ │ ├── xmlui.json │ │ └── xmlui.tmLanguage.json │ ├── testing │ │ ├── assertions.ts │ │ ├── component-test-helpers.ts │ │ ├── ComponentDrivers.ts │ │ ├── drivers │ │ │ ├── DateInputDriver.ts │ │ │ ├── ModalDialogDriver.ts │ │ │ ├── NumberBoxDriver.ts │ │ │ ├── TextBoxDriver.ts │ │ │ ├── TimeInputDriver.ts │ │ │ ├── TimerDriver.ts │ │ │ └── TreeDriver.ts │ │ ├── fixtures.ts │ │ ├── infrastructure │ │ │ ├── index.html │ │ │ ├── main.tsx │ │ │ ├── public │ │ │ │ ├── mockServiceWorker.js │ │ │ │ ├── resources │ │ │ │ │ ├── bell.svg │ │ │ │ │ ├── box.svg │ │ │ │ │ ├── doc.svg │ │ │ │ │ ├── eye.svg │ │ │ │ │ ├── flower-640x480.jpg │ │ │ │ │ ├── sun.svg │ │ │ │ │ ├── test-image-100x100.jpg │ │ │ │ │ └── txt.svg │ │ │ │ └── serve.json │ │ │ └── TestBed.tsx │ │ └── themed-app-test-helpers.ts │ └── vite-env.d.ts ├── tests │ ├── components │ │ ├── CodeBlock │ │ │ └── hightlight-code.test.ts │ │ ├── playground-pattern.test.ts │ │ └── Tree │ │ └── Tree-states.test.ts │ ├── components-core │ │ ├── abstractions │ │ │ └── treeAbstractions.test.ts │ │ ├── container │ │ │ └── buildProxy.test.ts │ │ ├── interception │ │ │ ├── orderBy.test.ts │ │ │ ├── ReadOnlyCollection.test.ts │ │ │ └── request-param-converter.test.ts │ │ ├── scripts-runner │ │ │ ├── AttributeValueParser.test.ts │ │ │ ├── eval-tree-arrow-async.test.ts │ │ │ ├── eval-tree-arrow.test.ts │ │ │ ├── eval-tree-func-decl-async.test.ts │ │ │ ├── eval-tree-func-decl.test.ts │ │ │ ├── eval-tree-pre-post.test.ts │ │ │ ├── eval-tree-regression.test.ts │ │ │ ├── eval-tree.test.ts │ │ │ ├── function-proxy.test.ts │ │ │ ├── parser-regression.test.ts │ │ │ ├── process-event.test.ts │ │ │ ├── process-function.test.ts │ │ │ ├── process-implicit-context.test.ts │ │ │ ├── process-statement-asgn.test.ts │ │ │ ├── process-statement-destruct.test.ts │ │ │ ├── process-statement-regs.test.ts │ │ │ ├── process-statement-sync.test.ts │ │ │ ├── process-statement.test.ts │ │ │ ├── process-switch-sync.test.ts │ │ │ ├── process-switch.test.ts │ │ │ ├── process-try-sync.test.ts │ │ │ ├── process-try.test.ts │ │ │ └── test-helpers.ts │ │ ├── test-metadata-handler.ts │ │ ├── theming │ │ │ ├── border-segments.test.ts │ │ │ ├── component-layout.resolver.test.ts │ │ │ ├── layout-property-parser.test.ts │ │ │ ├── layout-resolver.test.ts │ │ │ ├── layout-resolver2.test.ts │ │ │ ├── layout-vp-override.test.ts │ │ │ └── padding-segments.test.ts │ │ └── utils │ │ ├── date-utils.test.ts │ │ ├── format-human-elapsed-time.test.ts │ │ └── LruCache.test.ts │ ├── language-server │ │ ├── completion.test.ts │ │ ├── format.test.ts │ │ ├── hover.test.ts │ │ └── mockData.ts │ └── parsers │ ├── common │ │ └── input-stream.test.ts │ ├── markdown │ │ └── parse-binding-expression.test.ts │ ├── parameter-parser.test.ts │ ├── paremeter-parser.test.ts │ ├── scripting │ │ ├── eval-tree-arrow.test.ts │ │ ├── eval-tree-pre-post.test.ts │ │ ├── eval-tree.test.ts │ │ ├── function-proxy.test.ts │ │ ├── lexer-literals.test.ts │ │ ├── lexer-misc.test.ts │ │ ├── module-parse.test.ts │ │ ├── parser-arrow.test.ts │ │ ├── parser-assignments.test.ts │ │ ├── parser-binary.test.ts │ │ ├── parser-destructuring.test.ts │ │ ├── parser-errors.test.ts │ │ ├── parser-expressions.test.ts │ │ ├── parser-function.test.ts │ │ ├── parser-literals.test.ts │ │ ├── parser-primary.test.ts │ │ ├── parser-regex.test.ts │ │ ├── parser-statements.test.ts │ │ ├── parser-unary.test.ts │ │ ├── process-event.test.ts │ │ ├── process-implicit-context.test.ts │ │ ├── process-statement-asgn.test.ts │ │ ├── process-statement-destruct.test.ts │ │ ├── process-statement-regs.test.ts │ │ ├── process-statement-sync.test.ts │ │ ├── process-statement.test.ts │ │ ├── process-switch-sync.test.ts │ │ ├── process-switch.test.ts │ │ ├── process-try-sync.test.ts │ │ ├── process-try.test.ts │ │ ├── simplify-expression.test.ts │ │ ├── statement-hooks.test.ts │ │ └── test-helpers.ts │ ├── style-parser │ │ ├── generateHvarChain.test.ts │ │ ├── parseHVar.test.ts │ │ ├── parser.test.ts │ │ └── tokens.test.ts │ └── xmlui │ ├── lint.test.ts │ ├── parser.test.ts │ ├── scanner.test.ts │ ├── transform.attr.test.ts │ ├── transform.circular.test.ts │ ├── transform.element.test.ts │ ├── transform.errors.test.ts │ ├── transform.escape.test.ts │ ├── transform.regression.test.ts │ ├── transform.script.test.ts │ ├── transform.test.ts │ └── xmlui.ts ├── tests-e2e │ ├── api-bound-component-regression.spec.ts │ ├── api-call-as-extracted-component.spec.ts │ ├── assign-to-object-or-array-regression.spec.ts │ ├── binding-regression.spec.ts │ ├── children-as-template-context-vars.spec.ts │ ├── compound-component.spec.ts │ ├── context-vars-regression.spec.ts │ ├── data-bindings.spec.ts │ ├── datasource-and-api-usage-in-var.spec.ts │ ├── datasource-direct-binding.spec.ts │ ├── datasource-onLoaded-regression.spec.ts │ ├── modify-array-item-regression.spec.ts │ ├── namespaces.spec.ts │ ├── push-to-array-regression.spec.ts │ ├── screen-breakpoints.spec.ts │ ├── scripting.spec.ts │ ├── state-scope-in-pages.spec.ts │ └── state-var-scopes.spec.ts ├── tsconfig.bin.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /xmlui/src/components/Select/MultiSelectOption.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { type ForwardedRef, forwardRef, useId, useMemo } from "react"; 2 | import type { Option } from "../abstractions"; 3 | import { useSelect } from "./SelectContext"; 4 | import classnames from "classnames"; 5 | import styles from "./Select.module.scss"; 6 | import Icon from "../Icon/IconNative"; 7 | 8 | export const MultiSelectOption = forwardRef< 9 | HTMLDivElement, 10 | Option & { isHighlighted?: boolean; itemIndex?: number } 11 | >(function MultiSelectOption(option, forwardedRef: ForwardedRef<HTMLDivElement>) { 12 | const id = useId(); 13 | const { 14 | label, 15 | value, 16 | enabled = true, 17 | keywords, 18 | readOnly, 19 | children, 20 | isHighlighted = false, 21 | itemIndex, 22 | } = option; 23 | const { 24 | value: selectedValue, 25 | onChange, 26 | multiSelect, 27 | setOpen, 28 | setSelectedIndex, 29 | optionRenderer, 30 | } = useSelect(); 31 | const selected = useMemo(() => { 32 | return Array.isArray(selectedValue) && multiSelect 33 | ? selectedValue.map((v) => String(v)).includes(value) 34 | : String(selectedValue) === String(value); 35 | }, [selectedValue, value, multiSelect]); 36 | 37 | const handleClick = () => { 38 | if (readOnly) { 39 | setOpen(false); 40 | return; 41 | } 42 | if (enabled) { 43 | onChange(value); 44 | } 45 | }; 46 | 47 | return ( 48 | <div 49 | id={id} 50 | ref={forwardedRef} 51 | role="option" 52 | aria-disabled={!enabled} 53 | aria-selected={selected} 54 | className={classnames(styles.multiSelectOption, { 55 | [styles.disabledOption]: !enabled, 56 | [styles.highlighted]: isHighlighted, 57 | })} 58 | onMouseDown={(e) => { 59 | e.preventDefault(); 60 | e.stopPropagation(); 61 | }} 62 | onMouseEnter={() => { 63 | if (itemIndex !== undefined && setSelectedIndex && enabled) { 64 | setSelectedIndex(itemIndex); 65 | } 66 | }} 67 | onClick={handleClick} 68 | data-state={selected ? "checked" : undefined} 69 | > 70 | <div className={styles.multiSelectOptionContent}> 71 | {optionRenderer ? ( 72 | optionRenderer({ label, value, enabled, keywords }, selectedValue as any, false) 73 | ) : ( 74 | <> 75 | {children || label} 76 | {selected && <Icon name="checkmark" />} 77 | </> 78 | )} 79 | </div> 80 | </div> 81 | ); 82 | }); 83 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/reportEngineError.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ScriptParseError, StatementExecutionError, ThrowStatementError } from "./EngineError"; 2 | 3 | type ErrorInfo = { 4 | error: Error; 5 | helperMessage?: string; 6 | colors?: string[]; 7 | }; 8 | 9 | const appErrors: ErrorInfo[] = []; 10 | 11 | /** 12 | * Get all errors collected during the last run 13 | */ 14 | export function getAppErrors(): ErrorInfo[] { 15 | return appErrors; 16 | } 17 | 18 | /** 19 | * Use this function to reset the errors raised by the execution engine 20 | */ 21 | export function resetErrors(): void { 22 | appErrors.length = 0; 23 | } 24 | 25 | /** 26 | * Use this function to report an error 27 | * @param error Error or string describing the error to report 28 | * @param errorToThrow The error to throw 29 | */ 30 | export function reportEngineError(error: Error | string, errorToThrow?: any): void { 31 | // --- Wrap a string into an error 32 | if (typeof error === "string") { 33 | error = new Error(error); 34 | } 35 | 36 | let helperMessage = ""; 37 | let colors: string[] = []; 38 | 39 | // --- Error-specific helper messages 40 | if (error instanceof ScriptParseError) { 41 | let pos = (error?.position ?? 0) - 1; 42 | if (error.source) { 43 | while (pos < error.source.length - 1 && error.source[pos] === " ") { 44 | pos++; 45 | } 46 | helperMessage += `%c${error.message}%c\n\n`; 47 | helperMessage += `${error.source.substring(0, pos)}%c${error.source[pos]}%c${ 48 | error.source.substring(pos + 1) ?? "" 49 | }\n\n`; 50 | } 51 | helperMessage += `%cThe error handler associated with the parsed code did not run.%c`; 52 | colors = ["color: red", "color: inherited", "color: red", "color: inherited", "color: orange", "color: inherited"]; 53 | } else if (error instanceof StatementExecutionError) { 54 | helperMessage += `%cError while executing code: ${error.message}%c`; 55 | if (error.source) { 56 | helperMessage += `\n\n${error.source}`; 57 | } 58 | colors = ["color: red", "color: inherited"]; 59 | } else if (error instanceof ThrowStatementError) { 60 | helperMessage += `A 'throw' statement executed:\n\n%c${error.message}%c\n\n${error.errorObject}`; 61 | colors = ["color: red", "color: inherited"]; 62 | } 63 | 64 | if (helperMessage) { 65 | console.log(helperMessage, ...colors); 66 | } 67 | appErrors.push({ error, helperMessage, colors }); 68 | throw errorToThrow ?? error; 69 | } 70 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Option/Option.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Value and label separation**: Define what gets stored (value) separately from what users see (label) 5 | - **Automatic fallbacks**: Uses label as value or value as label when only one is provided 6 | - **Custom templates**: Support for rich content via optionTemplate property or child components 7 | - **State management**: Built-in enabled/disabled states for individual options 8 | - **Data integration**: Works seamlessly with Items components for dynamic option lists 9 | 10 | ## Using `Option` 11 | 12 | ### With `AutoComplete` 13 | 14 | ```xmlui-pg copy {4-6} display name="Example: Option in a AutoComplete" height="275px" 15 | <App> 16 | <Text value="Selected ID: {myComp.value}"/> 17 | <AutoComplete id="myComp"> 18 | <Option label="John, Smith" value="john" /> 19 | <Option label="Jane, Clint" value="jane" disabled="true" /> 20 | <Option label="Herbert, Frank" value="herbert" /> 21 | </AutoComplete> 22 | </App> 23 | ``` 24 | 25 | ### With `Select` 26 | 27 | ```xmlui-pg copy {4-6} display name="Example: Option in a Select" height="275px" 28 | <App> 29 | <Text value="Selected ID: {mySelect.value}"/> 30 | <Select id="mySelect"> 31 | <Option label="John, Smith" value="john" /> 32 | <Option label="Jane, Clint" value="jane" /> 33 | <Option label="Herbert, Frank" value="herbert" /> 34 | </Select> 35 | </App> 36 | ``` 37 | 38 | %-DESC-END 39 | 40 | %-PROP-START label 41 | 42 | >[!INFO] 43 | > If `Option` does not define any of the `label` or `value` properties, the option will not be rendered. 44 | 45 | ```xmlui-pg copy display name="Example: Using label" height="275px" 46 | <App> 47 | <Text value="Selected ID: {mySelect.value}"/> 48 | <Select id="mySelect"> 49 | <Option /> 50 | <Option label="Vanilla" value="van"/> 51 | <Option label="Chocolate" value="choc" /> 52 | <Option value="pist" /> 53 | </Select> 54 | </App> 55 | ``` 56 | 57 | %-PROP-END 58 | 59 | %-PROP-START value 60 | 61 | >[!INFO] 62 | > If `Option` does not define any of the `label` or `value` properties, the option will not be rendered. 63 | 64 | ```xmlui-pg copy display name="Example: Using value" height="275px" 65 | <App> 66 | <Text value="Selected ID: {mySelect.value}"/> 67 | <Select id="mySelect"> 68 | <Option /> 69 | <Option label="Vanilla" /> 70 | <Option label="Chocolate" value="chocolate" /> 71 | <Option label="Pistachio" value="pistachio" /> 72 | </Select> 73 | </App> 74 | ``` 75 | 76 | %-PROP-END 77 | ``` -------------------------------------------------------------------------------- /docs/public/pages/xmlui-charts/BarChart.md: -------------------------------------------------------------------------------- ```markdown 1 | # BarChart [#barchart] 2 | 3 | >[!WARNING] 4 | > This component is in an **experimental** state; you can use it in your app. However, we may modify it, and it may even have breaking changes in the future.The `BarChart` component represents a bar chart.Accepts a `LabelLst` component as a child to parametrize display labels. 5 | 6 | ## Properties 7 | 8 | ### `data` 9 | 10 | This property is used to provide the component with data to display.The data needs to be an array of objects. 11 | 12 | ### `dataKeys` 13 | 14 | This property specifies the keys in the data objects that should be used for rendering the bars.E.g. 'id' or 'key'. 15 | 16 | ### `hideTickX (default: false)` 17 | 18 | Controls the visibility of the X-axis ticks. If set to `true`, tick labels on the X-axis will be hidden. 19 | 20 | ### `hideTickY (default: false)` 21 | 22 | Controls the visibility of the Y-axis ticks. If set to `true`, tick labels on the Y-axis will be hidden. 23 | 24 | ### `hideX (default: false)` 25 | 26 | Determines whether the X-axis should be hidden. If set to `true`, the axis will not be rendered. 27 | 28 | ### `hideY (default: false)` 29 | 30 | Determines whether the Y-axis should be hidden. If set to `true`, the axis will not be rendered. 31 | 32 | ### `layout (default: "vertical")` 33 | 34 | This property determines the orientation of the bar chart. The `vertical` variant specifies the horizontal axis as the primary and lays out the bars from left to right. The `horizontal` variant specifies the vertical axis as the primary and has the bars spread from top to bottom. 35 | 36 | Available values: `horizontal`, `vertical` **(default)** 37 | 38 | ### `nameKey` 39 | 40 | Specifies the key in the data objects that will be used to label the different data series. 41 | 42 | ### `showLegend (default: false)` 43 | 44 | Determines whether the legend should be displayed. 45 | 46 | ### `stacked (default: false)` 47 | 48 | This property determines how the bars are laid out.If set to `true`, bars with the same category will be stacked on top of each other rather than placed side by side. 49 | 50 | ### `tickFormatter` 51 | 52 | A function that formats the axis tick labels. It receives a tick value and returns a formatted string. 53 | 54 | ## Events 55 | 56 | This component does not have any events. 57 | 58 | ## Exposed Methods 59 | 60 | This component does not expose any methods. 61 | 62 | ## Styling 63 | 64 | This component does not have any styles. 65 | ``` -------------------------------------------------------------------------------- /docs/content/components/Logo.md: -------------------------------------------------------------------------------- ```markdown 1 | # Logo [#logo] 2 | 3 | `Logo` displays your application's brand symbol by automatically loading logo images defined in the app manifest. While logos are typically configured using App-level properties (`logo`, `logo-dark`), this component provides direct control when you need custom logo placement or templating. 4 | 5 | Most apps use `logo="path/to/logo.svg"` on the App component rather than using `<Logo>` directly. Use this component when you need custom logo positioning or want to combine logos with other elements in a `logoTemplate`. 6 | 7 | ## Using Logo [#using-logo] 8 | 9 | The framework checks the application manifest for a logo resource (SVG file). 10 | If found, it loads and displays it in the Logo component. 11 | 12 | This is a sample manifest that shows a logo definition: 13 | 14 | ```json copy {5} 15 | { 16 | "name": "Tutorial", 17 | "version": "0.0.1", 18 | "resources": { 19 | "logo": "resources/xmlui-logo.svg", 20 | "favicon": "resources/favicon.ico" 21 | } 22 | } 23 | ``` 24 | 25 | In the following example, you can see a custom logo definition in the `AppHeader` via templating. 26 | There is a `Heading` with the title text "MyApp" before the logo. 27 | It also uses the `Logo` component within the template definition: 28 | 29 | ```xmlui-pg 30 | ---app copy display name="Example: using Logo" {6} height="200px" 31 | <App layout="horizontal"> 32 | <AppHeader> 33 | <property name="logoTemplate"> 34 | <Fragment> 35 | <Heading level="h2" value="MyApp"/> 36 | <Logo/> 37 | </Fragment> 38 | </property> 39 | </AppHeader> 40 | <NavPanel> 41 | <NavLink label="Home" to="/" icon="home"/> 42 | </NavPanel> 43 | <Pages fallbackPath="/"> 44 | <Page url="/"> 45 | <CHStack> 46 | (Sample content) 47 | </CHStack> 48 | </Page> 49 | </Pages> 50 | </App> 51 | ---desc 52 | The markup displays the app's logo: 53 | ``` 54 | 55 | ## Properties [#properties] 56 | 57 | ### `alt` (default: "Logo") [#alt-default-logo] 58 | 59 | Alternative text for the logo image for accessibility. 60 | 61 | ### `inline` (default: false) [#inline-default-false] 62 | 63 | When set to true, the image will be displayed as an inline element instead of a block element. 64 | 65 | ## Events [#events] 66 | 67 | This component does not have any events. 68 | 69 | ## Exposed Methods [#exposed-methods] 70 | 71 | This component does not expose any methods. 72 | 73 | ## Styling [#styling] 74 | 75 | This component does not have any styles. 76 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/NestedApp/logo.svg: -------------------------------------------------------------------------------- ``` 1 | <svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <path d="M9.04674 19.3954C8.2739 19.3954 7.60226 19.2265 7.03199 18.8887C6.47443 18.5384 6.0435 18.0505 5.73938 17.425C5.43526 16.7869 5.2832 16.0362 5.2832 15.173V9.89961C5.2832 9.7745 5.32771 9.66815 5.41637 9.58059C5.50502 9.493 5.61275 9.44922 5.73938 9.44922H7.41222C7.55157 9.44922 7.6593 9.493 7.73524 9.58059C7.8239 9.66815 7.86841 9.7745 7.86841 9.89961V15.0604C7.86841 16.6117 8.55895 17.3874 9.94021 17.3874C10.5991 17.3874 11.1187 17.181 11.4988 16.7681C11.8917 16.3553 12.0881 15.786 12.0881 15.0604V9.89961C12.0881 9.7745 12.1325 9.66815 12.2211 9.58059C12.3098 9.493 12.4175 9.44922 12.5443 9.44922H14.217C14.3436 9.44922 14.4513 9.493 14.54 9.58059C14.6288 9.66815 14.6732 9.7745 14.6732 9.89961V18.7574C14.6732 18.8825 14.6288 18.9888 14.54 19.0764C14.4513 19.164 14.3436 19.2078 14.217 19.2078H12.6773C12.538 19.2078 12.4239 19.164 12.3352 19.0764C12.2591 18.9888 12.2211 18.8825 12.2211 18.7574V17.988C11.879 18.4258 11.4545 18.7699 10.9476 19.0201C10.4407 19.2703 9.80704 19.3954 9.04674 19.3954Z" fill="#3367CC"/> 3 | <path d="M17.6397 19.2104C17.5129 19.2104 17.4052 19.1666 17.3165 19.079C17.2279 18.9914 17.1835 18.8851 17.1835 18.76V9.90221C17.1835 9.7771 17.2279 9.67075 17.3165 9.58319C17.4052 9.4956 17.5129 9.45182 17.6397 9.45182H19.2174C19.3567 9.45182 19.4644 9.4956 19.5404 9.58319C19.6292 9.67075 19.6736 9.7771 19.6736 9.90221V18.76C19.6736 18.8851 19.6292 18.9914 19.5404 19.079C19.4644 19.1666 19.3567 19.2104 19.2174 19.2104H17.6397ZM17.5636 7.8379C17.4243 7.8379 17.3102 7.80038 17.2215 7.72531C17.1454 7.63773 17.1074 7.52514 17.1074 7.38751V6.03633C17.1074 5.91122 17.1454 5.80487 17.2215 5.71731C17.3102 5.62972 17.4243 5.58594 17.5636 5.58594H19.2933C19.4327 5.58594 19.5467 5.62972 19.6354 5.71731C19.7242 5.80487 19.7686 5.91122 19.7686 6.03633V7.38751C19.7686 7.52514 19.7242 7.63773 19.6354 7.72531C19.5467 7.80038 19.4327 7.8379 19.2933 7.8379H17.5636Z" fill="#3367CC"/> 4 | <path fill-rule="evenodd" clip-rule="evenodd" d="M23.0215 2.81748H2.53486V23.044H23.0215V2.81748ZM2.18164 2.46875V23.3927H23.3747V2.46875H2.18164Z" fill="#3367CC"/> 5 | </svg> 6 | ``` -------------------------------------------------------------------------------- /tools/create-app/templates/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { install } from "../helpers/install"; 2 | import { copy } from "../helpers/copy"; 3 | import os from "os"; 4 | import fs from "fs/promises"; 5 | import path from "path"; 6 | import { bold, cyan } from "picocolors"; 7 | import pkg from '../package.json' 8 | 9 | import type { InstallTemplateArgs } from "./types"; 10 | 11 | /** 12 | * Install a UI Engine internal template to a given `root` directory. 13 | */ 14 | export const installTemplate = async ({ appName, root, packageManager, template, useGit }: InstallTemplateArgs) => { 15 | console.log(bold(`Using ${packageManager}.`)); 16 | 17 | /** 18 | * Copy the template files to the target directory. 19 | */ 20 | console.log("\nInitializing project with template:", template, "\n"); 21 | const templatePath = path.join(__dirname, template, "ts"); 22 | const copySource = ["**"]; 23 | 24 | if (!useGit) { 25 | copySource.push("!gitignore"); 26 | } 27 | await copy(copySource, root, { 28 | parents: true, 29 | cwd: templatePath, 30 | rename(name) { 31 | switch (name) { 32 | case "gitignore": 33 | case "eslintrc.json": { 34 | return `.${name}`; 35 | } 36 | // README.md is ignored by webpack-asset-relocator-loader used by ncc: 37 | // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 38 | case "README-template.md": { 39 | return "README.md"; 40 | } 41 | default: { 42 | return name; 43 | } 44 | } 45 | }, 46 | }); 47 | 48 | /** Create a package.json for the new project and write it to disk. */ 49 | const packageJson: any = { 50 | name: appName, 51 | version: "0.1.0", 52 | private: true, 53 | scripts: { 54 | start: "xmlui start", 55 | build: "xmlui build", 56 | preview: "xmlui preview", 57 | "build-prod": "npm run build -- --prod", 58 | "release-ci": "npm run build-prod && xmlui zip-dist", 59 | }, 60 | /** 61 | * Default dependencies. 62 | */ 63 | dependencies: { 64 | "xmlui": pkg.version, 65 | }, 66 | }; 67 | 68 | await fs.writeFile(path.join(root, "package.json"), JSON.stringify(packageJson, null, 2) + os.EOL); 69 | 70 | console.log("\nInstalling dependencies:"); 71 | for (const dependency in packageJson.dependencies) console.log(`- ${cyan(dependency)}`); 72 | 73 | console.log(); 74 | 75 | await install(packageManager); 76 | }; 77 | 78 | export * from "./types"; 79 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/utils/LruCache.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { beforeEach, describe, expect, it } from "vitest"; 2 | import { LRUCache } from "../../../src/components-core/utils/LruCache"; 3 | 4 | describe("LRUCache", () => { 5 | let lru: LRUCache; 6 | beforeEach(() => { 7 | lru = new LRUCache(5); 8 | }); 9 | 10 | it.skip("puts most recently added item to front of list", () => { 11 | // note: can't test using lru.get() as that will mess up ordering 12 | // reach into the internals directly instead 13 | lru.set("nick", "nick val 1"); 14 | expect(lru!.list!.head!.value).to.equal("nick val 1"); 15 | lru.set("char", "char val 1"); 16 | expect(lru.list!.head!.value).to.equal("char val 1"); 17 | lru.set("nick", "nick val 2"); 18 | expect(lru.list!.head!.value).to.equal("nick val 2"); 19 | }); 20 | 21 | it("puts most recently accessed item to front of list", () => { 22 | // note: can't test using lru.get() as that will mess up ordering 23 | // reach into the internals directly instead 24 | lru.set("nick", "nick val 1"); 25 | lru.set("char", "char val 1"); 26 | lru.set("brow", "brow val 1"); 27 | lru.set("lane", "lane val 1"); 28 | lru.get("nick"); 29 | expect(lru.list!.head!.value).to.equal("nick val 1"); 30 | lru.get("char"); 31 | expect(lru.list!.head!.value).to.equal("char val 1"); 32 | lru.get("lane"); 33 | expect(lru.list!.head!.value).to.equal("lane val 1"); 34 | lru.get("brow"); 35 | expect(lru.list!.head!.value).to.equal("brow val 1"); 36 | lru.get("brow"); 37 | expect(lru.list!.head!.value).to.equal("brow val 1"); 38 | }); 39 | 40 | it.skip("keeps track of size correctly", () => { 41 | lru.set("nick", "nick val 1"); 42 | expect(lru.list.size).to.equal(1); 43 | lru.set("char", "char val 1"); 44 | expect(lru.list.size).to.equal(2); 45 | lru.set("char", "char val 2"); 46 | expect(lru.list.size).to.equal(2); 47 | 48 | lru.set("bowie", "bowie val 1"); 49 | lru.set("david", "david val 1"); 50 | lru.set("dobrick", "dobrick val 1"); 51 | expect(lru.list.size).to.equal(5); 52 | }); 53 | 54 | it("evicts the last item in list when max size is reached", () => { 55 | lru.set("nick", 1); 56 | lru.set("bob", 2); 57 | lru.set("dylan", 3); 58 | lru.set("jonny", 4); 59 | lru.set("depth", 5); 60 | lru.set("shtick", 6); 61 | expect(lru.get("nick")).to.equal(undefined); 62 | expect(lru.get("bob")).not.to.equal(undefined); 63 | }); 64 | }); 65 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Tabs/TabItem.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createComponentRenderer } from "../../components-core/renderers"; 2 | import { TabItemComponent } from "./TabItemNative"; 3 | import { createMetadata, d, dComponent, dLabel } from "../metadata-helpers"; 4 | import { MemoizedItem } from "../container-helpers"; 5 | 6 | const COMP = "TabItem"; 7 | 8 | export const TabItemMd = createMetadata({ 9 | status: "stable", 10 | description: 11 | "`TabItem` defines individual tabs within a [Tabs](/components/Tabs) component, " + 12 | "providing both the tab header label and the content that displays when the tab " + 13 | "is selected. As a non-visual structural component, it serves as a container that " + 14 | "organizes content into distinct, switchable sections.", 15 | docFolder: "Tabs", 16 | props: { 17 | label: dLabel(), 18 | headerTemplate: dComponent("This property allows the customization of the TabItem header."), 19 | }, 20 | events: { 21 | activated: { 22 | description: "This event is triggered when the tab is activated.", 23 | }, 24 | }, 25 | contextVars: { 26 | $header: d( 27 | "This context value represents the header context with props: id (optional), index, label, isActive.", 28 | ), 29 | }, 30 | }); 31 | 32 | export const tabItemComponentRenderer = createComponentRenderer( 33 | COMP, 34 | TabItemMd, 35 | (rendererContext) => { 36 | const { node, renderChild, extractValue, lookupEventHandler } = rendererContext; 37 | return ( 38 | <TabItemComponent 39 | id={extractValue(node.uid)} 40 | label={extractValue(node.props.label)} 41 | activated={lookupEventHandler("activated")} 42 | headerRenderer={ 43 | node.props.headerTemplate 44 | ? (item) => { 45 | return ( 46 | <MemoizedItem 47 | node={node.props.headerTemplate} 48 | itemKey="$header" 49 | contextVars={{ 50 | $header: { 51 | id: item.id, 52 | index: item.index, 53 | label: item.label, 54 | isActive: item.isActive, 55 | }, 56 | }} 57 | renderChild={renderChild} 58 | /> 59 | ); 60 | } 61 | : undefined 62 | } 63 | > 64 | {renderChild(node.children)} 65 | </TabItemComponent> 66 | ); 67 | }, 68 | ); 69 | ``` -------------------------------------------------------------------------------- /xmlui/src/parsers/scripting/ParserError.ts: -------------------------------------------------------------------------------- ```typescript 1 | // The common root class of all parser error objects 2 | export class ParserError extends Error { 3 | constructor(message: string, public code?: string) { 4 | super(message); 5 | 6 | // --- Set the prototype explicitly. 7 | Object.setPrototypeOf(this, ParserError.prototype); 8 | } 9 | } 10 | 11 | // Describes the structure of error messages 12 | export interface ParserErrorMessage { 13 | code: ErrorCodes; 14 | text: string; 15 | position?: number; 16 | line?: number; 17 | column?: number; 18 | } 19 | 20 | export type ErrorCodes = 21 | | "W001" 22 | | "W002" 23 | | "W003" 24 | | "W004" 25 | | "W005" 26 | | "W006" 27 | | "W007" 28 | | "W008" 29 | | "W009" 30 | | "W010" 31 | | "W011" 32 | | "W012" 33 | | "W013" 34 | | "W014" 35 | | "W015" 36 | | "W016" 37 | | "W017" 38 | | "W018" 39 | | "W019" 40 | | "W020" 41 | | "W021" 42 | | "W022" 43 | | "W023" 44 | | "W024" 45 | | "W025" 46 | | "W026" 47 | | "W027" 48 | | "W028" 49 | | "W029" 50 | | "W030" 51 | | "W031"; 52 | 53 | // Error message type description 54 | type ErrorText = Record<string, string>; 55 | 56 | // The error messages of error codes 57 | export const errorMessages: ErrorText = { 58 | W001: "An expression expected", 59 | W002: "Unexpected token: {0}", 60 | W003: "An identifier expected", 61 | W004: "'}' expected", 62 | W005: "']' expected", 63 | W006: "')' expected", 64 | W007: "Invalid object property name type", 65 | W008: "':' expected", 66 | W009: "'=' expected", 67 | W010: "Invalid argument list", 68 | W011: "For loop variable must be initialized", 69 | W012: "'{' expected", 70 | W013: "'catch' or 'finally' expected", 71 | W014: "'(' or expected", 72 | W015: "'case' or 'default' expected", 73 | W016: "'default' case can be used only once within a switch statement", 74 | W017: "Invalid sequence expression", 75 | W018: "Invalid object literal", 76 | W019: "Identifier '{0}' is already imported", 77 | W020: "Function '{0}' is already defined in the module", 78 | W021: "'{0}' is already exported from the module", 79 | W022: "Cannot find module '{0}'", 80 | W023: "Module '{0}' does not export '{1}'", 81 | W024: "'function' expected", 82 | W025: "'from' expected", 83 | W026: "A string literal expected", 84 | W027: "Cannot declare var ('{0}') in an imported module", 85 | W028: "Invalid statement used in a module.", 86 | W029: "An imported module can contain only exported functions", 87 | W030: "Nested declarations cannot be exported", 88 | W031: "An identifier in a declaration cannot start with '$'" 89 | }; 90 | ``` -------------------------------------------------------------------------------- /docs/content/components/Timer.md: -------------------------------------------------------------------------------- ```markdown 1 | # Timer [#timer] 2 | 3 | `Timer` is a non-visual component that fires events at regular intervals. It can be enabled or disabled and ensures that the timer event handler completes before firing the next event. 4 | 5 | ## Using Timer [#using-timer] 6 | 7 | The following sample demonstrates many aspects of the `Timer` component. Use the switches and the buttons to observe how the component works. 8 | 9 | ```xmlui-pg display copy name="Using Timer" 10 | <App var.count="{0}"> 11 | <Text> 12 | Count: {count} | Timer is {timer.isPaused() ? 'paused' : 'running'} 13 | </Text> 14 | <Timer 15 | id="timer" 16 | initialDelay="2000" 17 | interval="200" 18 | onTick="count++;" 19 | enabled="{enable.value}" 20 | once="{once.value}" /> 21 | <Switch id="enable" label="Enable Timer" initialValue="true" /> 22 | <Switch id="once" label="Run Once" initialValue="{false}" /> 23 | <HStack> 24 | <Button onClick="timer.pause()" enabled="{!timer.isPaused()}"> 25 | Pause 26 | </Button> 27 | <Button onClick="timer.resume()" enabled="{timer.isPaused()}"> 28 | Resume 29 | </Button> 30 | </HStack> 31 | </App> 32 | ``` 33 | 34 | ## Properties [#properties] 35 | 36 | ### `enabled` (default: true) [#enabled-default-true] 37 | 38 | Whether the timer is enabled and should fire events. 39 | 40 | ### `initialDelay` (default: 0) [#initialdelay-default-0] 41 | 42 | The delay in milliseconds before the first timer event. 43 | 44 | ### `interval` (default: 1000) [#interval-default-1000] 45 | 46 | The interval in milliseconds between timer events. 47 | 48 | ### `once` (default: false) [#once-default-false] 49 | 50 | Whether the timer should stop after firing its first tick event. 51 | 52 | ## Events [#events] 53 | 54 | ### `tick` [#tick] 55 | 56 | This event is triggered at each interval when the ${COMP} is enabled. 57 | 58 | ## Exposed Methods [#exposed-methods] 59 | 60 | ### `isPaused` [#ispaused] 61 | 62 | Returns whether the timer is currently paused. 63 | 64 | **Signature**: `isPaused(): boolean` 65 | 66 | ### `isRunning` [#isrunning] 67 | 68 | Returns whether the timer is currently running (enabled and not paused). 69 | 70 | **Signature**: `isRunning(): boolean` 71 | 72 | ### `pause` [#pause] 73 | 74 | Pauses the timer. The timer can be resumed later from where it left off. 75 | 76 | **Signature**: `pause()` 77 | 78 | ### `resume` [#resume] 79 | 80 | Resumes a paused timer. If the timer is not paused, this method has no effect. 81 | 82 | **Signature**: `resume()` 83 | 84 | ## Styling [#styling] 85 | 86 | This component does not have any styles. 87 | ``` -------------------------------------------------------------------------------- /xmlui/playwright.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig, devices } from "@playwright/test"; 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // import path from 'path'; 9 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 10 | 11 | /** 12 | * See https://playwright.dev/docs/test-configuration. 13 | */ 14 | 15 | const port = 3211; 16 | export default defineConfig({ 17 | /* Run tests in files in parallel */ 18 | fullyParallel: true, 19 | testMatch: "*.spec.ts", 20 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 21 | forbidOnly: !!process.env.CI, 22 | 23 | /* 24 | Default Github job runners have 4 cores (on public repos, 2 on privates) 25 | https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories */ 26 | workers: undefined, 27 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 28 | reporter: process.env.CI ? [["github"], ["html"]] : [["html"]], 29 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 30 | use: { 31 | ...devices["Desktop Chrome"], 32 | channel: "chromium", 33 | /* Base URL to use in actions like `await page.goto('/')`. */ 34 | baseURL: `http://localhost:${port}`, 35 | 36 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 37 | trace: "on-first-retry", 38 | serviceWorkers: "allow", 39 | /* Grants specified permissions to the browser context. */ 40 | permissions: ["clipboard-read", "clipboard-write"], 41 | }, 42 | 43 | /* Global timeout settings */ 44 | timeout: process.env.CI ? 5000 : 10000, 45 | expect: { 46 | timeout: 5000, // 5 seconds for expect assertions 47 | }, 48 | 49 | retries: process.env.CI ? 2 : 1, 50 | /* Configure projects for major browsers */ 51 | projects: [ 52 | { 53 | name: "non-smoke", 54 | grepInvert: /@smoke/, 55 | }, 56 | { 57 | name: "smoke", 58 | grep: /@smoke/, 59 | }, 60 | ], 61 | 62 | /* Run your local dev server before starting the tests */ 63 | webServer: { 64 | command: process.env.CI 65 | ? `npx serve src/testing/infrastructure/dist -p ${port}` 66 | : `npm run start-test-bed -- --port ${port}`, 67 | timeout: 50 * 1000, 68 | port, 69 | reuseExistingServer: !process.env.CI, 70 | }, 71 | }); 72 | ``` -------------------------------------------------------------------------------- /packages/xmlui-website-blocks/src/ScrollToTop/ScrollToTop.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $component: "ScrollToTop"; 11 | $backgroundColor-ScrollToTop: createThemeVar("backgroundColor-#{$component}"); 12 | $borderColor-ScrollToTop: createThemeVar("borderColor-#{$component}"); 13 | $color-ScrollToTop: createThemeVar("color-#{$component}"); 14 | $size-ScrollToTop: createThemeVar("size-#{$component}"); 15 | $borderRadius-ScrollToTop: createThemeVar("borderRadius-#{$component}"); 16 | $shadow-ScrollToTop: createThemeVar("shadow-#{$component}"); 17 | $bottom-ScrollToTop: createThemeVar("bottom-#{$component}"); 18 | $horizontalSpacing-ScrollToTop: createThemeVar("horizontalSpacing-#{$component}"); 19 | $zIndex-ScrollToTop: createThemeVar("zIndex-#{$component}"); 20 | 21 | @layer components { 22 | .scrollToTop { 23 | position: fixed; 24 | bottom: $bottom-ScrollToTop; 25 | width: $size-ScrollToTop; 26 | height: $size-ScrollToTop; 27 | background-color: $backgroundColor-ScrollToTop; 28 | border: 1px solid $borderColor-ScrollToTop; 29 | border-radius: $borderRadius-ScrollToTop; 30 | box-shadow: $shadow-ScrollToTop; 31 | cursor: pointer; 32 | z-index: $zIndex-ScrollToTop; 33 | display: flex; 34 | align-items: center; 35 | justify-content: center; 36 | transition: all 0.3s ease; 37 | color: $color-ScrollToTop; 38 | 39 | &:hover { 40 | transform: translateY(-2px); 41 | box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); 42 | } 43 | 44 | &:active { 45 | transform: translateY(0); 46 | } 47 | 48 | &:focus { 49 | outline: 2px solid $backgroundColor-ScrollToTop; 50 | outline-offset: 2px; 51 | } 52 | 53 | &.positionStart { 54 | left: $horizontalSpacing-ScrollToTop; 55 | } 56 | 57 | &.positionCenter { 58 | left: 50%; 59 | transform: translateX(-50%); 60 | 61 | &:hover { 62 | transform: translateX(-50%) translateY(-2px); 63 | } 64 | 65 | &:active { 66 | transform: translateX(-50%) translateY(0); 67 | } 68 | } 69 | 70 | &.positionEnd { 71 | right: $horizontalSpacing-ScrollToTop; 72 | } 73 | } 74 | } 75 | 76 | // --- We export the theme variables to add them to the component renderer 77 | :export { 78 | themeVars: t.json-stringify($themeVars); 79 | } 80 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Markdown/parse-binding-expr.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { ValueExtractor } from "../../abstractions/RendererDefs"; 2 | import { T_ARROW_EXPRESSION } from "../../components-core/script-runner/ScriptingSourceTree"; 3 | 4 | /** 5 | * Finds and evaluates given binding expressions in markdown text. 6 | * The binding expressions are of the form `@{...}`. 7 | * @param text The markdown text 8 | * @param extractValue The function to resolve binding expressions 9 | * @returns the parsed text with resolved binding expressions 10 | */ 11 | export function parseBindingExpression(text: string, extractValue: ValueExtractor) { 12 | // Remove empty @{} expressions first 13 | text = text.replaceAll(/(?<!\\)\@\{\s*\}/g, ""); 14 | // The (?<!\\) is a "negative lookbehind" in regex that ensures that 15 | // if escaping the @{...} expression like this: \@{...}, we don't match it 16 | const regex = /(?<!\\)\@\{((?:[^{}]|\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*)\}/g; 17 | const result = text.replace(regex, (_, expr) => { 18 | const extracted = extractValue(`{${expr}}`); 19 | const resultExpr = mapByType(extracted); 20 | // The result expression might be an object, in that case we stringify it here, 21 | // at the last step, so that there are no unnecessary apostrophes 22 | return typeof resultExpr === "object" && resultExpr !== null 23 | ? JSON.stringify(resultExpr) 24 | : resultExpr; 25 | }); 26 | return result; 27 | 28 | // --- 29 | 30 | function mapByType(extracted: unknown) { 31 | if (extracted === null) { 32 | return null; 33 | } else if (extracted === undefined || typeof extracted === "undefined") { 34 | return undefined; 35 | } else if (typeof extracted === "object") { 36 | const arrowFuncResult = parseArrowFunc(extracted as Record<string, unknown>); 37 | if (arrowFuncResult) { 38 | return arrowFuncResult; 39 | } 40 | if (Array.isArray(extracted)) { 41 | return extracted; 42 | } 43 | return Object.fromEntries( 44 | Object.entries(extracted).map(([key, value]) => { 45 | return [key, mapByType(value)]; 46 | }), 47 | ); 48 | } else { 49 | return extracted; 50 | } 51 | } 52 | 53 | function parseArrowFunc(extracted: Record<string, unknown>): string { 54 | if ( 55 | extracted.hasOwnProperty("type") && 56 | extracted.type === T_ARROW_EXPRESSION && 57 | extracted?._ARROW_EXPR_ 58 | ) { 59 | return "[xmlui function]"; 60 | } 61 | return ""; 62 | } 63 | } 64 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Bookmark/Bookmark.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | > [!INFO] 4 | > Pop out the examples in this article to view them on full screen. 5 | 6 | ## Using Bookmark 7 | 8 | Use `Bookmark` as a standalone tag or wrap children with it. 9 | 10 | > [!INFO] 11 | > We suggest using a standalone bookmark, which does not increase the nesting depth of the source code, whenever possible. Note that a standalone bookmark will act as an additional child for its parent component, which can affect the layout (a `Stack` puts `gap`s between `Bookmark`s too). 12 | 13 | ### Standalone 14 | 15 | Add an `id` property to `Bookmark` instances and use the same identifiers in links with hash tags, as the following example shows: 16 | 17 | ```xmlui-pg copy display height="320px" name="Example: standalone Bookmark" 18 | ---app display copy 19 | <App layout="vertical-full-header" scrollWholePage="false"> 20 | <NavPanel> 21 | <Link to="/#red">Jump to red</Link> 22 | <Link to="/#green">Jump to green</Link> 23 | <Link to="/#blue">Jump to blue</Link> 24 | </NavPanel> 25 | <Pages> 26 | <Page url="/"> 27 | <Bookmark id="red"> 28 | <VStack height="200px" backgroundColor="red" /> 29 | </Bookmark> 30 | <Bookmark id="green"> 31 | <VStack height="200px" backgroundColor="green" /> 32 | </Bookmark> 33 | <Bookmark id="blue"> 34 | <VStack height="200px" backgroundColor="blue" /> 35 | </Bookmark> 36 | </Page> 37 | </Pages> 38 | </App> 39 | ---desc 40 | Clicking a link scrolls the bookmarked component adjacent to the corresponding `Bookmark` tag into the view: 41 | ``` 42 | 43 | ### With nested children 44 | 45 | Alternatively, you can nest components into `Bookmark`: 46 | 47 | ```xmlui-pg copy display height="320px" name="Example: Bookmark with nested children" 48 | ---app display copy 49 | <App layout="vertical-full-header" scrollWholePage="false"> 50 | <NavPanel> 51 | <Link to="/#red">Jump to red</Link> 52 | <Link to="/#green">Jump to green</Link> 53 | <Link to="/#blue">Jump to blue</Link> 54 | </NavPanel> 55 | <Pages> 56 | <Page url="/"> 57 | <Bookmark id="red"> 58 | <VStack height="200px" backgroundColor="red" /> 59 | </Bookmark> 60 | <Bookmark id="green"> 61 | <VStack height="200px" backgroundColor="green" /> 62 | </Bookmark> 63 | <Bookmark id="blue"> 64 | <VStack height="200px" backgroundColor="blue" /> 65 | </Bookmark> 66 | </Page> 67 | </Pages> 68 | </App> 69 | ---desc 70 | You can try; this example works like the previous one: 71 | ``` 72 | 73 | %-DESC-END 74 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/TreeDisplay/TreeDisplay.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import styles from "./TreeDisplay.module.scss"; 2 | 3 | import { createComponentRenderer } from "../../components-core/renderers"; 4 | import { parseScssVar } from "../../components-core/theming/themeVars"; 5 | import { TreeDisplay, defaultProps } from "./TreeDisplayNative"; 6 | import { createMetadata } from "../metadata-helpers"; 7 | 8 | const COMP = "TreeDisplay"; 9 | 10 | export const TreeDisplayMd = createMetadata({ 11 | status: "stable", 12 | description: 13 | `The \`${COMP}\` component displays hierarchical data in a tree structure. ` + 14 | `It accepts an indented text format where each line is an entry in the tree, and ` + 15 | `the number of leading spaces determines the nesting level. The component renders ` + 16 | `the tree with SVG lines and continuous vertical guides to clearly visualize parent-child relationships in the hierarchy.`, 17 | props: { 18 | content: { 19 | description: "The indented text content to display as a tree structure. Each level of indentation (using spaces) represents one level in the tree hierarchy.", 20 | valueType: "string", 21 | defaultValue: defaultProps.content, 22 | }, 23 | itemHeight: { 24 | description: "The height of each tree item in pixels.", 25 | valueType: "number", 26 | defaultValue: defaultProps.itemHeight, 27 | }, 28 | }, 29 | themeVars: parseScssVar(styles.themeVars), 30 | defaultThemeVars: { 31 | [`backgroundColor-${COMP}`]: "$backgroundColor-CodeBlock", 32 | [`borderRadius-${COMP}`]: "8px", 33 | [`padding-${COMP}`]: "$space-4", 34 | [`paddingLeft-${COMP}`]: "$space-2", 35 | [`fontSize-${COMP}`]: "$fontSize-code", 36 | [`fontWeight-${COMP}`]: "$fontWeight-normal", 37 | [`fontFamily-${COMP}`]: "$fontFamily-monospace", 38 | [`color-${COMP}`]: "$textColor-primary", 39 | [`color-connect-${COMP}`]: "$color-surface-200", 40 | [`boxShadow-${COMP}`]: "none", 41 | [`border-${COMP}`]: "0.5px solid $borderColor", 42 | }, 43 | }); 44 | 45 | export const treeDisplayComponentRenderer = createComponentRenderer( 46 | COMP, 47 | TreeDisplayMd, 48 | ({ node, extractValue, renderChild, className }) => { 49 | return ( 50 | <TreeDisplay 51 | className={className} 52 | content={extractValue.asOptionalString(node.props.content)} 53 | itemHeight={extractValue.asOptionalNumber(node.props.itemHeight)} 54 | > 55 | {renderChild(node.children)} 56 | </TreeDisplay> 57 | ); 58 | }, 59 | ); 60 | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/parser-errors.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, it } from "vitest"; 2 | import { Parser } from "../../../src/parsers/scripting/Parser"; 3 | 4 | describe("Parser - error cases", () => { 5 | const issueCases = [ 6 | { src: "2+3:", issue: "eof" }, 7 | { src: "a ??", issue: "W001" }, 8 | { src: "(a+b) ||", issue: "W001" }, 9 | { src: "!a &&", issue: "W001" }, 10 | { src: "(a+b) |", issue: "W001" }, 11 | { src: "!a &", issue: "W001" }, 12 | { src: "(a+b) ^", issue: "W001" }, 13 | { src: "!a ==", issue: "W001" }, 14 | { src: "(a+b) !=", issue: "W001" }, 15 | { src: "(a+b) <", issue: "W001" }, 16 | { src: "(a+b) <=", issue: "W001" }, 17 | { src: "(a+b) >", issue: "W001" }, 18 | { src: "(a+b) >=", issue: "W001" }, 19 | { src: "(a+b) >>", issue: "W001" }, 20 | { src: "(a+b) <<", issue: "W001" }, 21 | { src: "(a+b) >>>", issue: "W001" }, 22 | { src: "(a+b) +", issue: "W001" }, 23 | { src: "(a+b) -", issue: "W001" }, 24 | { src: "(a+b) *", issue: "W001" }, 25 | { src: "(a+b) /", issue: "W001" }, 26 | { src: "(a+b) %", issue: "W001" }, 27 | { src: "func(", issue: "W001" }, 28 | { src: "obj.", issue: "W001" }, 29 | { src: "obj[", issue: "W001" }, 30 | 31 | { src: "dummy.", issue: "W003" }, 32 | 33 | { src: "{", issue: "W004" }, 34 | { src: "{ abc: 123,", issue: "W004" }, 35 | { src: "{ abc: 123", issue: "W004" }, 36 | 37 | { src: "obj[abc", issue: "W005" }, 38 | 39 | { src: "func(", issue: "W006" }, 40 | { src: "func(123", issue: "W006" }, 41 | { src: "func(123,", issue: "W006" }, 42 | { src: "func(123, abs", issue: "W006" }, 43 | 44 | { src: "{ true: 123 }", issue: "W007" }, 45 | { src: "{ [a: 123 }", issue: "W005" }, 46 | { src: "{ {a: 12}: 123 }", issue: "W007" }, 47 | { src: "{ null: 123 }", issue: "W007" }, 48 | { src: "{ a+b: 123 }", issue: "W007" }, 49 | 50 | { src: "{ abc", issue: "W008" }, 51 | { src: "{ abc 123", issue: "W008" }, 52 | ]; 53 | issueCases.forEach((c) => { 54 | it(`Issue: ${c.src}/${c.issue}`, () => { 55 | // --- Arrange 56 | const wParser = new Parser(c.src); 57 | 58 | // --- Act 59 | try { 60 | wParser.parseExpr(); 61 | if (wParser.isEof) { 62 | if (c.issue === "eof") { 63 | return; 64 | } 65 | } else { 66 | if (c.issue === "eof") { 67 | throw new Error("EOF issue expected"); 68 | } 69 | } 70 | } catch (err) { 71 | return; 72 | } 73 | throw new Error("Error expected"); 74 | }); 75 | }); 76 | }); 77 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/DropdownMenu/MenuItem.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Action handling**: Support both navigation (`to` property) and custom click handlers 5 | - **Visual feedback**: Built-in active, hover, and disabled states for clear user interaction 6 | - **Icon support**: Optional icons with flexible positioning (start or end) 7 | - **Menu integration**: Designed to work seamlessly within `DropdownMenu` and `SubMenuItem` hierarchies 8 | 9 | **Usage pattern:** 10 | 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. 11 | 12 | %-DESC-END 13 | 14 | %-PROP-START icon 15 | 16 | ```xmlui-pg copy display name="Example: icon" height="200px" 17 | <App> 18 | <DropdownMenu label="DropdownMenu"> 19 | <MenuItem icon="drive">Item 1</MenuItem> 20 | <MenuItem icon="trash">Item 2</MenuItem> 21 | <MenuItem icon="email">Item 3</MenuItem> 22 | </DropdownMenu> 23 | </App> 24 | ``` 25 | 26 | %-PROP-END 27 | 28 | %-PROP-START iconPosition 29 | 30 | ```xmlui-pg copy display name="Example: iconPosition" height="200px" 31 | <App> 32 | <DropdownMenu label="DropdownMenu"> 33 | <MenuItem icon="drive" iconPosition="start">Item 1</MenuItem> 34 | <MenuItem icon="trash" iconPosition="end">Item 2</MenuItem> 35 | <MenuItem icon="email">Item 3</MenuItem> 36 | </DropdownMenu> 37 | </App> 38 | ``` 39 | 40 | %-PROP-END 41 | 42 | %-PROP-START active 43 | 44 | ```xmlui-pg copy display name="Example: active" height="200px" 45 | <App> 46 | <DropdownMenu label="DropdownMenu"> 47 | <MenuItem icon="drive" active="true">Item 1</MenuItem> 48 | <MenuItem icon="trash">Item 2</MenuItem> 49 | <MenuItem icon="email">Item 3</MenuItem> 50 | </DropdownMenu> 51 | </App> 52 | ``` 53 | 54 | %-PROP-END 55 | 56 | %-EVENT-START click 57 | 58 | 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`. 59 | 60 | If both properties are defined, `click` takes precedence. 61 | 62 | ```xmlui-pg copy display name="Example: click" height="200px" 63 | <DropdownMenu label="DropdownMenu"> 64 | <MenuItem onClick="toast('Item 1 clicked')">Item 1</MenuItem> 65 | <MenuItem onClick="toast('Item 2 clicked')">Item 2</MenuItem> 66 | <MenuItem onClick="toast('Item 3 clicked')">Item 3</MenuItem> 67 | </DropdownMenu> 68 | ``` 69 | 70 | %-EVENT-END 71 | ``` -------------------------------------------------------------------------------- /packages/xmlui-os-frames/src/WindowsAppFrame.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | $backgroundColor-content-WindowsAppFrame: createThemeVar("backgroundColor-content-WindowsAppFrame"); 11 | 12 | .toolbar { 13 | display: flex; 14 | justify-content: space-between; 15 | align-items: center; 16 | height: 28px; 17 | } 18 | 19 | .topInfo { 20 | display: flex; 21 | flex-grow: 1; 22 | align-items: center; 23 | } 24 | 25 | .uicon { 26 | height: 100%; 27 | display: flex; 28 | align-items: center; 29 | } 30 | 31 | .appFullName { 32 | font-size: 0.75rem; 33 | } 34 | 35 | .actbtns { 36 | display: flex; 37 | align-items: center; 38 | 39 | height: 100%; 40 | 41 | .uicon { 42 | height: 100%; 43 | padding: 0 18px; 44 | transition: all ease-in-out 60ms; 45 | 46 | img { 47 | transition: all ease-in-out 60ms; 48 | } 49 | 50 | &:hover { 51 | background: rgba(136, 136, 136, 0.2); 52 | } 53 | 54 | &.closeBtn:hover { 55 | background: rgba(255, 0, 0, 0.8); 56 | img { 57 | filter: invert(1); 58 | } 59 | } 60 | } 61 | } 62 | 63 | .prtclk { 64 | cursor: pointer; 65 | } 66 | 67 | 68 | .closeBtn { 69 | cursor: pointer; 70 | margin-right: 0; 71 | } 72 | 73 | .notepad { 74 | resize: both; 75 | overflow: auto; 76 | display: flex; 77 | flex-direction: column; 78 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); 79 | border-radius: 0.375rem; 80 | width: 55rem; 81 | height: 32rem; 82 | min-width: 400px; 83 | min-height: 300px; 84 | 85 | .windowScreen { 86 | display: flex; 87 | flex-direction: column; 88 | flex: 1; 89 | min-height: 0; 90 | } 91 | 92 | .topBar { 93 | display: flex; 94 | font-size: 0.75rem; 95 | padding-top: 0.5rem; 96 | padding-bottom: 0.5rem; 97 | } 98 | 99 | .topBarItem { 100 | margin-left: 0.5rem; 101 | margin-right: 0.5rem; 102 | } 103 | 104 | .topBarItem + .topBarItem { 105 | margin-left: 1rem; 106 | margin-right: 1rem; 107 | } 108 | 109 | .restWindow { 110 | min-height: 0; 111 | flex-grow: 1; 112 | background-color: $backgroundColor-content-WindowsAppFrame; 113 | overflow: auto; 114 | } 115 | 116 | .noteText { 117 | min-height: 0; 118 | scrollbar-width: thin; 119 | scrollbar-color: #c0c0c0 #f9f9f9; 120 | padding: t.$space-4; 121 | gap: t.$space-4; 122 | display: flex; 123 | flex-direction: column; 124 | } 125 | } 126 | 127 | 128 | // --- We export the theme variables to add them to the component renderer 129 | :export{ 130 | themeVars: t.json-stringify($themeVars) 131 | } ``` -------------------------------------------------------------------------------- /xmlui/src/abstractions/scripting/Token.ts: -------------------------------------------------------------------------------- ```typescript 1 | // Represents a generic token 2 | export type Token = { 3 | // The raw text of the token 4 | readonly text: string; 5 | 6 | // The type of the token 7 | readonly type: TokenType; 8 | 9 | // The location of the token 10 | readonly location: TokenLocation; 11 | }; 12 | 13 | // Represents the location of a token 14 | export interface TokenLocation { 15 | // Start position in the source stream 16 | readonly startPosition: number; 17 | 18 | // End position (exclusive) in the source stream 19 | readonly endPosition: number; 20 | 21 | // Start line number 22 | readonly startLine: number; 23 | 24 | // End line number of the token 25 | readonly endLine: number; 26 | 27 | // Start column number of the token 28 | readonly startColumn: number; 29 | 30 | // End column number of the token 31 | readonly endColumn: number; 32 | } 33 | 34 | // Token types available for parsing 35 | // Using declare enum to make this a type-only declaration 36 | export declare enum TokenType { 37 | Eof = -1, 38 | Ws = -2, 39 | BlockComment = -3, 40 | EolComment = -4, 41 | Unknown = 0, 42 | 43 | // --- Binding Expression specific tokens 44 | LParent, 45 | RParent, 46 | 47 | Identifier, 48 | 49 | Exponent, 50 | Divide, 51 | Multiply, 52 | Remainder, 53 | Plus, 54 | Minus, 55 | BitwiseXor, 56 | BitwiseOr, 57 | LogicalOr, 58 | BitwiseAnd, 59 | LogicalAnd, 60 | IncOp, 61 | DecOp, 62 | Assignment, 63 | 64 | AddAssignment, 65 | SubtractAssignment, 66 | ExponentAssignment, 67 | MultiplyAssignment, 68 | DivideAssignment, 69 | RemainderAssignment, 70 | ShiftLeftAssignment, 71 | ShiftRightAssignment, 72 | SignedShiftRightAssignment, 73 | BitwiseAndAssignment, 74 | BitwiseXorAssignment, 75 | BitwiseOrAssignment, 76 | LogicalAndAssignment, 77 | LogicalOrAssignment, 78 | NullCoalesceAssignment, 79 | 80 | Semicolon, 81 | Comma, 82 | Colon, 83 | LSquare, 84 | RSquare, 85 | QuestionMark, 86 | NullCoalesce, 87 | OptionalChaining, 88 | BinaryNot, 89 | LBrace, 90 | RBrace, 91 | Equal, 92 | StrictEqual, 93 | LogicalNot, 94 | NotEqual, 95 | StrictNotEqual, 96 | LessThan, 97 | LessThanOrEqual, 98 | ShiftLeft, 99 | GreaterThan, 100 | GreaterThanOrEqual, 101 | ShiftRight, 102 | SignedShiftRight, 103 | Dot, 104 | Spread, 105 | Global, 106 | Backtick, 107 | DollarLBrace, 108 | Arrow, 109 | 110 | DecimalLiteral, 111 | HexadecimalLiteral, 112 | BinaryLiteral, 113 | RealLiteral, 114 | StringLiteral, 115 | Infinity, 116 | NaN, 117 | True, 118 | False, 119 | 120 | Typeof, 121 | Null, 122 | Undefined, 123 | In, 124 | 125 | Let, 126 | Const, 127 | Var, 128 | If, 129 | Else, 130 | Return, 131 | Break, 132 | Continue, 133 | Do, 134 | While, 135 | For, 136 | Of, 137 | Try, 138 | Catch, 139 | Finally, 140 | Throw, 141 | Switch, 142 | Case, 143 | Default, 144 | Delete, 145 | Function, 146 | Export, 147 | Import, 148 | As, 149 | From, 150 | } 151 | ``` -------------------------------------------------------------------------------- /packages/xmlui-os-frames/src/MacOSAppFrame.module.scss: -------------------------------------------------------------------------------- ```scss 1 | @use "xmlui/themes.scss" as t; 2 | 3 | // --- This code snippet is required to collect the theme variables used in this module 4 | $themeVars: (); 5 | @function createThemeVar($componentVariable) { 6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global; 7 | @return t.getThemeVar($themeVars, $componentVariable); 8 | } 9 | 10 | .app { 11 | --elevated-shadow: 0px 8.5px 10px #0000001d, 0px 68px 80px #0000003b; 12 | will-change: width, height; 13 | box-shadow: var(--elevated-shadow); 14 | cursor: var(--system-cursor-default), auto; 15 | border-radius: .75rem; 16 | grid-template-rows: 1fr; 17 | display: grid; 18 | width: 50rem; 19 | height: 30rem; 20 | z-index: 14; 21 | touch-action: none; 22 | position: relative; 23 | resize: both; 24 | overflow: auto; 25 | } 26 | 27 | .appButtons { 28 | position: absolute; 29 | top: 1rem; 30 | left: 1rem; 31 | box-shadow: none !important; 32 | z-index: var(--system-z-index-window-traffic-lights); 33 | } 34 | 35 | .appButtonsInner { 36 | --button-size: .8rem; 37 | grid-template-columns: repeat(3, var(--button-size)); 38 | align-items: center; 39 | gap: .6rem; 40 | height: 100%; 41 | display: grid; 42 | 43 | svg{ 44 | opacity: 0; 45 | } 46 | 47 | &:hover{ 48 | svg{ 49 | opacity: 1; 50 | } 51 | } 52 | } 53 | 54 | .button{ 55 | height: var(--button-size); 56 | width: var(--button-size); 57 | background-color: var(--bgcolor); 58 | box-shadow: 0 0 0 .5px var(--border-color); 59 | border-radius: 50%; 60 | transition: transform .1s ease-in; 61 | display: flex; 62 | justify-content: center; 63 | align-items: center; 64 | } 65 | 66 | .close{ 67 | --bgcolor: #ff5f56; 68 | --border-color: #e0443e; 69 | } 70 | 71 | .minimize{ 72 | --bgcolor: #ffbd2e; 73 | --border-color: #dea123; 74 | } 75 | 76 | .stretch{ 77 | --bgcolor: #27c93f; 78 | --border-color: #1aab29; 79 | } 80 | 81 | 82 | .container { 83 | background-color: t.$backgroundColor; 84 | border-radius: inherit; 85 | grid-template-rows: auto 1fr; 86 | min-height: auto; 87 | max-height: 100%; 88 | display: grid; 89 | overflow-y: hidden; 90 | height: 100% !important; 91 | } 92 | 93 | 94 | .titleBar { 95 | border-bottom: solid .9px hsla(var(--system-color-dark-hsl), .3); 96 | justify-content: center; 97 | width: 100%; 98 | padding: .9rem 1rem; 99 | display: flex; 100 | } 101 | 102 | .mainArea { 103 | color: var(--system-color-light-contrast); 104 | flex-direction: column; 105 | width: 100%; 106 | height: 100%; 107 | font-size: 1rem; 108 | display: flex; 109 | overflow-y: auto; 110 | padding: t.$space-4; 111 | gap: t.$space-4; 112 | } 113 | 114 | // --- We export the theme variables to add them to the component renderer 115 | :export{ 116 | themeVars: t.json-stringify($themeVars) 117 | } ``` -------------------------------------------------------------------------------- /xmlui/src/components/AutoComplete/AutoComplete.md: -------------------------------------------------------------------------------- ```markdown 1 | %-DESC-START 2 | 3 | **Key features:** 4 | - **Type-ahead filtering**: Users can type to narrow down options in real-time 5 | - **Multi-select support**: Set `multi="true"` to allow selecting multiple items 6 | - **Custom option creation**: Enable `creatable="true"` to let users add new options 7 | - **Rich customization**: Use `optionTemplate` to create complex option layouts 8 | 9 | ## Using AutoComplete 10 | 11 | ```xmlui-pg copy display height="200px" name="Example: Using AutoComplete" 12 | <App> 13 | <AutoComplete> 14 | <Option value="1" label="Bruce Wayne" /> 15 | <Option value="2" label="Clark Kent" enabled="false" /> 16 | <Option value="3" label="Diana Prince" /> 17 | </AutoComplete> 18 | </App> 19 | ``` 20 | 21 | %-DESC-END 22 | 23 | %-PROP-START emptyListTemplate 24 | 25 | ```xmlui-pg copy display height="200px" name="Example: emptyListTemplate" 26 | <App> 27 | <AutoComplete> 28 | <property name="emptyListTemplate"> 29 | <Text>No options found</Text> 30 | </property> 31 | </AutoComplete> 32 | </App> 33 | ``` 34 | 35 | %-PROP-END 36 | 37 | %-PROP-START multi 38 | 39 | ```xmlui-pg copy display height="300px" name="Example: multi" 40 | <App> 41 | <AutoComplete multi="true"> 42 | <Option value="1" label="Bruce Wayne" /> 43 | <Option value="2" label="Clark Kent" /> 44 | <Option value="3" label="Diana Prince" /> 45 | <Option value="4" label="Barry Allen" /> 46 | <Option value="5" label="Hal Jordan" /> 47 | </AutoComplete> 48 | </App> 49 | ``` 50 | 51 | %-PROP-END 52 | 53 | %-PROP-START optionTemplate 54 | 55 | ```xmlui-pg copy display height="300px" name="Example: optionTemplate" 56 | <App> 57 | <AutoComplete multi="true"> 58 | <property name="optionTemplate"> 59 | <Text textAlign="center" color="red">{$item.label}</Text> 60 | </property> 61 | <Option value="1" label="Bruce Wayne" /> 62 | <Option value="2" label="Clark Kent" /> 63 | <Option value="3" label="Diana Prince" /> 64 | </AutoComplete> 65 | </App> 66 | ``` 67 | 68 | %-PROP-END 69 | 70 | %-EVENT-START itemCreated 71 | 72 | Add a few new items not in the options list. The following markup will display them: 73 | 74 | ```xmlui-pg copy display height="300px" name="Example: itemCreated" 75 | <App var.newItems="{[]}"> 76 | <AutoComplete 77 | id="autoComplete" 78 | creatable="true" 79 | onItemCreated="item => newItems.push(item)"> 80 | <Option value="1" label="Bruce Wayne" /> 81 | <Option value="2" label="Clark Kent" /> 82 | </AutoComplete> 83 | <Text testId="text"> 84 | New items: {newItems.join(", ")} 85 | </Text> 86 | </App> 87 | ``` 88 | 89 | %-EVENT-END 90 | ``` -------------------------------------------------------------------------------- /xmlui/src/abstractions/_conventions.md: -------------------------------------------------------------------------------- ```markdown 1 | # TypeScript Abstractions Conventions 2 | 3 | This document outlines best practices and conventions for working with TypeScript abstractions in the XMLUI project. Following these conventions ensures that files in the abstractions folder only contain type definitions and don't generate JavaScript code when compiled. 4 | 5 | ## Core Principles 6 | 7 | The abstractions folder is intended to contain **type definitions only** with no runtime JavaScript code generation. 8 | 9 | ## Conventions for Type Definitions 10 | 11 | ### 1. Type-Only Constructs 12 | 13 | Use these TypeScript constructs in abstractions files: 14 | - `interface` for defining object shapes 15 | - `type` for type aliases and unions 16 | - `declare` keyword for values that must exist at runtime 17 | 18 | Avoid these constructs that generate JavaScript: 19 | - `class` declarations 20 | - `function` declarations 21 | - `const`, `let`, or `var` declarations without `declare` 22 | - Regular `enum` declarations 23 | 24 | ### 2. Implementation Patterns 25 | 26 | #### Pattern A: Type-Only Declarations 27 | Use the `declare` keyword to indicate that a value exists at runtime but its implementation is elsewhere: 28 | ```typescript 29 | export declare const MY_CONSTANT: 42; 30 | export declare enum MyEnum { Value1, Value2 } 31 | export declare function myFunction(): void; 32 | ``` 33 | 34 | #### Pattern B: Implementation Separation 35 | Place implementation files outside the abstractions folder: 36 | ```typescript 37 | // In abstractions/MyTypes.ts 38 | export type MyType = { prop: string }; 39 | 40 | // In implementation/MyImplementation.ts 41 | import type { MyType } from "../abstractions/MyTypes"; 42 | export const createMyType = (): MyType => ({ prop: "value" }); 43 | ``` 44 | 45 | ### 3. Import Patterns 46 | 47 | - Always use `import type` for type imports in abstractions: 48 | ```typescript 49 | import type { SomeType } from "./OtherFile"; 50 | ``` 51 | 52 | ## Common Scenarios 53 | 54 | 1. **Constants**: Use `declare const` in abstractions 55 | 2. **Enums**: Use `declare enum` or type unions 56 | 3. **Functions**: Use `declare function` or move implementation outside 57 | 4. **Classes**: Use interfaces instead or move implementation outside 58 | 59 | ## Benefits 60 | 61 | - Clear separation between types and implementation 62 | - Improved maintainability and refactoring 63 | - No runtime overhead from abstraction files 64 | - Better TypeScript compilation performance 65 | 66 | By following these conventions, you help maintain a clean architecture with proper separation between type definitions and runtime implementation. 67 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/abstractions/ComponentRenderer.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { Dispatch, MutableRefObject, RefObject } from "react"; 2 | 3 | import type { AppContextObject } from "../../abstractions/AppContextDefs"; 4 | import type { LookupAsyncFnInner, LookupSyncFnInner } from "../../abstractions/ActionDefs"; 5 | import type { CodeDeclaration } from "../script-runner/ScriptingSourceTree"; 6 | import type { ComponentMetadata, ParentRenderContext } from "../../abstractions/ComponentDefs"; 7 | import type { ComponentRendererContextBase } from "../../abstractions/RendererDefs"; 8 | import type { 9 | ComponentApi, 10 | ContainerState, 11 | RegisterComponentApiFnInner, 12 | } from "../rendering/ContainerWrapper"; 13 | import type { ContainerAction } from "../rendering/containers"; 14 | 15 | // This interface defines the renderer context for the XMLUI core framework 16 | // components. Its implementations are used only within the component core. 17 | export interface InnerRendererContext<T extends ComponentMetadata = ComponentMetadata> 18 | extends ComponentRendererContextBase<T> { 19 | // The dispatcher function to change the state of the component 20 | dispatch: ContainerDispatcher; 21 | 22 | // The function to register a component API 23 | registerComponentApi: RegisterComponentApiFnInner; 24 | 25 | // The function to obtain a synchronous action handler 26 | lookupSyncCallback: LookupSyncFnInner; 27 | 28 | // The function to obtain an async action handler 29 | lookupAction: LookupAsyncFnInner; 30 | 31 | // The memoized variables (with their values) used in the component 32 | memoedVarsRef: MutableRefObject<MemoedVars>; 33 | 34 | parentRenderContext?: ParentRenderContext; 35 | 36 | uidInfoRef?: RefObject<Record<string, any>>; 37 | } 38 | 39 | // This property is a redux-style dispatcher that manages state changes in a container. 40 | export type ContainerDispatcher = Dispatch<ContainerAction>; 41 | 42 | // This type represents a map of objects providing access to memoed variables within the 43 | // container of a particular component. The key is an expression; the value is an 44 | // accessor object. 45 | export type MemoedVars = Map< 46 | any, 47 | { 48 | getDependencies: ( 49 | value: string | CodeDeclaration, 50 | referenceTrackedApi: Record<string, ComponentApi>, 51 | ) => Array<string>; 52 | obtainValue: ( 53 | expression: any, 54 | state: ContainerState, 55 | appContext: AppContextObject | undefined, 56 | strict: boolean | undefined, 57 | stateDeps: Record<string, any>, 58 | appContextDeps: Record<string, any>, 59 | ) => any; 60 | } 61 | >; 62 | ``` -------------------------------------------------------------------------------- /xmlui/src/components/Avatar/AvatarNative.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { CSSProperties, Ref } from "react"; 2 | import { forwardRef, memo, useMemo } from "react"; 3 | import classnames from "classnames"; 4 | 5 | import styles from "./Avatar.module.scss"; 6 | 7 | type Props = { 8 | size?: string; 9 | url?: string; 10 | name?: string; 11 | style?: CSSProperties; 12 | className?: string; 13 | } & Pick<React.HTMLAttributes<HTMLDivElement>, "onClick">; 14 | 15 | export const defaultProps: Pick<Props, "size"> = { 16 | size: "sm", 17 | }; 18 | 19 | export const Avatar = memo(forwardRef(function Avatar( 20 | { size = defaultProps.size, url, name, style, className, onClick, ...rest }: Props, 21 | ref: Ref<any>, 22 | ) { 23 | // Memoize the abbreviated name calculation to avoid recalculation on every render 24 | const abbreviatedName = useMemo(() => abbrevName(name ?? null), [name]); 25 | 26 | // Handle keyboard events for accessibility 27 | const handleKeyDown = (event: React.KeyboardEvent) => { 28 | if (onClick && (event.key === 'Enter' || event.key === ' ')) { 29 | event.preventDefault(); 30 | onClick(event as any); 31 | } 32 | }; 33 | 34 | // Simplified className generation by directly mapping size to styles 35 | const commonClassNames = classnames( 36 | className, 37 | styles.container, 38 | styles[size as keyof typeof styles] || styles.sm, // Fallback to sm if size not found 39 | { [styles.clickable]: !!onClick } 40 | ); 41 | const altTxt = !!name ? `Avatar of ${name}` : "Avatar"; 42 | 43 | if (url) { 44 | return ( 45 | <img 46 | {...rest} 47 | ref={ref} 48 | src={url} 49 | alt={altTxt} 50 | className={commonClassNames} 51 | style={style} 52 | onClick={onClick} 53 | onKeyDown={handleKeyDown} 54 | tabIndex={onClick ? 0 : undefined} 55 | /> 56 | ); 57 | } else 58 | return ( 59 | <div 60 | {...rest} 61 | ref={ref} 62 | className={commonClassNames} 63 | style={style} 64 | onClick={onClick} 65 | onKeyDown={handleKeyDown} 66 | role="img" 67 | aria-label={altTxt} 68 | tabIndex={onClick ? 0 : undefined} 69 | > 70 | {abbreviatedName || <span aria-hidden="true"></span>} 71 | {/* Display initials or an empty decorative span */} 72 | </div> 73 | ); 74 | })); 75 | 76 | function abbrevName(name: string | null): string | null { 77 | if (!!name) { 78 | const abbrev = name 79 | .trim() 80 | .split(" ") 81 | .filter((word) => !!word.trim().length) 82 | .map((word) => word[0].toUpperCase()) 83 | .slice(0, 3) 84 | .join(""); 85 | return abbrev; 86 | } 87 | return null; 88 | } 89 | ``` -------------------------------------------------------------------------------- /xmlui/src/components-core/theming/themes/palette.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const palette = { 2 | $colorSurface0: "$color-surface-0", 3 | $colorSurface50: "$color-surface-50", 4 | $colorSurface100: "$color-surface-100", 5 | $colorSurface200: "$color-surface-200", 6 | $colorSurface200A70: "rgb(from $color-surface-200 r g b / 0.7)", 7 | $colorSurface200A80: "rgb(from $color-surface-200 r g b / 0.8)", 8 | $colorSurface300: "$color-surface-300", 9 | $colorSurface400A80: "rgb(from $color-surface-400 r g b / 0.8)", 10 | $colorSurface400: "$color-surface-400", 11 | $colorSurface500: "$color-surface-500", 12 | $colorSurface500A80: "rgba($color-surface-500, .8)", 13 | $colorSurface500A60: "rgba($color-surface-500, .6)", 14 | $colorSurface600: "$color-surface-600", 15 | $colorSurface700: "$color-surface-700", 16 | $colorSurface700A30: "rgb(from $color-surface-700 r g b / 0.3)", 17 | $colorSurface700A90: "rgb(from $color-surface-700 r g b / 0.9)", 18 | $colorSurface800: "$color-surface-800", 19 | $colorSurface900: "$color-surface-900", 20 | $colorSurface950: "$color-surface-950", 21 | 22 | $colorPrimary50: "$color-primary-50", 23 | $colorPrimary100: "$color-primary-100", 24 | $colorPrimary200: "$color-primary-200", 25 | $colorPrimary300: "$color-primary-300", 26 | $colorPrimary400: "$color-primary-400", 27 | $colorPrimary500: "$color-primary-500", 28 | $colorPrimary600: "$color-primary-600", 29 | $colorPrimary700: "$color-primary-700", 30 | $colorPrimary800: "$color-primary-800", 31 | $colorPrimary900: "$color-primary-900", 32 | $colorPrimary950: "$color-primary-950", 33 | 34 | $colorSecondary50: "$color-secondary-50", 35 | $colorSecondary100: "$color-secondary-100", 36 | $colorSecondary200: "$color-secondary-200", 37 | $colorSecondary400: "$color-secondary-400", 38 | $colorSecondary500: "$color-secondary-500", 39 | $colorSecondary600: "$color-secondary-600", 40 | $colorSecondary900: "$color-secondary-900", 41 | $colorSecondary950: "$color-secondary-950", 42 | 43 | $colorWarn700: "$color-warn-700", 44 | $colorWarn950: "$color-warn-950", 45 | 46 | $colorDanger50: "$color-danger-50", 47 | $colorDanger100: "$color-danger-100", 48 | $colorDanger400: "$color-danger-400", 49 | $colorDanger500: "$color-danger-500", 50 | $colorDanger600: "$color-danger-600", 51 | $colorDanger700: "$color-danger-700", 52 | $colorDanger900: "$color-danger-900", 53 | $colorDanger950: "$color-danger-950", 54 | 55 | $colorSuccess600: "$color-success-600", 56 | $colorSuccess950: "$color-success-950", 57 | 58 | $colorInfo600: "$color-info-600", 59 | $colorInfo800: "$color-info-700", 60 | }; 61 | ``` -------------------------------------------------------------------------------- /xmlui/tests/components-core/scripts-runner/process-implicit-context.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it } from "vitest"; 2 | 3 | import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; 4 | import { createEvalContext, parseStatements } from "./test-helpers"; 5 | import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; 6 | 7 | describe("Process implicit context (exp)", () => { 8 | it("Implicit context #1", async () => { 9 | // --- Arrange 10 | const source = "x = Impl.myCalc(23);"; 11 | 12 | const Impl: any = { 13 | _SUPPORT_IMPLICIT_CONTEXT: true, 14 | _GET_CONTEXT: () => 100, 15 | myCalc: (context: any, arg: any) => context + arg 16 | }; 17 | 18 | const evalContext = createEvalContext({ 19 | localContext: { 20 | x: 1 21 | }, 22 | appContext: { 23 | Impl 24 | }, 25 | implicitContextGetter: (obj: any) => obj._GET_CONTEXT() 26 | }); 27 | const statements = parseStatements(source); 28 | 29 | // --- Act 30 | const diag = await processStatementQueueAsync(statements, evalContext); 31 | 32 | // --- Assert 33 | expect(evalContext.mainThread!.blocks!.length).equal(1); 34 | expect(evalContext.localContext.x).equal(123); 35 | 36 | expect(diag.processedStatements).equal(1); 37 | expect(diag.maxLoops).equal(0); 38 | expect(diag.maxBlocks).equal(1); 39 | expect(diag.maxQueueLength).equal(1); 40 | expect(diag.clearToLabels).equal(0); 41 | expect(diag.unshiftedItems).equal(0); 42 | }); 43 | 44 | it("Implicit context (sync) # ", () => { 45 | // --- Arrange 46 | const source = "x = Impl.myCalc(23);"; 47 | 48 | const Impl: any = { 49 | _SUPPORT_IMPLICIT_CONTEXT: true, 50 | _GET_CONTEXT: () => 100, 51 | myCalc: (context: any, arg: any) => context + arg 52 | }; 53 | 54 | const evalContext = createEvalContext({ 55 | localContext: { 56 | x: 1 57 | }, 58 | appContext: { 59 | Impl 60 | }, 61 | implicitContextGetter: (obj: any) => obj._GET_CONTEXT() 62 | }); 63 | const statements = parseStatements(source); 64 | 65 | // --- Act 66 | const diag = processStatementQueue(statements, evalContext); 67 | 68 | // --- Assert 69 | expect(evalContext.mainThread!.blocks!.length).equal(1); 70 | expect(evalContext.localContext.x).equal(123); 71 | 72 | expect(diag.processedStatements).equal(1); 73 | expect(diag.maxLoops).equal(0); 74 | expect(diag.maxBlocks).equal(1); 75 | expect(diag.maxQueueLength).equal(1); 76 | expect(diag.clearToLabels).equal(0); 77 | expect(diag.unshiftedItems).equal(0); 78 | }); 79 | }); 80 | ``` -------------------------------------------------------------------------------- /xmlui/tests/parsers/scripting/process-implicit-context.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { describe, expect, it } from "vitest"; 2 | 3 | import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync"; 4 | import { createEvalContext, parseStatements } from "./test-helpers"; 5 | import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async"; 6 | 7 | describe("Process implicit context", () => { 8 | it("Implicit context #1", async () => { 9 | // --- Arrange 10 | const source = "x = Impl.myCalc(23);"; 11 | 12 | const Impl: any = { 13 | _SUPPORT_IMPLICIT_CONTEXT: true, 14 | _GET_CONTEXT: () => 100, 15 | myCalc: (context: any, arg: any) => context + arg, 16 | }; 17 | 18 | const evalContext = createEvalContext({ 19 | localContext: { 20 | x: 1, 21 | }, 22 | appContext: { 23 | Impl, 24 | }, 25 | implicitContextGetter: (obj: any) => obj._GET_CONTEXT(), 26 | }); 27 | const statements = parseStatements(source); 28 | 29 | // --- Act 30 | const diag = await processStatementQueueAsync(statements, evalContext); 31 | 32 | // --- Assert 33 | expect(evalContext.mainThread!.blocks!.length).equal(1); 34 | expect(evalContext.localContext.x).equal(123); 35 | 36 | expect(diag.processedStatements).equal(1); 37 | expect(diag.maxLoops).equal(0); 38 | expect(diag.maxBlocks).equal(1); 39 | expect(diag.maxQueueLength).equal(1); 40 | expect(diag.clearToLabels).equal(0); 41 | expect(diag.unshiftedItems).equal(0); 42 | }); 43 | 44 | it("Implicit context (sync) # ", () => { 45 | // --- Arrange 46 | const source = "x = Impl.myCalc(23);"; 47 | 48 | const Impl: any = { 49 | _SUPPORT_IMPLICIT_CONTEXT: true, 50 | _GET_CONTEXT: () => 100, 51 | myCalc: (context: any, arg: any) => context + arg, 52 | }; 53 | 54 | const evalContext = createEvalContext({ 55 | localContext: { 56 | x: 1, 57 | }, 58 | appContext: { 59 | Impl, 60 | }, 61 | implicitContextGetter: (obj: any) => obj._GET_CONTEXT(), 62 | }); 63 | const statements = parseStatements(source); 64 | 65 | // --- Act 66 | const diag = processStatementQueue(statements, evalContext); 67 | 68 | // --- Assert 69 | expect(evalContext.mainThread!.blocks!.length).equal(1); 70 | expect(evalContext.localContext.x).equal(123); 71 | 72 | expect(diag.processedStatements).equal(1); 73 | expect(diag.maxLoops).equal(0); 74 | expect(diag.maxBlocks).equal(1); 75 | expect(diag.maxQueueLength).equal(1); 76 | expect(diag.clearToLabels).equal(0); 77 | expect(diag.unshiftedItems).equal(0); 78 | }); 79 | }); 80 | ```