This is page 12 of 179. Use http://codebase.md/xmlui-org/xmlui/%7Bimage%7D?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ ├── silver-llamas-cough.md
│ └── true-jeans-agree.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
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── HelloMd.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── control-cache-invalidation.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── tsconfig.json
│ ├── 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
│ ├── 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
│ │ └── 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
│ ├── 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
│ ├── 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
│ ├── 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
│ ├── 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
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Spreadsheet.tsx
│ │ └── SpreadsheetNative.tsx
│ └── 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.spec.ts
│ │ ├── HeroSection.tsx
│ │ └── HeroSectionNative.tsx
│ ├── index.tsx
│ ├── ScrollToTop
│ │ ├── ScrollToTop.module.scss
│ │ ├── ScrollToTop.tsx
│ │ └── ScrollToTopNative.tsx
│ └── vite-env.d.ts
├── playwright.config.ts
├── 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.cjs
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── 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.js
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ ├── LabelListNative.module.scss
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── 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
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.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
--------------------------------------------------------------------------------
/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 |
```
--------------------------------------------------------------------------------
/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 |
```
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { defineConfig, devices } from "@playwright/test";
2 | import path from "path";
3 |
4 | /**
5 | * Read environment variables from file.
6 | * https://github.com/motdotla/dotenv
7 | */
8 | // import dotenv from 'dotenv';
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 |
17 | // Default to using dev server unless explicitly set to false
18 | const useDevServer = process.env.PLAYWRIGHT_USE_DEV_SERVER !== "false";
19 | const CI = process.env.CI;
20 |
21 | export default defineConfig({
22 | /* Run tests in files in parallel */
23 | fullyParallel: true,
24 | testMatch: "*.spec.ts",
25 | /* Fail the build on CI if you accidentally left test.only in the source code. */
26 | forbidOnly: !!CI,
27 |
28 | workers: CI ? "100%" : "75%",
29 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */
30 | reporter: CI ? [["github"], ["html"]] : [["html", { open: "never" }]],
31 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
32 | use: {
33 | ...devices["Desktop Chrome"],
34 | channel: "chromium",
35 | /* Base URL to use in actions like `await page.goto('/')`. */
36 | baseURL: `http://localhost:${port}`,
37 |
38 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
39 | trace: "on-first-retry",
40 | serviceWorkers: "allow",
41 | /* Grants specified permissions to the browser context. */
42 | permissions: ["clipboard-read", "clipboard-write"],
43 | },
44 |
45 | retries: CI ? 2 : 1,
46 | /* Configure projects for major browsers */
47 | projects: [
48 | {
49 | name: "xmlui-nonsmoke",
50 | testDir: "./xmlui",
51 | grepInvert: /@smoke/,
52 | },
53 | {
54 | name: "xmlui-smoke",
55 | testDir: "./xmlui",
56 | grep: /@smoke/,
57 | },
58 | {
59 | name: "extensions-nonsmoke",
60 | testDir: "./packages",
61 | grepInvert: /@smoke/,
62 | },
63 | {
64 | name: "extensions-smoke",
65 | testDir: "./packages",
66 | grep: /@smoke/,
67 | },
68 | ],
69 |
70 | /* Run your local dev server before starting the tests */
71 | webServer: {
72 | command:
73 | useDevServer && !CI
74 | ? `cd xmlui && npm run start-test-bed -- --port ${port}`
75 | : `npx serve xmlui/src/testing/infrastructure/dist -p ${port}`,
76 | timeout: 50 * 1000,
77 | port,
78 | reuseExistingServer: !CI,
79 | cwd: path.resolve(__dirname),
80 | },
81 | });
82 |
```
--------------------------------------------------------------------------------
/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 |
```
--------------------------------------------------------------------------------
/packages/xmlui-devtools/src/devtools/utils.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { uint8ArrayToBase64 } from "../../../../xmlui/src/components-core/utils/base64-utils";
2 |
3 | /**
4 | * Convert a string to its UTF-8 bytes and compress it.
5 | *
6 | * @param {string} str
7 | * @returns {Promise<Uint8Array>}
8 | */
9 | async function compress(str: string) {
10 | // Convert the string to a byte stream.
11 | const stream = new Blob([str]).stream();
12 |
13 | // Create a compressed stream.
14 | const compressedStream = stream.pipeThrough(
15 | new CompressionStream("gzip")
16 | );
17 |
18 | // Convert the string to a byte stream.
19 | const reader = compressedStream.getReader();
20 | const chunks = [];
21 | while (true) {
22 | const { done, value } = await reader.read();
23 | if (done) break;
24 | chunks.push(value);
25 | }
26 |
27 | return await concatUint8Arrays(chunks);
28 | }
29 |
30 | /**
31 | * Decompress bytes into a UTF-8 string.
32 | *
33 | * @param {Uint8Array} compressedBytes
34 | * @returns {Promise<string>}
35 | */
36 | async function decompress(compressedBytes: Uint8Array) {
37 | // Convert the bytes to a stream.
38 | const stream = new Blob([new Uint8Array(compressedBytes)]).stream();
39 |
40 | // Create a decompressed stream.
41 | const decompressedStream = stream.pipeThrough(
42 | new DecompressionStream("gzip")
43 | );
44 |
45 | // Convert the string to a byte stream.
46 | const reader = decompressedStream.getReader();
47 | const chunks = [];
48 | while (true) {
49 | const { done, value } = await reader.read();
50 | if (done) break;
51 | chunks.push(value);
52 | }
53 |
54 | const stringBytes = await concatUint8Arrays(chunks);
55 |
56 | // Convert the bytes to a string.
57 | return new TextDecoder().decode(stringBytes);
58 | }
59 |
60 | /**
61 | * Combine multiple Uint8Arrays into one.
62 | */
63 | async function concatUint8Arrays(uint8arrays: ReadonlyArray<Uint8Array>) {
64 | // Convert each Uint8Array into an ArrayBufferView (copy if necessary) so it matches BlobPart[] typing.
65 | const parts: BlobPart[] = uint8arrays.map(u => new Uint8Array(u));
66 | const blob = new Blob(parts);
67 | const buffer = await blob.arrayBuffer();
68 | return new Uint8Array(buffer);
69 | }
70 |
71 | async function createQueryString(target: any) {
72 | // Convert the Uint8Array to a Base64 string.
73 |
74 | const compressed = await compress(target);
75 | const base64 = uint8ArrayToBase64(compressed);
76 |
77 | // Create a query string.
78 | return encodeURIComponent(base64);
79 | }
80 |
81 | export { compress, decompress, createQueryString };
82 |
```
--------------------------------------------------------------------------------
/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 |
```
--------------------------------------------------------------------------------
/tools/vscode/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "xmlui-vscode",
3 | "displayName": "xmlui",
4 | "description": "XMLUI language support",
5 | "icon": "resources/xmlui-logo.png",
6 | "author": "xmlui.org",
7 | "publisher": "xmlui",
8 | "license": "MIT",
9 | "version": "0.10.26",
10 | "private": true,
11 | "categories": [],
12 | "keywords": [],
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/xmlui-com/xmlui"
16 | },
17 | "engines": {
18 | "vscode": "^1.75.0"
19 | },
20 | "activationEvents": [
21 | "onLanguage:xmlui"
22 | ],
23 | "vsce": {
24 | "dependencies": false
25 | },
26 | "main": "./dist/extension",
27 | "contributes": {
28 | "configuration": {
29 | "type": "object",
30 | "title": "XMLUI configuration",
31 | "properties": {
32 | "XMLUILanguageService.trace.server": {
33 | "scope": "window",
34 | "type": "string",
35 | "enum": [
36 | "off",
37 | "messages",
38 | "verbose"
39 | ],
40 | "default": "off",
41 | "description": "Traces the communication between VS Code and the language server."
42 | }
43 | }
44 | },
45 | "languages": [
46 | {
47 | "id": "xmlui",
48 | "aliases": [
49 | "XMLUI",
50 | "Xmlui"
51 | ],
52 | "extensions": [
53 | ".xmlui"
54 | ]
55 | },
56 | {
57 | "id": "javascript",
58 | "aliases": [
59 | "XS",
60 | "xs"
61 | ],
62 | "extensions": [
63 | ".xs"
64 | ]
65 | }
66 | ],
67 | "grammars": [
68 | {
69 | "language": "xmlui",
70 | "scopeName": "source.xmlui",
71 | "path": "./syntaxes/xmlui.tmLanguage.json",
72 | "embeddedLanguages": {
73 | "meta.embedded.block.javascrip": "javascript"
74 | }
75 | }
76 | ]
77 | },
78 | "scripts": {
79 | "build:vsix": "vsce package",
80 | "build": "node esbuild.js --production",
81 | "build:all": "./build.sh",
82 | "type-check": "tsc --noEmit",
83 | "watch": "npm-run-all -p watch:*",
84 | "watch:esbuild": "node esbuild.js --watch",
85 | "watch:tsc": "tsc --noEmit --watch --project tsconfig.json"
86 | },
87 | "dependencies": {
88 | "vscode-languageclient": "^9.0.1",
89 | "xmlui": "*"
90 | },
91 | "devDependencies": {
92 | "@eslint/js": "^9.13.0",
93 | "@stylistic/eslint-plugin": "^2.9.0",
94 | "@types/node": "^20",
95 | "@types/vscode": "^1.75.1",
96 | "@vscode/vsce": "^3.6.0",
97 | "esbuild": "^0.25.1",
98 | "eslint": "^9.13.0",
99 | "npm-run-all": "^4.1.5",
100 | "typescript": "^5.7.2",
101 | "typescript-eslint": "^8.16.0"
102 | }
103 | }
104 |
```
--------------------------------------------------------------------------------
/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 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/FormSection/FormSection.md:
--------------------------------------------------------------------------------
```markdown
1 | %-PROP-START columnGap
2 |
3 | ```xmlui-pg copy display name="Example: columnGap"
4 | <Form padding="1rem">
5 | <FormSection columnGap="1rem">
6 | <FormItem width="50%" label="Name" bindTo="" />
7 | <FormItem width="50%" label="Occupation" bindTo="" />
8 | </FormSection>
9 | </Form>
10 | ```
11 |
12 | %-PROP-END
13 |
14 | %-PROP-STARt rowGap
15 |
16 | ```xmlui-pg copy display name="Example: rowGap"
17 | <Form padding="1rem">
18 | <FormSection rowGap="2rem">
19 | <FormItem label="Name" bindTo="" />
20 | <FormItem label="Occupation" bindTo="" />
21 | </FormSection>
22 | </Form>
23 | ```
24 |
25 | %-PROP-END
26 |
27 | %-PROP-START info
28 |
29 | ```xmlui-pg copy display name="Example: info"
30 | <Form padding="1rem">
31 | <FormSection info="This is some information about a particular section.">
32 | <FormItem label="Input Field" bindTo="" />
33 | </FormSection>
34 | </Form>
35 | ```
36 |
37 | %-PROP-END
38 |
39 | %-PROP-START infoFontSize
40 |
41 | ```xmlui-pg copy {4} display name="Example: infoFontSize"
42 | <Form padding="1rem">
43 | <FormSection
44 | info="This is some information about a particular section."
45 | infoFontSize="18px"
46 | >
47 | <FormItem label="Input Field" bindTo="" />
48 | </FormSection>
49 | </Form>
50 | ```
51 |
52 | %-PROP-END
53 |
54 | %-PROP-START heading
55 |
56 | ```xmlui-pg copy display name="Example: heading"
57 | <Form padding="1rem">
58 | <FormSection heading="Basic Heading">
59 | <FormItem label="Input Field" bindTo="" />
60 | </FormSection>
61 | </Form>
62 | ```
63 |
64 | %-PROP-END
65 |
66 | %-PROP-START headingLevel
67 |
68 | ```xmlui-pg copy display name="Example: headingLevel"
69 | <Form padding="1rem">
70 | <FormSection heading="Basic Heading" headingLevel="h1">
71 | <FormItem label="Input Field" bindTo="" />
72 | </FormSection>
73 | </Form>
74 | ```
75 |
76 | %-PROP-END
77 |
78 | %-PROP-START headingWeight
79 |
80 | The default weight is `bold`.
81 |
82 | ```xmlui-pg copy display name="Example: headingWeight"
83 | <Form padding="1rem">
84 | <FormSection heading="Basic Heading" headingWeight="normal">
85 | <FormItem label="Input Field" bindTo="" />
86 | </FormSection>
87 | </Form>
88 | ```
89 |
90 | %-PROP-END
91 |
92 | %-PROP-START paddingBottom
93 |
94 | ```xmlui-pg copy display name="Example: paddingBottom"
95 | <Form padding="1rem">
96 | <FormSection paddingBottom="3rem" heading="Basic Info">
97 | <FormItem width="50%" label="First Name" bindTo="" />
98 | <FormItem width="50%" label="Last Name" bindTo="" />
99 | </FormSection>
100 | <FormSection paddingBottom="0" heading="Job Related">
101 | <FormItem width="50%" label="Occupation" bindTo="" />
102 | <FormItem width="50%" label="Job Description" bindTo="" />
103 | </FormSection>
104 | </Form>
105 | ```
106 |
107 | %-PROP-END
108 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Avatar/Avatar.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./Avatar.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import { sizeMd } from "../../components/abstractions";
6 | import { Avatar, defaultProps } from "./AvatarNative";
7 | import { createMetadata, d } from "../metadata-helpers";
8 |
9 | const COMP = "Avatar";
10 |
11 | export const AvatarMd = createMetadata({
12 | status: "stable",
13 | description:
14 | "`Avatar` displays a user or entity's profile picture as a circular image, " +
15 | "with automatic fallback to initials when no image is provided. It's commonly " +
16 | "used in headers, user lists, comments, and anywhere you need to represent a " +
17 | "person or organization.",
18 | props: {
19 | size: {
20 | description: `This property defines the display size of the ${COMP}.`,
21 | availableValues: sizeMd,
22 | valueType: "string",
23 | defaultValue: defaultProps.size,
24 | },
25 | name: {
26 | description:
27 | `This property sets the name value the ${COMP} uses to display initials. If neither ` +
28 | "this property nor \`url\` is defined, an empty avatar is displayed.",
29 | valueType: "string",
30 | },
31 | url: {
32 | description:
33 | `This property specifies the URL of the image to display in the ${COMP}. ` +
34 | "If neither this property nor \`name\` is defined, an empty avatar is displayed.",
35 | valueType: "string",
36 | },
37 | },
38 | events: {
39 | click: d("This event is triggered when the avatar is clicked."),
40 | },
41 | themeVars: parseScssVar(styles.themeVars),
42 | defaultThemeVars: {
43 | [`borderRadius-${COMP}`]: "4px",
44 | [`boxShadow-${COMP}`]: "inset 0 0 0 1px rgba(4,32,69,0.1)",
45 | [`textColor-${COMP}`]: "$textColor-secondary",
46 | [`fontWeight-${COMP}`]: "$fontWeight-bold",
47 | [`border-${COMP}`]: "0px solid $color-surface-400A80",
48 | [`backgroundColor-${COMP}`]: "$color-surface-100",
49 | },
50 | });
51 |
52 | export const avatarComponentRenderer = createComponentRenderer(
53 | COMP,
54 | AvatarMd,
55 | ({ node, extractValue, lookupEventHandler, className, extractResourceUrl }) => {
56 | return (
57 | <Avatar
58 | className={className}
59 | size={node.props?.size}
60 | url={node.props.url ? extractResourceUrl(node.props.url) : undefined}
61 | name={extractValue(node.props.name)}
62 | onClick={lookupEventHandler("click")}
63 | />
64 | );
65 | },
66 | );
67 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/ChangeListener/ChangeListener.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | **Key features:**
4 | - **Value monitoring**: Watches any expression, variable, or component property for changes
5 | - **Throttling support**: Prevents excessive triggering with `throttleWaitInMs` for rapid changes
6 | - **Previous/new values**: Access both old and new values in the change handler
7 | - **Reactive patterns**: Coordinates between components or triggers side effects
8 |
9 | %-DESC-END
10 |
11 | %-PROP-START listenTo
12 |
13 | The following sample demonstrates using this property. Every time the user clicks the button, a counter is incremented. The `ChangeListener` component watches the counter's value. Whenever it changes, the component fires the `didChange` event, which stores whether the new counter value is even into the `isEven` variable.
14 |
15 | ```xmlui-pg copy display name="Example: listenTo"
16 | <App var.counter="{0}" var.isEven="{false}">
17 | <Button label="Increment counter" onClick="{counter++}" />
18 | <ChangeListener
19 | listenTo="{counter}"
20 | onDidChange="isEven = counter % 2 == 0" />
21 | <Text>Counter is {counter} which {isEven? "is": "isn't"} even.</Text>
22 | </App>
23 | ```
24 |
25 | %-PROP-END
26 |
27 | %-PROP-START throttleWaitInMs
28 |
29 | The following example works like the previous one (in the `listen` prop's description). However, the user can reset or set the throttling time to 3 seconds. You can observe that while the throttling time is 3 seconds, the counter increments on every click, but `isEven` only refreshes once within 3 seconds.
30 |
31 | ```xmlui-pg copy display name="Example: throttleWaitInMs"
32 | <App var.counter="{0}" var.isEven="{false}" var.throttle="{0}">
33 | <HStack>
34 | <Button label="Increment counter" onClick="{counter++}" />
35 | <Button label="Set 3 sec throttling" onClick="throttle = 3000" />
36 | <Button label="Reset throttling" onClick="throttle = 0" />
37 | </HStack>
38 |
39 | <ChangeListener
40 | listenTo="{counter}"
41 | throttleWaitInMs="{throttle}"
42 | onDidChange="isEven = counter % 2 == 0" />
43 | <Text>Counter is {counter} which {isEven? "is": "isn't"} even.</Text>
44 | </App>
45 | ```
46 |
47 | %-PROP-END
48 |
49 | %-EVENT-START didChange
50 |
51 | This event is fired when the component observes a value change (within the specified throttling interval). Define the event handler that responds to that change (as the previous samples demonstrate).
52 |
53 | The event argument is an object with `prevValue` and `newValue` properties that (as their name suggests) contain the previous and the new values.
54 |
55 | %-EVENT-END
56 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/AppState/AppStateNative.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs";
2 | import type { AsyncFunction } from "../../abstractions/FunctionDefs";
3 | import { useIsomorphicLayoutEffect } from "../../components-core/utils/hooks";
4 | import { useAppStateContextPart } from "../../components/App/AppStateContext";
5 |
6 | export const defaultProps: Pick<{ bucket?: string }, "bucket"> = {
7 | bucket: "default",
8 | };
9 |
10 | type Props = {
11 | bucket?: string;
12 | initialValue: Record<string, any>;
13 | updateState: UpdateStateFn;
14 | registerComponentApi: RegisterComponentApiFn;
15 | onDidUpdate?: AsyncFunction;
16 | };
17 |
18 | export function AppState({
19 | bucket = defaultProps.bucket,
20 | updateState,
21 | initialValue,
22 | registerComponentApi,
23 | onDidUpdate,
24 | }: Props) {
25 | const update = useAppStateContextPart((value) => value.update);
26 | const value = useAppStateContextPart((value) => value?.appState?.[bucket]);
27 |
28 | useIsomorphicLayoutEffect(() => {
29 | if (initialValue !== undefined) {
30 | update(bucket, initialValue);
31 | }
32 | }, [bucket, initialValue]);
33 |
34 | useIsomorphicLayoutEffect(() => {
35 | updateState({ value });
36 |
37 | // Fire the didUpdate event when value changes
38 | if (onDidUpdate) {
39 | onDidUpdate({ bucket, value, previousValue: undefined }); // Note: previousValue tracking could be added if needed
40 | }
41 | }, [updateState, value, onDidUpdate, bucket]);
42 |
43 | useIsomorphicLayoutEffect(() => {
44 | registerComponentApi({
45 | update: (patch) => update(bucket, patch),
46 | appendToList: (key: string, id: any) => {
47 | const currentState = value || {};
48 | const currentArray = currentState[key] || [];
49 | // Only add if the id doesn't already exist in the array
50 | if (!currentArray.includes(id)) {
51 | const newArray = [...currentArray, id];
52 | update(bucket, { [key]: newArray });
53 | }
54 | },
55 | removeFromList: (key: string, id: any) => {
56 | const currentState = value || {};
57 | const currentArray = currentState[key] || [];
58 | const newArray = currentArray.filter((item: any) => item !== id);
59 | update(bucket, { [key]: newArray });
60 | },
61 | listIncludes: (key: string, id: any) => {
62 | const currentState = value || {};
63 | const currentArray = currentState[key] || [];
64 | return currentArray.includes(id);
65 | },
66 | });
67 | }, [bucket, registerComponentApi, update, value]);
68 |
69 | return null;
70 | }
71 |
```