This is page 24 of 182. Use http://codebase.md/xmlui-org/xmlui/xmlui-latest.js?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── clean-onions-pick.md
│ ├── config.json
│ ├── curly-llamas-try.md
│ ├── loose-trains-sit.md
│ ├── moody-pans-poke.md
│ ├── puny-worms-make.md
│ ├── rare-cooks-write.md
│ ├── 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
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ ├── lorem-ipsum.png
│ │ │ │ ├── playground-checkbox-source.png
│ │ │ │ ├── playground.png
│ │ │ │ ├── use-xmlui-mcp-to-find-a-howto.png
│ │ │ │ └── xmlui-demo-gallery.png
│ │ │ ├── introducing-xmlui.md
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ ├── xmlui-playground.md
│ │ │ └── xmlui-powered-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
--------------------------------------------------------------------------------
/xmlui/src/components/Column/Column.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import { createComponentRenderer } from "../../components-core/renderers";
2 | import { createMetadata } from "../metadata-helpers";
3 | import { Column, defaultProps } from "./ColumnNative";
4 |
5 | const COMP = "Column";
6 |
7 | export const ColumnMd = createMetadata({
8 | status: "stable",
9 | description:
10 | "`Column` defines the structure and behavior of individual table columns " +
11 | "within a [`Table`](/components/Table) component. Each Column controls data " +
12 | "binding, header display, sorting capabilities, sizing, and can contain any " +
13 | "XMLUI components for rich cell content.",
14 | props: {
15 | bindTo: {
16 | description:
17 | "Indicates the name of the current row item's property, the value of which to " +
18 | "lay out in the column. If this property is not defined, the column is not sortable.",
19 | valueType: "string",
20 | },
21 | header: {
22 | description:
23 | "This property defines a label for a particular column. If not set, the " +
24 | "`bindTo` property value is used for the label.",
25 | valueType: "string",
26 | },
27 | width: {
28 | description:
29 | "This property defines the width of the column. You can use a numeric value, a pixel " +
30 | "value (such as \`100px\`), or a star size value (such as \`*\`, \`2*\`, etc.). " +
31 | "You will get an error if you use any other unit (or value)." +
32 | "If not defined, the component will use a width according to the column values and " +
33 | "the available space.",
34 | valueType: "any",
35 | },
36 | minWidth: {
37 | description: `Indicates the minimum width a particular column can have. Same rules apply as with [width](#width).`,
38 | valueType: "any",
39 | },
40 | maxWidth: {
41 | description: `Indicates the maximum width a particular column can have. Same rules apply as with [width](#width).`,
42 | valueType: "any",
43 | },
44 | canSort: {
45 | description:
46 | "This property sets whether the user can sort by a column by clicking on its header " +
47 | "(\`true\`) or not (\`false\`). If the `bindTo` property is not defined, the column is not sortable.",
48 | defaultValue: defaultProps.canSort,
49 | valueType: "boolean",
50 | },
51 | pinTo: {
52 | description:
53 | `This property allows the column to be pinned to ` +
54 | `the \`left\` (left-to-right writing style) or \`right\` (left-to-right writing style) edge ` +
55 | "of the table. If the writing style is right-to-left, the locations are switched. " +
56 | "If this property is not set, the column is not pinned to any edge.",
57 | availableValues: ["left", "right"],
58 | valueType: "string",
59 | },
60 | canResize: {
61 | description:
62 | `This property indicates whether the user can resize the column. If set to ` +
63 | `\`true\`, the column can be resized by dragging the column border. If set to ` +
64 | `\`false\`, the column cannot be resized. Double-clicking the column border ` +
65 | `resets to the original size.`,
66 | valueType: "boolean",
67 | defaultValue: defaultProps.canResize,
68 | },
69 | },
70 | contextVars: {
71 | $item: {
72 | description: "The complete data row object being rendered",
73 | },
74 | $cell: {
75 | description: "The specific cell value for this column",
76 | },
77 | $itemIndex: {
78 | description: "Zero-based row index",
79 | },
80 | $colIndex: {
81 | description: "Zero-based column index",
82 | },
83 | $row: {
84 | description: "The complete data row object being rendered (the same as \`$item\`).",
85 | },
86 | $rowIndex: {
87 | description: "Zero-based row index (the same as \`$itemIndex\`).",
88 | },
89 | },
90 | });
91 |
92 | export const columnComponentRenderer = createComponentRenderer(
93 | COMP,
94 | ColumnMd,
95 | (rendererContext) => {
96 | const { node, renderChild, extractValue, className } = rendererContext;
97 | return (
98 | <Column
99 | className={className}
100 | header={extractValue.asDisplayText(node.props.header)}
101 | accessorKey={extractValue.asOptionalString(node.props.bindTo)}
102 | canSort={extractValue.asOptionalBoolean(node.props.canSort, true)}
103 | canResize={extractValue.asOptionalBoolean(node.props.canResize)}
104 | pinTo={extractValue.asOptionalString(node.props.pinTo)}
105 | width={extractValue(node.props.width)}
106 | minWidth={extractValue(node.props.minWidth)}
107 | maxWidth={extractValue(node.props.maxWidth)}
108 | nodeChildren={node.children}
109 | renderChild={renderChild}
110 | id={node.uid}
111 | />
112 | );
113 | },
114 | );
115 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/NavLink/NavLink.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import styles from "./NavLink.module.scss";
2 |
3 | import { createComponentRenderer } from "../../components-core/renderers";
4 | import { parseScssVar } from "../../components-core/theming/themeVars";
5 | import { createMetadata, d, dClick, dEnabled, dLabel } from "../metadata-helpers";
6 | import { Icon } from "../Icon/IconNative";
7 | import { NavLink, defaultProps } from "./NavLinkNative";
8 | import { LinkTargetMd } from "../abstractions";
9 |
10 | const COMP = "NavLink";
11 |
12 | export const NavLinkMd = createMetadata({
13 | status: "stable",
14 | description:
15 | "`NavLink` creates interactive navigation items that connect users to different " +
16 | "destinations within an app or external URLs. It automatically indicates active " +
17 | "states, supports custom icons and labels, and can execute custom actions instead " +
18 | "of navigation when needed.",
19 | props: {
20 | to: d(`This property defines the URL of the link.`),
21 | enabled: dEnabled(),
22 | active: {
23 | description:
24 | `This property indicates if the particular navigation is an active link. An active link ` +
25 | `has a particular visual appearance, provided its [\`displayActive\`](#displayactive) ` +
26 | `property is set to \`true\`.`,
27 | valueType: "boolean",
28 | defaultValue: defaultProps.active,
29 | },
30 | target: {
31 | description: `This optionally property specifies how to open the clicked link.`,
32 | availableValues: LinkTargetMd,
33 | type: "string",
34 | },
35 | label: dLabel(),
36 | vertical: {
37 | description:
38 | `This property sets how the active status is displayed on the \`${COMP}\` component. If ` +
39 | `set to true, the indicator is displayed on the side which lends itself to a vertically ` +
40 | `aligned navigation menu. By default, it displays a horizontal indicator.`,
41 | valueType: "boolean",
42 | },
43 | displayActive: {
44 | description:
45 | `This Boolean property indicates if the active state of a link should have a visual ` +
46 | `indication. Setting it to \`false\` removes the visual indication of an active link.`,
47 | valueType: "boolean",
48 | defaultValue: defaultProps.displayActive,
49 | },
50 | icon: d(
51 | `This property allows you to add an optional icon (specify the icon's name) to the navigation link.`,
52 | ),
53 | },
54 | events: {
55 | click: dClick(COMP),
56 | },
57 | themeVars: parseScssVar(styles.themeVars),
58 | themeVarDescriptions: {
59 | [`color-indicator-${COMP}`]:
60 | "Provides the following states: `--hover`, `--active`, `--pressed`",
61 | },
62 | defaultThemeVars: {
63 | [`border-${COMP}`]: "0px solid $borderColor",
64 | [`borderRadius-${COMP}`]: "$borderRadius",
65 | [`backgroundColor-${COMP}`]: "transparent",
66 | [`paddingHorizontal-${COMP}`]: "$space-4",
67 | [`paddingVertical-${COMP}`]: "$space-2",
68 | [`fontSize-${COMP}`]: "$fontSize-sm",
69 | [`fontWeight-${COMP}`]: "$fontWeight-normal",
70 | [`fontFamily-${COMP}`]: "$fontFamily",
71 | [`textColor-${COMP}`]: "$textColor-primary",
72 | [`fontWeight-${COMP}--pressed`]: "$fontWeight-normal",
73 | [`thickness-indicator-${COMP}`]: "$space-0_5",
74 |
75 | [`outlineColor-${COMP}--focus`]: "$outlineColor--focus",
76 | [`outlineWidth-${COMP}--focus`]: "$outlineWidth--focus",
77 | [`outlineStyle-${COMP}--focus`]: "$outlineStyle--focus",
78 | [`outlineOffset-${COMP}--focus`]: "-1px",
79 | [`borderRadius-indicator-${COMP}`]: "$borderRadius",
80 | [`color-icon-${COMP}`]: "$color-surface-500",
81 | [`color-indicator-${COMP}--active`]: "$color-primary-500",
82 | [`color-indicator-${COMP}--pressed`]: "$color-primary-500",
83 | [`color-indicator-${COMP}--hover`]: "$color-primary-600",
84 | },
85 | });
86 |
87 | export const navLinkComponentRenderer = createComponentRenderer(
88 | COMP,
89 | NavLinkMd,
90 | ({ node, extractValue, renderChild, className }) => {
91 | const iconName = extractValue.asString(node.props.icon);
92 | return (
93 | <NavLink
94 | uid={node.uid}
95 | to={extractValue(node.props.to)}
96 | disabled={!extractValue.asOptionalBoolean(node.props.enabled ?? true)}
97 | vertical={extractValue.asOptionalBoolean(node.props.vertical)}
98 | displayActive={extractValue.asOptionalBoolean(node.props.displayActive)}
99 | forceActive={extractValue.asOptionalBoolean(node.props.active)}
100 | className={className}
101 | target={extractValue(node.props?.target)}
102 | icon={<Icon name={iconName} className={styles.icon} />}
103 | >
104 | {extractValue.asDisplayText(node.props.label) || renderChild(node.children)}
105 | </NavLink>
106 | );
107 | },
108 | );
109 |
```
--------------------------------------------------------------------------------
/docs/public/pages/working-with-markdown.md:
--------------------------------------------------------------------------------
```markdown
1 | # Working with Markdown
2 |
3 | When you use XMLUI to create an application's user interface, the [Text](/components/Text) component enables you to display short-form text. But XMLUI can also support sites like this one, using the [Markdown](/components/Markdown) component for long-form text.
4 |
5 | There are @{Object.keys(appGlobals.content).length + Object.keys(appGlobals.prefetchedContent).length } pages here. Of these, @{Object.keys(appGlobals.content).length} are autogenerated Markdown files that document components, and @{Object.keys(appGlobals.prefetchedContent).length} are handwritten Markdown pages like this one.
6 |
7 | How do we know that? This site is an XMLUI app, and the [Markdown page you are reading](https://github.com/xmlui-org/xmlui/blob/main/docs/public/pages/working-with-markdown.md) can access [global app settings](/globals#appglobals) just as the `Search` component does.
8 |
9 | ```xmlui
10 | <!-- Search component used in this site's Main.xmlui -->
11 | <Search data="{appGlobals.plainTextContent}" />
12 | ```
13 |
14 | XMLUI isn't just a way to build apps like XMLUI Invoice. It's also a way to build websites, like this one, using pages written in Markdown format.
15 |
16 | ## Using the Markdown component
17 |
18 | Like every XMLUI app, this site is controlled by a [Main.xmlui](https://github.com/xmlui-org/xmlui/blob/main/docs/src/Main.xmlui). In this case `Main.xmlui` uses core components including [App](/components/App), [AppHeader](/components/AppHeader), [NavPanel](/components/NavPanel), [NavGroup](/components/NavGroup), [NavLink](/components/NavLink), [Pages](/components/Pages), [Page](/components/Page).
19 |
20 | The [Markdown](/components/Markdown) component that renders this page is invoked from `Main.xmlui`.
21 |
22 | ```xmlui
23 | <Page url="/working-with-markdown">
24 | <DocumentPage url="/pages/working-with-markdown.md"/>
25 | </Page>
26 | ```
27 |
28 | `DocumentPage` is a [user-defined component](/user-defined-components) that wraps our site's structure around the contents of the file.
29 |
30 | ```xmlui
31 | <Component name="DocumentPage">
32 | <HStack gap="$space-5">
33 | <VStack width="*">
34 | <Markdown
35 | when="{$props.content || $props.url}"
36 | content="{$props.content}"
37 | data="{$props.url}" />
38 | <DocumentLinks />
39 | </VStack>
40 | <TableOfContents
41 | width="$space-64"
42 | when="{mediaSize.sizeIndex > 3 && !$props.hideToc}"
43 | maxHeadingLevel="3"
44 | />
45 | <VStack
46 | width="$space-64"
47 | when="{mediaSize.sizeIndex > 3 && $props.hideToc}"
48 | />
49 | </HStack>
50 | </Component>
51 | ```
52 |
53 | [TableOfContents](/components/TableOfContents) is a core component that produces the ToC in the right pane of this page.
54 |
55 | `DocumentLinks` is a user-defined component that introspect's site metadata in order to create the previous / next links at the bottom of the page.
56 |
57 | ```xmlui
58 | <Component name="DocumentLinks">
59 | <Theme
60 | textDecorationLine-Link="none"
61 | textColor-Link="$textColor-DocumentLinks"
62 | textColor-Link--hover="textColor-DocumentLinks--hover"
63 | backgroundColor-ContentSeparator="$backgroundColor-separator-DocumentLinks">
64 | <ContentSeparator
65 | marginVertical="$space-6"
66 | when="{$linkInfo.prevLink || $linkInfo.nextLink}"/>
67 | <HStack verticalAlignment="center" gap="$space-2">
68 | <Link when="{$linkInfo.prevLink}" to="{$linkInfo.prevLink.to}">
69 | <Icon name="chevronleft"/>
70 | <Text variant="subtitle">
71 | {$linkInfo.prevLink.label}
72 | </Text>
73 | </Link>
74 | <SpaceFiller/>
75 | <Link when="{$linkInfo.nextLink}" to="{$linkInfo.nextLink.to}">
76 | <Text variant="subtitle">
77 | {$linkInfo.nextLink.label}
78 | </Text>
79 | <Icon name="chevronright"/>
80 | </Link>
81 | </HStack>
82 | </Theme>
83 | </Component>
84 | ```
85 |
86 | ## Markdown theming
87 |
88 | Native XMLUI components and corresponding Markdown elements share common theme variables. Consider this XMLUI `Table` that's styled according to the current theme.
89 |
90 | ```xmlui-pg display name="Example: using the Table component"
91 | <App>
92 | <Table width="40%" data="{
93 | [{
94 | apples: 3,
95 | pears: 7,
96 | oranges: 11
97 | }]
98 | }"
99 | >
100 | <Column bindTo="apples"/>
101 | <Column bindTo="pears"/>
102 | <Column bindTo="oranges"/>
103 | </Table>
104 | </App>
105 | ```
106 |
107 | Now let's create that same table using this markdown syntax.
108 |
109 | ```
110 | | apples | oranges | pears
111 | | --- | --- | ---
112 | | 3 | 7 | 11
113 | ```
114 |
115 | It looks the same.
116 |
117 | | apples | oranges | pears
118 | | --- | --- | ---
119 | | 3 | 7 | 11
120 |
121 |
122 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/DropdownMenu/DropdownMenu.module.scss:
--------------------------------------------------------------------------------
```scss
1 | @use "../../components-core/theming/themes" as t;
2 |
3 | // --- This code snippet is required to collect the theme variables used in this module
4 | $themeVars: ();
5 | @function createThemeVar($componentVariable) {
6 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
7 | @return t.getThemeVar($themeVars, $componentVariable);
8 | }
9 |
10 | $backgroundColor-DropdownMenu: createThemeVar("backgroundColor-DropdownMenu");
11 | $borderRadius-DropdownMenu: createThemeVar("borderRadius-DropdownMenu");
12 | $boxShadow-DropdownMenu: createThemeVar("boxShadow-DropdownMenu");
13 | $borderColor-DropdownMenu-content: createThemeVar("borderColor-DropdownMenu-content");
14 | $borderWidth-DropdownMenu-content: createThemeVar("borderWidth-DropdownMenu-content");
15 | $borderStyle-DropdownMenu-content: createThemeVar("borderStyle-DropdownMenu-content");
16 | $minWidth-DropdowMenu: createThemeVar("minWidth-DropdownMenu");
17 | $backgroundColor-MenuItem: createThemeVar("backgroundColor-MenuItem");
18 | $color-MenuItem: createThemeVar("color-MenuItem");
19 | $fontFamily-MenuItem: createThemeVar("fontFamily-MenuItem");
20 | $gap-MenuItem: createThemeVar("gap-MenuItem");
21 | $fontSize-MenuItem: createThemeVar("fontSize-MenuItem");
22 | $paddingVertical-MenuItem: createThemeVar("paddingVertical-MenuItem");
23 | $paddingHorizontal-MenuItem: createThemeVar("paddingHorizontal-MenuItem");
24 | $backgroundColor-MenuItem--hover: createThemeVar("backgroundColor-MenuItem--hover");
25 | $backgroundColor-MenuItem--active: createThemeVar("backgroundColor-MenuItem--active");
26 | $backgroundColor-MenuItem--active--hover: createThemeVar("backgroundColor-MenuItem--active--hover");
27 | $color-MenuItem--hover: createThemeVar("color-MenuItem--hover");
28 | $color-MenuItem--active: createThemeVar("color-MenuItem--active");
29 | $color-MenuItem--active--hover: createThemeVar("color-MenuItem--active--hover");
30 | $color-MenuItem--disabled: createThemeVar("color-MenuItem--disabled");
31 | $marginTop-MenuSeparator: createThemeVar("marginTop-MenuSeparator");
32 | $marginBottom-MenuSeparator: createThemeVar("marginBottom-MenuSeparator");
33 | $width-MenuSeparator: createThemeVar("width-MenuSeparator");
34 | $height-MenuSeparator: createThemeVar("height-MenuSeparator");
35 | $color-MenuSeparator: createThemeVar("color-MenuSeparator");
36 |
37 | @layer components {
38 |
39 | .DropdownMenuContent {
40 | max-height: var(--radix-dropdown-menu-content-available-height);
41 | overflow: auto;
42 | }
43 |
44 | .DropdownMenuContent,
45 | .DropdownMenuSubContent {
46 | background-color: $backgroundColor-DropdownMenu;
47 | border-radius: $borderRadius-DropdownMenu;
48 | min-width: $minWidth-DropdowMenu;
49 | box-shadow: $boxShadow-DropdownMenu;
50 | animation-duration: 400ms;
51 | animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
52 | will-change: transform, opacity;
53 | border: $borderWidth-DropdownMenu-content $borderStyle-DropdownMenu-content
54 | $borderColor-DropdownMenu-content;
55 | }
56 |
57 | .DropdownMenuItem,
58 | .DropdownMenuSubTrigger {
59 | background-color: $backgroundColor-MenuItem;
60 | color: $color-MenuItem;
61 | font-family: $fontFamily-MenuItem;
62 | display: flex;
63 | border: 0;
64 | width: 100%;
65 | gap: $gap-MenuItem;
66 | font-size: $fontSize-MenuItem;
67 | padding: $paddingVertical-MenuItem 0;
68 | padding-inline: $paddingHorizontal-MenuItem; //t.$space-3;
69 | cursor: pointer;
70 | align-items: center;
71 | justify-content: space-between;
72 | user-select: none;
73 | outline: none;
74 |
75 | &:hover,
76 | &[data-highlighted] {
77 | background-color: $backgroundColor-MenuItem--hover;
78 | color: $color-MenuItem--hover;
79 | }
80 |
81 | &.active {
82 | background-color: $backgroundColor-MenuItem--active;
83 | color: $color-MenuItem--active;
84 |
85 | &:hover {
86 | background-color: $backgroundColor-MenuItem--active--hover;
87 | color: $color-MenuItem--active--hover;
88 | }
89 | }
90 |
91 | &.disabled {
92 | background-color: $backgroundColor-MenuItem;
93 | color: $color-MenuItem--disabled;
94 | cursor: not-allowed;
95 |
96 | &:hover {
97 | background-color: $backgroundColor-MenuItem;
98 | color: $color-MenuItem--disabled;
99 | }
100 | }
101 |
102 | .wrapper {
103 | flex: 1;
104 | }
105 | }
106 |
107 | .DropdownMenuSeparator {
108 | margin-top: $marginTop-MenuSeparator;
109 | margin-bottom: $marginBottom-MenuSeparator;
110 | width: $width-MenuSeparator;
111 | height: $height-MenuSeparator;
112 | background-color: $color-MenuSeparator;
113 |
114 | &:first-child, &:last-child{
115 | display: none;
116 | }
117 | }
118 | }
119 |
120 | // --- We export the theme variables to add them to the component renderer
121 | :export {
122 | themeVars: t.json-stringify($themeVars);
123 | }
124 |
```
--------------------------------------------------------------------------------
/xmlui/vite.config.ts:
--------------------------------------------------------------------------------
```typescript
1 | // @ts-ignore
2 | import path from "path";
3 | import { defineConfig, loadEnv } from "vite";
4 | import react from "@vitejs/plugin-react";
5 | import svgr from "vite-plugin-svgr";
6 | import ViteYaml from "@modyfi/vite-plugin-yaml";
7 | import { default as ViteXmlui } from "./bin/vite-xmlui-plugin";
8 | import dts from "vite-plugin-dts";
9 | import { libInjectCss } from "vite-plugin-lib-inject-css";
10 | import copy from "rollup-plugin-copy";
11 | // @ts-ignore
12 | import * as packageJson from "./package.json";
13 |
14 | export default ({ mode = "lib" }) => {
15 | const env = loadEnv(mode, process.cwd(), "");
16 | let lib;
17 | let define;
18 | let distSubDirName = "";
19 | switch (mode) {
20 | case "standalone": {
21 | distSubDirName = "standalone";
22 | lib = {
23 | entry: [path.resolve("src", "index-standalone.ts")],
24 | name: "xmlui",
25 | formats: ["umd"] as any,
26 | fileName: (format: any) => `xmlui-standalone.${format}.js`,
27 | };
28 | define = {
29 | "process.env": {
30 | NODE_ENV: env.NODE_ENV,
31 | VITE_MOCK_ENABLED: true,
32 | VITE_MOCK_WORKER_LOCATION: "mockApi.js",
33 | VITE_USED_COMPONENTS_XmluiCodeHightlighter: "false",
34 | VITE_USED_COMPONENTS_Tree: "false",
35 | VITE_USED_COMPONENTS_TableEditor: "false",
36 | // VITE_USED_COMPONENTS_Charts: "false",
37 | // VITE_USER_COMPONENTS_Inspect: "false",
38 | VITE_XMLUI_VERSION: `${env.npm_package_version} (built ${new Date().toLocaleDateString("en-US")})`,
39 | },
40 | };
41 | break;
42 | }
43 | case "metadata": {
44 | distSubDirName = "metadata";
45 | lib = {
46 | entry: [path.resolve("src", "components", "collectedComponentMetadata.ts")],
47 | name: "xmlui-metadata",
48 | fileName: "xmlui-metadata",
49 | };
50 | define = {
51 | "process.env": {
52 | NODE_ENV: env.NODE_ENV,
53 | },
54 | };
55 | break;
56 | }
57 | default: {
58 | distSubDirName = "lib";
59 | lib = {
60 | entry: {
61 | xmlui: path.resolve("src", "index.ts"),
62 | "xmlui-parser": path.resolve("src", "parsers", "xmlui-parser", "index.ts"),
63 | "language-server": path.resolve("src", "language-server", "server.ts"),
64 | "language-server-web-worker": path.resolve(
65 | "src",
66 | "language-server",
67 | "server-web-worker.ts",
68 | ),
69 | "syntax-monaco": path.resolve("src", "syntax", "monaco", "index.ts"),
70 | "syntax-textmate": path.resolve("src", "syntax", "textMate", "index.ts"),
71 | testing: path.resolve("src", "testing", "index.ts"),
72 | },
73 | formats: ["es"] as any,
74 | };
75 | }
76 | }
77 | let plugins =
78 | mode === "metadata"
79 | ? [ViteXmlui({})]
80 | : [react(), svgr(), ViteYaml(), ViteXmlui({}), libInjectCss(), dts({ rollupTypes: true })];
81 |
82 | if (mode === "lib") {
83 | plugins.push(
84 | copy({
85 | hook: "writeBundle",
86 | targets: [
87 | {
88 | src: ["src/**/*.scss", "!src/**/*.module.scss"],
89 | dest: "dist/lib/scss",
90 | rename: (name, extension, fullPath) => {
91 | //we do this to preserve the folder structure of the scss files
92 | // e.g. src/components/button/Button.scss should be copied to dist/lib/scss/components/button/Button.scss
93 | // and not to dist/lib/scss/Button.scss
94 | // fullPath will be something like 'src/components/button/Button.scss'
95 | // We want to remove the 'src/' prefix to get the relative path
96 | const relativePath = fullPath.replace("src/", "");
97 |
98 | // This returns 'components/button/Button.scss'
99 | return relativePath;
100 | },
101 | },
102 | ],
103 | }),
104 | );
105 | }
106 | return defineConfig({
107 | resolve: {
108 | alias: {
109 | lodash: "lodash-es",
110 | },
111 | },
112 | define,
113 | esbuild: {
114 | target: "es2020",
115 | },
116 | optimizeDeps: {
117 | esbuildOptions: {
118 | target: "es2020",
119 | },
120 | },
121 | build: {
122 | minify: "terser",
123 | emptyOutDir: true,
124 | outDir: `dist/${distSubDirName}`,
125 | lib,
126 | rollupOptions: {
127 | treeshake: mode === "metadata" ? "smallest" : undefined,
128 | external:
129 | mode === "standalone"
130 | ? []
131 | : [...Object.keys(packageJson.dependencies), "react/jsx-runtime", "@playwright/test"],
132 | output: {
133 | globals: {
134 | react: "React",
135 | "react-dom": "ReactDOM",
136 | "react/jsx-runtime": "react/jsx-runtime",
137 | },
138 | },
139 | },
140 | },
141 | plugins: plugins,
142 | });
143 | };
144 |
```
--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/parser-unary.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, expect, it } from "vitest";
2 |
3 | import { Parser } from "../../../src/parsers/scripting/Parser";
4 | import {
5 | T_BINARY_EXPRESSION,
6 | T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
7 | T_CONDITIONAL_EXPRESSION,
8 | T_FUNCTION_INVOCATION_EXPRESSION,
9 | T_IDENTIFIER,
10 | T_LITERAL,
11 | T_MEMBER_ACCESS_EXPRESSION,
12 | T_SEQUENCE_EXPRESSION,
13 | T_UNARY_EXPRESSION,
14 | UnaryExpression,
15 | } from "../../../src/components-core/script-runner/ScriptingSourceTree";
16 |
17 | describe("Parser - unary expressions", () => {
18 | const unaryCases = [
19 | { src: "+a", op: "+", exp: T_IDENTIFIER },
20 | { src: "-a", op: "-", exp: T_IDENTIFIER },
21 | { src: "!a", op: "!", exp: T_IDENTIFIER },
22 | { src: "~a", op: "~", exp: T_IDENTIFIER },
23 | { src: "typeof a", op: "typeof", exp: T_IDENTIFIER },
24 | { src: "delete a", op: "delete", exp: T_IDENTIFIER },
25 |
26 | { src: "+3", op: "+", exp: T_LITERAL },
27 | { src: "-3.14", op: "-", exp: T_LITERAL },
28 | { src: "!0b1111", op: "!", exp: T_LITERAL },
29 | { src: "~0xa123", op: "~", exp: T_LITERAL },
30 | { src: 'typeof "abc"', op: "typeof", exp: T_LITERAL },
31 | { src: 'delete "abc"', op: "delete", exp: T_LITERAL },
32 |
33 | { src: "+!a", op: "+", exp: T_UNARY_EXPRESSION },
34 | { src: "-!a", op: "-", exp: T_UNARY_EXPRESSION },
35 | { src: "!!a", op: "!", exp: T_UNARY_EXPRESSION },
36 | { src: "~!a", op: "~", exp: T_UNARY_EXPRESSION },
37 | { src: "typeof !a", op: "typeof", exp: T_UNARY_EXPRESSION },
38 | { src: "delete !a", op: "delete", exp: T_UNARY_EXPRESSION },
39 |
40 | { src: "+(a+b)", op: "+", exp: T_BINARY_EXPRESSION },
41 | { src: "-(a+b)", op: "-", exp: T_BINARY_EXPRESSION },
42 | { src: "!(a+b)", op: "!", exp: T_BINARY_EXPRESSION },
43 | { src: "~(a+b)", op: "~", exp: T_BINARY_EXPRESSION },
44 | { src: "typeof (a+b)", op: "typeof", exp: T_BINARY_EXPRESSION },
45 | { src: "delete (a+b)", op: "delete", exp: T_BINARY_EXPRESSION },
46 |
47 | { src: "+(a, b)", op: "+", exp: T_SEQUENCE_EXPRESSION },
48 | { src: "-(a, b)", op: "-", exp: T_SEQUENCE_EXPRESSION },
49 | { src: "!(a, b)", op: "!", exp: T_SEQUENCE_EXPRESSION },
50 | { src: "~(a, b)", op: "~", exp: T_SEQUENCE_EXPRESSION },
51 | { src: "typeof (a, b)", op: "typeof", exp: T_SEQUENCE_EXPRESSION },
52 | { src: "delete (a, b)", op: "delete", exp: T_SEQUENCE_EXPRESSION },
53 |
54 | { src: "+(a ? b : c)", op: "+", exp: T_CONDITIONAL_EXPRESSION },
55 | { src: "-(a ? b : c)", op: "-", exp: T_CONDITIONAL_EXPRESSION },
56 | { src: "!(a ? b : c)", op: "!", exp: T_CONDITIONAL_EXPRESSION },
57 | { src: "~(a ? b : c)", op: "~", exp: T_CONDITIONAL_EXPRESSION },
58 | { src: "typeof (a ? b : c)", op: "typeof", exp: T_CONDITIONAL_EXPRESSION },
59 | { src: "delete (a ? b : c)", op: "delete", exp: T_CONDITIONAL_EXPRESSION },
60 |
61 | { src: "+c(a, b)", op: "+", exp: T_FUNCTION_INVOCATION_EXPRESSION },
62 | { src: "-c(a, b)", op: "-", exp: T_FUNCTION_INVOCATION_EXPRESSION },
63 | { src: "!c(a, b)", op: "!", exp: T_FUNCTION_INVOCATION_EXPRESSION },
64 | { src: "~c(a, b)", op: "~", exp: T_FUNCTION_INVOCATION_EXPRESSION },
65 | { src: "typeof c(a, b)", op: "typeof", exp: T_FUNCTION_INVOCATION_EXPRESSION },
66 | { src: "delete c(a, b)", op: "delete", exp: T_FUNCTION_INVOCATION_EXPRESSION },
67 |
68 | { src: "+a.b", op: "+", exp: T_MEMBER_ACCESS_EXPRESSION },
69 | { src: "-a.b", op: "-", exp: T_MEMBER_ACCESS_EXPRESSION },
70 | { src: "!a.b", op: "!", exp: T_MEMBER_ACCESS_EXPRESSION },
71 | { src: "~a.b", op: "~", exp: T_MEMBER_ACCESS_EXPRESSION },
72 | { src: "typeof a.b", op: "typeof", exp: T_MEMBER_ACCESS_EXPRESSION },
73 | { src: "delete a.b", op: "delete", exp: T_MEMBER_ACCESS_EXPRESSION },
74 |
75 | { src: "+a[b]", op: "+", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
76 | { src: "-a[b]", op: "-", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
77 | { src: "!a[b]", op: "!", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
78 | { src: "~a[b]", op: "~", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
79 | { src: "typeof a[b]", op: "typeof", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
80 | { src: "delete a[b]", op: "delete", exp: T_CALCULATED_MEMBER_ACCESS_EXPRESSION },
81 | ];
82 | unaryCases.forEach((c) => {
83 | it(`Unary: ${c.src}`, () => {
84 | // --- Arrange
85 | const wParser = new Parser(c.src);
86 |
87 | // --- Act
88 | const expr = wParser.parseExpr();
89 |
90 | // --- Assert
91 | expect(expr).not.equal(null);
92 | if (!expr) return;
93 | expect(expr.type).equal(T_UNARY_EXPRESSION);
94 | const unary = expr as UnaryExpression;
95 | expect(unary.op).equal(c.op);
96 | expect(unary.expr.type).equal(c.exp);
97 | });
98 | });
99 | });
100 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/FileInput/FileInput.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | **Key features:**
4 | - **File type filtering**: Restrict selection to specific file types using `acceptsFileType`
5 | - **Multiple file selection**: Enable users to select multiple files simultaneously
6 | - **Directory selection**: Allow folder selection instead of individual files
7 | - **Customizable button**: Configure button text, icons, position, and styling to match your design
8 |
9 | %-DESC-END
10 |
11 |
12 | %-PROP-START acceptsFileType
13 |
14 | ```xmlui-pg copy display name="Example: acceptsFileType"
15 | <App>
16 | <FileInput acceptsFileType="{['.txt', '.jpg']}" />
17 | </App>
18 | ```
19 |
20 | %-PROP-END
21 |
22 | %-PROP-START buttonIcon
23 |
24 | ```xmlui-pg copy display name="Example: buttonIcon"
25 | <App>
26 | <FileInput buttonIcon="drive" buttonLabel="Let there be drive" />
27 | <FileInput buttonIcon="drive" />
28 | </App>
29 | ```
30 |
31 | %-PROP-END
32 |
33 | %-PROP-START buttonIconPosition
34 |
35 | ```xmlui-pg copy display name="Example: buttonIconPosition"
36 | <App>
37 | <FileInput buttonIcon="drive" buttonLabel="End" buttonIconPosition="end" />
38 | </App>
39 | ```
40 |
41 | %-PROP-END
42 |
43 | %-PROP-START buttonLabel
44 |
45 | This property is an optional string to set a label for the button part.
46 |
47 | ```xmlui-pg copy display name="Example: label"
48 | <App >
49 | <FileInput />
50 | <FileInput buttonLabel="I am the button label" />
51 | </App>
52 | ```
53 |
54 | %-PROP-END
55 |
56 | %-PROP-START buttonSize
57 |
58 | ```xmlui-pg copy display name="Example: buttonSize"
59 | <App>
60 | <FileInput buttonSize="lg" />
61 | </App>
62 | ```
63 |
64 | %-PROP-END
65 |
66 | %-PROP-START buttonThemeColor
67 |
68 | ```xmlui-pg copy display name="Example: buttonThemeColor"
69 | <App>
70 | <FileInput buttonThemeColor="secondary" />
71 | </App>
72 | ```
73 |
74 | %-PROP-END
75 |
76 | %-PROP-START buttonVariant
77 |
78 | ```xmlui-pg copy display name="Example: buttonVariant"
79 | <App>
80 | <FileInput buttonLabel="outlined" buttonVariant="outlined" />
81 | </App>
82 | ```
83 |
84 | %-PROP-END
85 |
86 | %-PROP-STAR directory
87 |
88 | ```xmlui-pg copy display name="Example: directory"
89 | <App>
90 | <FileInput directory="true" />
91 | </App>
92 | ```
93 |
94 | %-PROP-END
95 |
96 | %-PROP-START multiple
97 |
98 | ```xmlui-pg copy display name="Example: multiple"
99 | <App>
100 | <FileInput multiple="false" />
101 | <FileInput multiple="true" />
102 | </App>
103 | ```
104 |
105 | %-PROP-END
106 |
107 | %-PROP-START validationStatus
108 |
109 | ```xmlui-pg copy display name="Example: validationStatus"
110 | <App>
111 | <FileInput />
112 | <FileInput validationStatus="valid" />
113 | <FileInput validationStatus="warning" />
114 | <FileInput validationStatus="error" />
115 | </App>
116 | ```
117 |
118 | %-EVENT-START didChange
119 |
120 | Write in the input field and see how the `Text` underneath it is updated in accordingly.
121 |
122 | ```xmlui-pg copy {2} display name="Example: didChange"
123 | <App var.field="">
124 | <FileInput onDidChange="(file) => field = file[0]?.name" />
125 | <Text value="{field}" />
126 | </App>
127 | ```
128 |
129 | %-EVENT-END
130 |
131 | %-EVENT-START gotFocus
132 |
133 | Clicking on the `FileInput` in the example demo changes the label text.
134 | Note how clicking elsewhere resets the text to the original.
135 |
136 | ```xmlui-pg copy {4-5} display name="Example: gotFocus/lostFocus"
137 | <App>
138 | <FileInput
139 | buttonLabel="{focused === true ? 'I got focused!' : 'I lost focus...'}"
140 | onGotFocus="focused = true"
141 | onLostFocus="focused = false"
142 | var.focused="{false}"
143 | />
144 | </App>
145 | ```
146 |
147 | %-EVENT-END
148 |
149 | %-EVENT-START lostFocus
150 |
151 | (See the example above)
152 |
153 | %-EVENT-END
154 |
155 | %-API-START focus
156 |
157 | ```xmlui-pg copy /fileInputComponent.focus()/ display name="Example: focus"
158 | <App>
159 | <HStack>
160 | <Button label="Focus FileInput" onClick="fileInputComponent.focus()" />
161 | <FileInput id="fileInputComponent" />
162 | </HStack>
163 | </App>
164 | ```
165 |
166 | %-API-END
167 |
168 | %-API-START open
169 |
170 | ```xmlui-pg copy /fileInputComponent.open()/ display name="Example: open"
171 | <App>
172 | <HStack>
173 | <Button label="Open FileInput" onClick="fileInputComponent.open()" />
174 | <FileInput id="fileInputComponent" />
175 | </HStack>
176 | </App>
177 | ```
178 |
179 | %-API-END
180 |
181 | %-API-START setValue
182 |
183 | (**NOT IMPLEMENTED YET**) You can use this method to set the component's current value programmatically.
184 |
185 | %-API-END
186 |
187 | %-API-START value
188 |
189 | In the example below, select a file using the file browser of the `FileInput` component
190 | and note how the `Text` component displays the selected file's name:
191 |
192 | ```xmlui-pg copy {3-4} display name="Example: value"
193 | <App>
194 | <HStack>
195 | <Text value="Selected file name: {fileInputComponent.value}" />
196 | <FileInput id="fileInputComponent" />
197 | </HStack>
198 | </App>
199 | ```
200 |
201 | %-API-END
202 |
203 | %-STYLE-START
204 |
205 | The `FileInput` component does not theme variables directly.
206 | However, it uses the [`Button`](/components/Button) and [`TextBox`](/components/TextBox) components under the hood.
207 | Thus, modifying the styles of both of these components affects the `FileInput`.
208 |
209 | See [Button styling](/components/Button#styling) and [TextBox styling](/components/TextBox#styling).
210 |
211 | %-STYLE-END
```
--------------------------------------------------------------------------------
/xmlui/src/language-server/services/hover.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { GetText } from "../../parsers/xmlui-parser/parser";
2 | import { findTokenAtPos } from "../../parsers/xmlui-parser/utils";
3 | import { SyntaxKind } from "../../parsers/xmlui-parser/syntax-kind";
4 | import type { Node } from "../../parsers/xmlui-parser/syntax-node";
5 | import { compNameForTagNameNode, findTagNameNodeInStack } from "./common/syntax-node-utilities";
6 | import * as docGen from "./common/docs-generation";
7 | import type { ComponentMetadataCollection, MetadataProvider } from "./common/metadata-utils";
8 | import type { Hover, Position } from "vscode-languageserver";
9 | import { MarkupKind } from "vscode-languageserver";
10 | import { offsetRangeToPosRange } from "./common/lsp-utils";
11 |
12 | type SimpleHover = null | {
13 | value: string;
14 | range: {
15 | pos: number;
16 | end: number;
17 | };
18 | };
19 |
20 | type HoverContex = {
21 | node: Node;
22 | getText: GetText;
23 | metaByComp: MetadataProvider;
24 | offsetToPosition: (pos: number) => Position;
25 | };
26 |
27 | /**
28 | * @returns The hover content string
29 | */
30 | export function handleHoverCore(
31 | { node, getText, metaByComp }: Omit<HoverContex, "offsetToPosition">,
32 | position: number,
33 | ): SimpleHover {
34 | const findRes = findTokenAtPos(node, position);
35 |
36 | if (findRes === undefined) {
37 | return null;
38 | }
39 | const { chainAtPos } = findRes;
40 |
41 | const atNode = chainAtPos.at(-1)!;
42 | const parentNode = chainAtPos.at(-2);
43 | switch (atNode.kind) {
44 | case SyntaxKind.Identifier:
45 | switch (parentNode?.kind) {
46 | case SyntaxKind.TagNameNode: {
47 | return hoverName({
48 | metaByComp: metaByComp,
49 | tagNameNode: parentNode,
50 | identNode: atNode,
51 | getText,
52 | });
53 | }
54 | case SyntaxKind.AttributeKeyNode: {
55 | return hoverAttr({
56 | metaByComp,
57 | attrKeyNode: parentNode,
58 | parentStack: chainAtPos.slice(0, -2),
59 | getText,
60 | });
61 | }
62 | }
63 | break;
64 | }
65 | return null;
66 | }
67 |
68 | export function handleHover(ctx: HoverContex, position: number): Hover {
69 | const hoverRes = handleHoverCore(ctx, position);
70 | if (hoverRes === null) {
71 | return null;
72 | }
73 | const { value, range } = hoverRes;
74 | return {
75 | contents: {
76 | kind: MarkupKind.Markdown,
77 | value,
78 | },
79 | range: offsetRangeToPosRange(ctx.offsetToPosition, range),
80 | };
81 | }
82 |
83 | function hoverAttr({
84 | metaByComp,
85 | attrKeyNode,
86 | parentStack,
87 | getText,
88 | }: {
89 | metaByComp: MetadataProvider;
90 | attrKeyNode: Node;
91 | parentStack: Node[];
92 | getText: GetText;
93 | }): SimpleHover {
94 | if (parentStack.at(-1).kind !== SyntaxKind.AttributeNode) {
95 | return null;
96 | }
97 | if (parentStack.at(-2).kind !== SyntaxKind.AttributeListNode) {
98 | return null;
99 | }
100 | const tag = parentStack.at(-3);
101 | if (tag?.kind !== SyntaxKind.ElementNode) {
102 | return null;
103 | }
104 |
105 | const tagNameNode = findTagNameNodeInStack(parentStack);
106 | if (!tagNameNode) {
107 | return null;
108 | }
109 | const compName = compNameForTagNameNode(tagNameNode, getText);
110 |
111 | const component = metaByComp.getComponent(compName);
112 | if (!component) {
113 | return null;
114 | }
115 | const attrKeyChildren = attrKeyNode.children!;
116 | const identIdx = attrKeyChildren.findIndex((c) => c.kind === SyntaxKind.Identifier);
117 |
118 | if (identIdx === -1) {
119 | return null;
120 | }
121 |
122 | const attrIdent = attrKeyChildren[identIdx];
123 | const propIsNamespaceDefinition =
124 | attrKeyChildren[identIdx + 1]?.kind === SyntaxKind.Colon &&
125 | attrKeyChildren[identIdx + 2]?.kind === SyntaxKind.Identifier &&
126 | getText(attrIdent) === "xmlns";
127 |
128 | if (propIsNamespaceDefinition) {
129 | return {
130 | value: `Defines a namespace. TODO Further Documentation needed.`,
131 | range: {
132 | pos: attrKeyNode.pos,
133 | end: attrKeyNode.end,
134 | },
135 | };
136 | }
137 |
138 | const attrName = getText(attrIdent);
139 |
140 | const attrMd = component.getAttr(attrName);
141 | if (!attrMd) {
142 | return null;
143 | }
144 | const value = docGen.generateAttrDescription(attrName, attrMd);
145 | return {
146 | value,
147 | range: {
148 | pos: attrKeyNode.pos,
149 | end: attrKeyNode.end,
150 | },
151 | };
152 | }
153 |
154 | function hoverName({
155 | metaByComp,
156 | tagNameNode,
157 | identNode,
158 | getText,
159 | }: {
160 | metaByComp: MetadataProvider;
161 | tagNameNode: Node;
162 | identNode: Node;
163 | getText: GetText;
164 | }): SimpleHover {
165 | const compName = compNameForTagNameNode(tagNameNode, getText);
166 | if (!compName) {
167 | return null;
168 | }
169 | const compMetadata = metaByComp.getComponent(compName)?.getMetadata();
170 | if (!compMetadata) {
171 | return null;
172 | }
173 | const value = docGen.generateCompNameDescription(compName, compMetadata);
174 | return {
175 | value,
176 | range: {
177 | pos: identNode.pos,
178 | end: identNode.end,
179 | },
180 | };
181 | }
182 |
```
--------------------------------------------------------------------------------
/docs/public/pages/tutorial-04.md:
--------------------------------------------------------------------------------
```markdown
1 | # InfoCards
2 |
3 | The `Dashboard` page opens with a set of infocards. Here is a simplified version of two of them.
4 |
5 | ```xmlui-pg display name="infocards"
6 | <App>
7 | <variable name="dashboardStats" value="{
8 | {
9 | value:
10 | [
11 | { outstanding: 3569, paid_this_year: 1745}
12 | ]
13 | }
14 | }" />
15 |
16 | <HStack>
17 | <Card>
18 | <Text>Outstanding</Text>
19 | <Text>{ dashboardStats.value[0].outstanding } </Text>
20 | </Card>
21 | <Card>
22 | <Text>Paid this year</Text>
23 | <Text>{ dashboardStats.value[0].paid_this_year } </Text>
24 | </Card>
25 | </HStack>
26 | </App>
27 | ```
28 |
29 | In the app, `dashboardStats` is a [DataSource](/components/DataSource).
30 |
31 | ```xmlui
32 | <DataSource id="dashboardStats" url="/api/dashboard/stats" method="GET" />
33 | ```
34 |
35 | It returns a structure like the variable we've defined above: an object with a `value` key that points to a list of objects corresponding to rows in the database. In this case there's only one row because the query behind `/api/dashboard/stats` reports multiple values (count of invoices, total amount outstanding, total amount paid this year, etc) as columns in that single row.
36 |
37 | ## A custom Card
38 |
39 | There are five infocards on the XMLUI Invoice dashboard. To style them all in a consistent way, the app defines an `InfoCard` that wraps `Card` and `Text`.
40 |
41 |
42 | ```xmlui
43 | <Component name="InfoCard">
44 |
45 | <Card width="{$props.width}" borderRadius="8px" boxShadow="$boxShadow-spread">
46 |
47 | <Text>{$props.title}</Text>
48 |
49 | <Text fontWeight="$fontWeight-extra-bold" fontSize="larger">
50 | { $props.currency === 'true' ? '$' + $props.value : $props.value }
51 | </Text>
52 | </Card>
53 |
54 | </Component>
55 | ```
56 |
57 | These are in turn wrapped in a `Dashboard` that passes properties to `InfoCard`: `title`, `width`, `value`, and optionally a `currency` flag for `$` formatting.
58 |
59 |
60 | ```xmlui
61 | <Component name="Dashboard">
62 |
63 | <DataSource id="dashboardStats" url="/api/dashboard/stats" method="GET"/>
64 |
65 | <HStack>
66 | <H1>Dashboard</H1>
67 | <SpaceFiller/>
68 | <Button label="Create Invoice" onClick="navigate('/invoices/new')"/>
69 | </HStack>
70 |
71 | <FlowLayout>
72 | <InfoCard
73 | width="20%"
74 | title="Outstanding"
75 | value="{ dashboardStats.value[0] }"
76 | currency='true'
77 | />
78 | <InfoCard
79 | ...
80 | />
81 | <InfoCard
82 | ...
83 | />
84 | <InfoCard
85 | ...
86 | />
87 | <InfoCard
88 | ...
89 | />
90 |
91 | <Statuses width="50%" title="Statuses"/>
92 |
93 | <MonthlyStatus width="50%" title="Monthly Status"/>
94 |
95 | </FlowLayout>
96 |
97 | <DailyRevenue title="Daily Revenue"/>
98 |
99 | </Component>
100 | ```
101 |
102 | A [user-defined component](/components-intro) like `Dashboard` can define any set of names and values. `InfoCard` receives them in its `$props` [context variable](/context-variables). `InfoCard` is opinionated about `borderRadius` and `boxShadow`. It could also receive these in `$props` but chooses not to. And while it is strongly opinionated about the `borderRadius`, which it hardcodes, it is willing to use the theme variable `$boxShadow-spread` so that setting can be theme-governed.
103 |
104 | Here's a more complete version of the row of `InfoCard`s used in the Invoices app.
105 |
106 |
107 | ```xmlui-pg noHeader
108 | ---app display
109 | <App>
110 | <Dashboard />
111 | </App>
112 | ---comp
113 | <Component name="InfoCard">
114 |
115 | <Card width="{$props.width}" borderRadius="8px" boxShadow="$boxShadow-spread">
116 |
117 | <Text>{$props.title}</Text>
118 |
119 | <Text fontWeight="$fontWeight-extra-bold" fontSize="larger">
120 | { $props.currency === 'true' ? '$' + $props.value : $props.value }
121 | </Text>
122 | </Card>
123 |
124 | </Component>
125 | ---comp
126 | <Component name="Dashboard">
127 |
128 | <variable name="dashboardStats" value="{
129 | {
130 | value:
131 | [
132 | {
133 | outstanding: 3569,
134 | paid_this_year: 1745,
135 | draft_invoices: 6,
136 | sent_invoices: 7,
137 | paid_invoices: 79
138 | }
139 | ]
140 | }
141 | }" />
142 |
143 |
144 | <HStack>
145 | <InfoCard
146 | width="20%"
147 | title="Outstanding"
148 | value="{ dashboardStats.value[0].outstanding }"
149 | currency='true'
150 | />
151 | <InfoCard
152 | width="*"
153 | title="Paid"
154 | value="{dashboardStats.value[0].paid_this_year}"
155 | currency='true'
156 | />
157 | <InfoCard
158 | width="*"
159 | title="Draft"
160 | value="{dashboardStats.value[0].draft_invoices}"
161 | />
162 | <InfoCard
163 | width="*"
164 | title="Sent"
165 | value="{dashboardStats.value[0].sent_invoices}"
166 | />
167 | <InfoCard
168 | width="*"
169 | title="Paid"
170 | value="{dashboardStats.value[0].paid_invoices}"
171 | />
172 | </HStack>
173 |
174 | </Component>
175 | ```
176 |
177 |
```
--------------------------------------------------------------------------------
/xmlui/src/components-core/interception/ReadonlyCollection.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { orderBy } from "../utils/misc"
2 |
3 | export type GroupItem<K, V> = {
4 | key: K,
5 | items: V[]
6 | }
7 |
8 | export class ReadOnlyCollection<T = any> {
9 | private readonly list: T[]
10 |
11 | constructor(items: Iterable<T>) {
12 | this.list = [];
13 | for (const item of items) {
14 | this.list.push(item);
15 | }
16 | }
17 |
18 | get length(): number {
19 | return this.list.length;
20 | }
21 |
22 | toArray(): T[] {
23 | return this.list.slice(0);
24 | }
25 |
26 | at(index: number): T | undefined {
27 | if (index < 0 || index > this.list.length - 1) {
28 | throw new Error(`Index (${index}) is out of range [0..${Math.max(this.list.length - 1, 0)}]`)
29 | }
30 | return this.list[index];
31 | }
32 |
33 | all(): ReadOnlyCollection<T> {
34 | return new ReadOnlyCollection<T>(this.list);
35 | }
36 |
37 | async single(predicate: (item: T) => Promise<boolean>): Promise<T | undefined> {
38 | const filtered = await this.where(predicate);
39 | if (filtered.length === 0) {
40 | throw new Error("No items found with the specified predicate")
41 | } else if (filtered.length > 1) {
42 | throw new Error("Multiple items found with the specified predicate")
43 | }
44 | return filtered.at(0);
45 | }
46 |
47 | async singleOrDefault(predicate: (item: T) => Promise<boolean>, defValue?: T): Promise<T | undefined> {
48 | const filtered = await this.where(predicate);
49 | if (filtered.length === 0) {
50 | return defValue;
51 | } else if (filtered.length > 1) {
52 | throw new Error("Multiple items found with the specified predicate")
53 | }
54 | return filtered.at(0);
55 | }
56 |
57 | async where(predicate: (item: T) => Promise<boolean>): Promise<ReadOnlyCollection<T>> {
58 | return new ReadOnlyCollection(await this.whereAsArray(predicate));
59 | }
60 |
61 | async whereAsArray(predicate: (item: T) => Promise<boolean>): Promise<T[]> {
62 | const results = await Promise.all(this.list.map(predicate ?? (async () => true)));
63 | return this.list.filter((_v, index) => results[index]);
64 | }
65 |
66 | async orderBy(...mappers: any[]): Promise<ReadOnlyCollection<T>> {
67 | return new ReadOnlyCollection(await orderBy(this.list, ...mappers))
68 | }
69 |
70 | async orderByAsArray(...mappers: any[]): Promise<T[]> {
71 | return await orderBy(this.list, ...mappers);
72 | }
73 |
74 | async groupBy<D>(groupKey: (item: T) => Promise<D>): Promise<ReadOnlyCollection<GroupItem<D, T>>> {
75 | return new ReadOnlyCollection(await this.groupByAsArray(groupKey));
76 | }
77 |
78 | async groupByAsArray<D>(groupKey: (item: T) => Promise<D>): Promise<GroupItem<D, T>[]> {
79 | const grouped = new Map<D, T[]>();
80 | for (const item of this.list) {
81 | const key = await groupKey(item);
82 | const matchingList = grouped.get(key);
83 | if (matchingList === undefined) {
84 | grouped.set(key, [item]);
85 | } else {
86 | matchingList.push(item)
87 | }
88 | }
89 | const groupedItems: GroupItem<D, T>[] = [];
90 | for (const [key, items] of grouped.entries()) {
91 | groupedItems.push({ key, items } )
92 | }
93 | return groupedItems;
94 | }
95 |
96 | async distinct<D>(distinctValue?: (item: T) => Promise<D>): Promise<ReadOnlyCollection<D>> {
97 | distinctValue ??= async (x: T) => x as unknown as D;
98 | const values = new Set<D>();
99 | for (const item of this.list) {
100 | values.add(await distinctValue(item));
101 | }
102 | return new ReadOnlyCollection<D>(values.values());
103 | }
104 |
105 | async distinctAsArray<D>(distinctValue?: (item: T) => Promise<D>): Promise<D[]> {
106 | return (await this.distinct(distinctValue)).toArray();
107 | }
108 |
109 | async maxValue(fieldName = "id", predicate?: (item: T) => Promise<boolean>): Promise<any | null> {
110 | let values = predicate ? (await new ReadOnlyCollection(this.list)
111 | .where(predicate)).toArray() : this.list;
112 | values = await orderBy(values, (item: T) => (item as any)[fieldName], true);
113 | return values.length === 0 ? undefined : (values[0] as any)[fieldName];
114 | }
115 |
116 | async skip(count: number): Promise<ReadOnlyCollection<T>> {
117 | return new ReadOnlyCollection<T>(this.list.slice(count));
118 | }
119 |
120 | async take(count: number): Promise<ReadOnlyCollection<T>> {
121 | return new ReadOnlyCollection<T>(this.list.slice(0, count));
122 | }
123 |
124 | async skipTake(skip: number, take: number): Promise<ReadOnlyCollection<T>> {
125 | return new ReadOnlyCollection<T>(this.list.slice(skip, take + skip));
126 | }
127 |
128 | }
```
--------------------------------------------------------------------------------
/xmlui/src/components/Slider/Slider.module.scss:
--------------------------------------------------------------------------------
```scss
1 |
2 | @use "../../components-core/theming/themes" as t;
3 |
4 | // --- This code snippet is required to collect the theme variables used in this module
5 | $themeVars: ();
6 | @function createThemeVar($componentVariable) {
7 | $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
8 | @return t.getThemeVar($themeVars, $componentVariable);
9 | }
10 |
11 | $componentName: "Slider";
12 |
13 | // Basic variant mixin for consistent theming
14 | @mixin variant($variantName) {
15 | border-radius: createThemeVar("Input:borderRadius-#{$componentName}-#{$variantName}");
16 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}");
17 | border-width: createThemeVar("Input:borderWidth-#{$componentName}-#{$variantName}");
18 | border-style: createThemeVar("Input:borderStyle-#{$componentName}-#{$variantName}");
19 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}");
20 |
21 | &:hover {
22 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--hover");
23 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--hover");
24 | }
25 |
26 | &:focus-within {
27 | border-color: createThemeVar("Input:borderColor-#{$componentName}-#{$variantName}--focus");
28 | box-shadow: createThemeVar("Input:boxShadow-#{$componentName}-#{$variantName}--focus");
29 | }
30 | }
31 |
32 | @layer components {
33 | // Container for the slider
34 | .sliderContainer {
35 | width: 100%;
36 | position: relative;
37 | padding: t.$space-2 0;
38 | }
39 |
40 | // Root element
41 | .sliderRoot {
42 | position: relative;
43 | display: flex;
44 | width: 100%;
45 | touch-action: none;
46 | user-select: none;
47 | align-items: center;
48 | height: 20px;
49 | }
50 |
51 | // Track element
52 | .sliderTrack {
53 | position: relative;
54 | height: 8px;
55 | width: 100%;
56 | flex-grow: 1;
57 | overflow: hidden;
58 | border-radius: 9999px;
59 | background-color: createThemeVar("Input:backgroundColor-track-#{$componentName}");
60 |
61 | @include variant("default");
62 |
63 | &.error {
64 | @include variant("error");
65 | }
66 | &.warning {
67 | @include variant("warning");
68 | }
69 | &.valid {
70 | @include variant("success");
71 | }
72 |
73 | &.disabled, &[data-disabled] {
74 | background-color: createThemeVar("Input:backgroundColor-track-#{$componentName}--disabled");
75 | }
76 |
77 | &:not(.disabled):not([data-disabled]):hover {
78 | cursor: pointer;
79 | }
80 | }
81 |
82 | // Range element
83 | .sliderRange {
84 | position: absolute;
85 | height: 100%;
86 | background-color: createThemeVar("Input:backgroundColor-range-#{$componentName}");
87 | border-radius: 9999px;
88 |
89 | &.disabled, &[data-disabled] {
90 | background-color: createThemeVar("Input:backgroundColor-range-#{$componentName}--disabled");
91 | }
92 | }
93 |
94 | // Thumb element
95 | .sliderThumb {
96 | cursor: pointer;
97 | display: block;
98 | width: 20px;
99 | height: 20px;
100 | border-radius: 50%;
101 | border: createThemeVar("Input:borderWidth-thumb-#{$componentName}")
102 | createThemeVar("Input:borderStyle-thumb-#{$componentName}")
103 | createThemeVar("Input:borderColor-thumb-#{$componentName}");
104 | background-color: createThemeVar("Input:backgroundColor-thumb-#{$componentName}");
105 | box-shadow: createThemeVar("Input:boxShadow-thumb-#{$componentName}");
106 | position: relative;
107 | transition: background-color 0.2s, box-shadow 0.2s;
108 | outline: none;
109 |
110 | &.disabled, &[data-disabled] {
111 | pointer-events: none;
112 | opacity: 0.5;
113 | }
114 |
115 | &:focus-visible, &[data-focus] {
116 | background-color: createThemeVar("Input:backgroundColor-thumb-#{$componentName}--focus");
117 | box-shadow: createThemeVar("Input:boxShadow-thumb-#{$componentName}--focus");
118 | }
119 | &:hover, &[data-hover] {
120 | background-color: createThemeVar("Input:backgroundColor-thumb-#{$componentName}--hover");
121 | box-shadow: createThemeVar("Input:boxShadow-thumb-#{$componentName}--hover");
122 | }
123 | &:active, &[data-active] {
124 | background-color: createThemeVar("Input:backgroundColor-thumb-#{$componentName}--active");
125 | box-shadow: createThemeVar("Input:boxShadow-thumb-#{$componentName}--active");
126 | }
127 | }
128 |
129 | /* States */
130 | .disabled {
131 | opacity: 0.5;
132 | cursor: not-allowed;
133 | }
134 |
135 | .readOnly {
136 | opacity: 0.7;
137 | cursor: default;
138 | }
139 |
140 | .error {
141 | border-color: createThemeVar("Input:borderColor-#{$componentName}-error");
142 | }
143 |
144 | .warning {
145 | border-color: createThemeVar("Input:borderColor-#{$componentName}-warning");
146 | }
147 |
148 | .valid {
149 | border-color: createThemeVar("Input:borderColor-#{$componentName}-success");
150 | }
151 | }
152 |
153 | // --- We export the theme variables to add them to the component renderer
154 | :export {
155 | themeVars: t.json-stringify($themeVars);
156 | }
157 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/Splitter/HSplitter.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { getBounds } from "../../testing/component-test-helpers";
2 | import { expect, test } from "../../testing/fixtures";
3 |
4 | // =============================================================================
5 | // BASIC FUNCTIONALITY TESTS
6 | // =============================================================================
7 |
8 | test.describe("Basic Functionality", () => {
9 | test("renders with basic setup", async ({ initTestBed, page }) => {
10 | await initTestBed(`
11 | <HSplitter height="200px" width="400px" testId="hsplitter">
12 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/>
13 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/>
14 | </HSplitter>
15 | `);
16 |
17 | await expect(page.getByTestId("hsplitter")).toBeVisible();
18 | await expect(page.getByTestId("primary")).toBeVisible();
19 | await expect(page.getByTestId("secondary")).toBeVisible();
20 | });
21 |
22 | test("defaults to horizontal orientation", async ({ initTestBed, page }) => {
23 | await initTestBed(`
24 | <HSplitter height="200px" width="400px" testId="hsplitter">
25 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/>
26 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/>
27 | </HSplitter>
28 | `);
29 |
30 | const primary = page.getByTestId("primary");
31 | const secondary = page.getByTestId("secondary");
32 |
33 | const primaryBounds = await getBounds(primary);
34 | const secondaryBounds = await getBounds(secondary);
35 |
36 | // In horizontal orientation, primary should be to the left of secondary
37 | expect(primaryBounds.right).toBeLessThanOrEqual(secondaryBounds.left + 10); // Allow for small overlap due to resizer
38 | });
39 |
40 | test("ignores orientation property when explicitly set", async ({ initTestBed, page }) => {
41 | await initTestBed(`
42 | <HSplitter height="200px" width="400px" orientation="vertical" testId="hsplitter">
43 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/>
44 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/>
45 | </HSplitter>
46 | `);
47 |
48 | const primary = page.getByTestId("primary");
49 | const secondary = page.getByTestId("secondary");
50 |
51 | const primaryBounds = await getBounds(primary);
52 | const secondaryBounds = await getBounds(secondary);
53 |
54 | // Even with orientation="vertical", HSplitter should still be horizontal
55 | // Primary should be to the left of secondary, NOT above it
56 | expect(primaryBounds.right).toBeLessThanOrEqual(secondaryBounds.left + 10);
57 | });
58 |
59 | test("supports all Splitter properties except orientation", async ({ initTestBed, page, createSplitterDriver }) => {
60 | await initTestBed(`
61 | <HSplitter
62 | height="200px"
63 | width="400px"
64 | initialPrimarySize="30%"
65 | minPrimarySize="20%"
66 | maxPrimarySize="80%"
67 | floating="false"
68 | testId="hsplitter"
69 | >
70 | <Stack backgroundColor="lightblue" height="100%" testId="primary"/>
71 | <Stack backgroundColor="darksalmon" height="100%" testId="secondary"/>
72 | </HSplitter>
73 | `);
74 |
75 | const hsplitter = page.getByTestId("hsplitter");
76 | const primary = page.getByTestId("primary");
77 | const driver = await createSplitterDriver(hsplitter);
78 |
79 | // Verify initial size is applied
80 | const hsplitterBounds = await getBounds(hsplitter);
81 | const primaryBounds = await getBounds(primary);
82 |
83 | const expectedWidth = hsplitterBounds.width * 0.3;
84 | const tolerance = 15;
85 | expect(Math.abs(primaryBounds.width - expectedWidth)).toBeLessThan(tolerance);
86 |
87 | // Verify resizer is visible (floating=false)
88 | const resizer = await driver.getResizer();
89 | await expect(resizer).toBeVisible();
90 | });
91 | });
92 |
93 | // =============================================================================
94 | // ACCESSIBILITY TESTS
95 | // =============================================================================
96 |
97 | test.describe("Accessibility", () => {
98 | test("resizer has horizontal cursor style", async ({ initTestBed, page, createSplitterDriver }) => {
99 | await initTestBed(`
100 | <HSplitter height="200px" width="400px" testId="hsplitter">
101 | <Stack backgroundColor="lightblue" height="100%"/>
102 | <Stack backgroundColor="darksalmon" height="100%"/>
103 | </HSplitter>
104 | `);
105 |
106 | const hsplitter = page.getByTestId("hsplitter");
107 | const driver = await createSplitterDriver(hsplitter);
108 | const resizer = await driver.getResizer();
109 |
110 | // HSplitter should always use horizontal cursor (ew-resize)
111 | await expect(resizer).toHaveCSS("cursor", "ew-resize");
112 | });
113 | });
114 |
```
--------------------------------------------------------------------------------
/xmlui/dev-docs/next/xmlui-wcag-accessibility-report.md:
--------------------------------------------------------------------------------
```markdown
1 | # XMLUI WCAG 2.1 AA Accessibility Audit Report
2 |
3 | **Audit Date:** July 26, 2025
4 | **Tool Used:** Pa11y (WCAG2AA standard)
5 | **URL Tested:** http://localhost:5173 (XMLUI Documentation Site)
6 | **Total Issues Found:** 24 Errors
7 |
8 | ## Executive Summary
9 |
10 | The accessibility audit of the XMLUI documentation site revealed several WCAG 2.1 AA compliance issues that need to be addressed. The primary concerns are:
11 |
12 | 1. **Duplicate ID Attributes** - Critical HTML validation issues
13 | 2. **Missing Button Labels** - Accessibility API naming problems
14 | 3. **Color Contrast Issues** - Multiple instances of insufficient contrast ratios
15 | 4. **Anchor Link Contrast** - Link visibility concerns
16 |
17 | ## Detailed Findings
18 |
19 | ### 1. Critical Issues (Must Fix)
20 |
21 | #### Duplicate ID Attributes (3 instances)
22 | **WCAG Guideline:** 4.1.1 - Parsing
23 | **Severity:** Critical
24 |
25 | - `id=":rbd:"` - Search input element
26 | - `id=":rp:"` - Checkbox/switch element
27 | - `id="introduction"` - Anchor span element
28 |
29 | **Impact:** These duplicate IDs break HTML validity and can cause assistive technology confusion.
30 |
31 | **Recommendation:** Ensure all ID attributes are unique across the page.
32 |
33 | #### Missing Button Names (2 instances)
34 | **WCAG Guideline:** 4.1.2 - Name, Role, Value
35 | **Severity:** High
36 |
37 | Two button elements lack accessible names for screen readers:
38 | - Header button elements with only SVG content
39 | - No aria-label, title, or text content provided
40 |
41 | **Recommendation:** Add `aria-label` attributes to describe button functionality.
42 |
43 | ### 2. Color Contrast Issues (18 instances)
44 |
45 | #### Code Syntax Highlighting Contrast Problems
46 | **WCAG Guideline:** 1.4.3 - Contrast (Minimum)
47 | **Required Ratio:** 4.5:1 for normal text, 3:1 for large text
48 |
49 | Multiple code elements have insufficient contrast:
50 |
51 | - **Color #66748E:** 4.06:1 ratio (needs 4.5:1)
52 | - Recommendation: Change background to #f7faff
53 | - **Color #0074A9:** 4.44:1 ratio (needs 4.5:1)
54 | - Recommendation: Change background to #ebf0fb
55 | - **Color #F07100:** 2.56:1 ratio (severely insufficient)
56 | - Recommendation: Change text color to #070300
57 | - **Color #2F86D2:** 3.31:1 ratio (insufficient)
58 | - Recommendation: Change text color to #000306
59 |
60 | #### Anchor Link Contrast
61 | - **Anchor hash links:** 2.8:1 ratio (needs 3:1 minimum)
62 | - Recommendation: Change text color to #8196b4
63 |
64 | ## Priority Recommendations
65 |
66 | ### Immediate Actions (High Priority)
67 | 1. **Fix Duplicate IDs** - Generate unique IDs for all elements
68 | 2. **Add Button Labels** - Implement aria-label for all unlabeled buttons
69 | 3. **Fix Severe Contrast Issues** - Address colors with ratios below 3:1
70 |
71 | ### Short-term Actions (Medium Priority)
72 | 1. **Improve Code Syntax Colors** - Adjust syntax highlighting color scheme
73 | 2. **Review Theme Colors** - Audit all theme colors for WCAG compliance
74 | 3. **Test with Screen Readers** - Validate fixes with assistive technology
75 |
76 | ### Long-term Actions (Lower Priority)
77 | 1. **Implement Automated Testing** - Add accessibility testing to CI/CD pipeline
78 | 2. **Create Accessibility Guidelines** - Document color contrast requirements
79 | 3. **Regular Audits** - Schedule periodic accessibility reviews
80 |
81 | ## Technical Implementation Notes
82 |
83 | ### Code Syntax Highlighting Fix
84 | ```css
85 | /* Recommended color adjustments for syntax highlighting */
86 | .syntax-punctuation { color: #66748E; background: #f7faff; }
87 | .syntax-string { color: #0074A9; background: #ebf0fb; }
88 | .syntax-keyword { color: #070300; } /* was #F07100 */
89 | .syntax-property { color: #000306; } /* was #2F86D2 */
90 | ```
91 |
92 | ### Button Accessibility Fix
93 | ```html
94 | <!-- Before -->
95 | <button class="_headerButton_1b3x4_231" data-state="closed">
96 | <svg>...</svg>
97 | </button>
98 |
99 | <!-- After -->
100 | <button class="_headerButton_1b3x4_231" data-state="closed" aria-label="Toggle menu">
101 | <svg>...</svg>
102 | </button>
103 | ```
104 |
105 | ## Compliance Status
106 |
107 | - **WCAG 2.1 A:** ❌ Non-compliant (duplicate IDs, missing labels)
108 | - **WCAG 2.1 AA:** ❌ Non-compliant (contrast issues)
109 | - **WCAG 2.1 AAA:** ❌ Non-compliant (multiple issues)
110 |
111 | ## Next Steps
112 |
113 | 1. Address critical duplicate ID issues immediately
114 | 2. Add proper button labeling for screen reader support
115 | 3. Implement color contrast fixes in the theme system
116 | 4. Test fixes with automated tools and manual testing
117 | 5. Document accessibility standards for the project
118 |
119 | ## Tools and Testing Environment
120 |
121 | - **Testing Tool:** Pa11y v6.x with WCAG2AA ruleset
122 | - **Browser:** Chrome (headless via Pa11y)
123 | - **Testing Date:** July 26, 2025
124 | - **Base URL:** http://localhost:5173
125 |
126 | ---
127 |
128 | *This report provides a baseline accessibility assessment. Regular testing and monitoring are recommended to maintain compliance as the application evolves.*
```
--------------------------------------------------------------------------------
/xmlui/src/components/Slot/Slot.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | ## Using Slot
4 |
5 | You can add `Slot` to a user-defined component as a placeholder. When you refer to the particular component in the markup, the children are transposed to the `Slot`.
6 |
7 | ```xmlui-pg name="Using Slot"
8 | ---app copy display {3-5}
9 | <App name="XMLUI Hello World">
10 | <ActionBar>
11 | <Button label="Create" onClick="window.alert('Create clicked')" />
12 | <Button label="Edit" onClick="window.alert('Edit clicked')" />
13 | <Button label="Delete" onClick="window.alert('Delete clicked')" />
14 | </ActionBar>
15 | </App>
16 | ---desc
17 | The app flows down three buttons to the `ActionBar` to render.
18 | ---comp copy display {5}
19 | <Component name="ActionBar">
20 | <Card>
21 | <H3>Use these actions</H3>
22 | <HStack>
23 | <Slot />
24 | </HStack>
25 | </Card>
26 | </Component>
27 | ---desc
28 | `ActionBar` renders the passed children by replacing `Slot` with them.
29 | ```
30 |
31 | ## Default Slot content
32 |
33 | You can provide default content for the `Slot`. If the user-defined component does not have any children, XMLUI will render the default content.
34 |
35 | ```xmlui-pg
36 | ---app copy display name="Define default Slot content"
37 | <App>
38 | <ActionBar />
39 | </App>
40 | ---comp copy display {6}
41 | <Component name="ActionBar">
42 | <Card>
43 | <H3>Use these actions</H3>
44 | <HStack>
45 | <Slot>
46 | <Button label="Default" onClick="window.alert('Default clicked')" />
47 | </Slot>
48 | </HStack>
49 | </Card>
50 | </Component>
51 | ```
52 |
53 | ## Named Slots
54 |
55 | You can add multiple slots to a user-defined component; you can have a default slot and several *named* slots. Slot names should end with `template`, and you can use the `<property>` markup syntax to declare their values.
56 |
57 | ```xmlui-pg
58 | ---app copy display name="Using named Slots" {4, 7, 9-11}
59 | <App>
60 | <ActionBar>
61 | <property name="headerTemplate">
62 | <H2>Click one of these actions</H2>
63 | </property>
64 | <property name="footerTemplate">
65 | <Text>Footer content goes here</Text>
66 | </property>
67 | <Button label="Create" onClick="window.alert('Create clicked')" />
68 | <Button label="Edit" onClick="window.alert('Edit clicked')" />
69 | <Button label="Delete" onClick="window.alert('Delete clicked')" />
70 | </ActionBar>
71 | </App>
72 | ---desc
73 | This app passes a header template and a footer template slot to the `ActionBar` component and also declares buttons to render.
74 | ---comp copy display {3-5, 7-9, 11}
75 | <Component name="ActionBar">
76 | <Card>
77 | <Slot name="headerTemplate">
78 | <H3>Use these actions</H3>
79 | </Slot>
80 | <HStack>
81 | <Slot>
82 | <Button label="Default" onClick="window.alert('Default clicked')" />
83 | </Slot>
84 | </HStack>
85 | <Slot name="footerTemplate" />
86 | </Card>
87 | </Component>
88 | ---desc
89 | XMLUI finds the appropriate slots by their name and transposes their content received from the app. Just like the default slot, named slots can have default content.
90 | ```
91 |
92 | > [!WARN] XMLUI will display an error message when the `Slot` name does not end with "Template".
93 |
94 |
95 | ## Template properties
96 |
97 | The user-defined component can provide properties for the actual template.
98 |
99 | ```xmlui-pg
100 | ---app copy display name="Using template properties" /header/ /name="headerTemplate"/ /$processedHeader/
101 | <App>
102 | <ActionBar header="Action Bar Example">
103 | <property name="headerTemplate">
104 | <Text variant="title">{$processedHeader}</Text>
105 | </property>
106 | <Button label="Create" onClick="window.alert('Create clicked')" />
107 | <Button label="Edit" onClick="window.alert('Edit clicked')" />
108 | <Button label="Delete" onClick="window.alert('Delete clicked')" />
109 | </ActionBar>
110 | </App>
111 | ---desc
112 | The app passes a `header` property value to the `ActionBar` component. `Actionbar` utilizes this property, transforms it, and passes it back to the template in the `$processedHeader` context variable so that the app can use it. `$processHeader` is available only within the `headerTemplate` slot.
113 | ---comp copy display /transformedHeader/ /processedHeader="{transformedHeader}"/
114 | <Component name="ActionBar">
115 | <Card var.transformedHeader="*** {$props.header.toUpperCase()} ***">
116 | <Slot name="headerTemplate" processedHeader="{transformedHeader}" >
117 | <H3>{transformedHeader}</H3>
118 | </Slot>
119 | <HStack>
120 | <Slot>
121 | <Button label="Default" onClick="window.alert('Default clicked')" />
122 | </Slot>
123 | </HStack>
124 | </Card>
125 | </Component>
126 | ---desc
127 | `Actionbar` transforms the `header` property and stores it internally in the `transformedHeader` variable. It utilizes the value in the default header definition and also passes it back to the actual template definition with the `processedHeader` name. XMLUI creates the `$processedHeader` context variable from this name.
128 | ```
129 |
130 | %-DESC-END
```
--------------------------------------------------------------------------------
/xmlui/src/components/FlowLayout/FlowLayout.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | For details on how to work with \`FlowLayout\` (like sizing children), see [this guide](/layout#flowlayout).
4 |
5 | ## Using `SpaceFiller` with `FlowLayout`
6 |
7 | The `SpaceFiller` component can be used as a line break.
8 | See the [reference docs](/components/SpaceFiller) for details.
9 |
10 | %-DESC-END
11 |
12 | %-PROP-START gap
13 |
14 | The `gap` property defines the gap between items in the same row and between rows. The `FlowLayout` component creates a new row when an item is about to overflow the current row.
15 |
16 | ```xmlui-pg copy display name="Example: gap"
17 | ---app copy display
18 | <App>
19 | <FlowLayout gap="$space-12">
20 | <Stack width="25%" height="32px" backgroundColor="red" />
21 | <Stack width="25%" height="32px" backgroundColor="blue" />
22 | <Stack width="25%" height="32px" backgroundColor="green" />
23 | <Stack width="25%" height="32px" backgroundColor="yellow" />
24 | <Stack width="25%" height="32px" backgroundColor="maroon" />
25 | <Stack width="25%" height="32px" backgroundColor="teal" />
26 | <Stack width="25%" height="32px" backgroundColor="seagreen" />
27 | <Stack width="25%" height="32px" backgroundColor="olive" />
28 | </FlowLayout>
29 | </App>
30 | ---desc
31 | In this markup, only four items fit in a single row.
32 | The `gap` property sets the same gaps within and between rows.
33 | ```
34 |
35 | This markup demonstrates different `gap` values:
36 |
37 | ```xmlui-pg copy display name="Example: different size units"
38 | ---app copy display
39 | <App>
40 | <FlowLayout>
41 | <Stack width="25%" height="32px" backgroundColor="red" />
42 | <Stack width="25%" height="32px" backgroundColor="blue" />
43 | <Stack width="25%" height="32px" backgroundColor="green" />
44 | <Stack width="25%" height="32px" backgroundColor="yellow" />
45 | </FlowLayout>
46 | <FlowLayout gap="10px">
47 | <Stack width="25%" height="32px" backgroundColor="red" />
48 | <Stack width="25%" height="32px" backgroundColor="blue" />
49 | <Stack width="25%" height="32px" backgroundColor="green" />
50 | <Stack width="25%" height="32px" backgroundColor="yellow" />
51 | </FlowLayout>
52 | <FlowLayout gap="1rem">
53 | <Stack width="25%" height="32px" backgroundColor="red" />
54 | <Stack width="25%" height="32px" backgroundColor="blue" />
55 | <Stack width="25%" height="32px" backgroundColor="green" />
56 | <Stack width="25%" height="32px" backgroundColor="yellow" />
57 | </FlowLayout>
58 | <FlowLayout gap="4ch">
59 | <Stack width="25%" height="32px" backgroundColor="red" />
60 | <Stack width="25%" height="32px" backgroundColor="blue" />
61 | <Stack width="25%" height="32px" backgroundColor="green" />
62 | <Stack width="25%" height="32px" backgroundColor="yellow" />
63 | </FlowLayout>
64 | </App>
65 | ---desc
66 | All items within a `FlowLayout` instance fit in a single row, so `gap` affects only the space between items. The space between rows comes from the outermost `Stack`.
67 | ```
68 |
69 | %-PROP-END
70 |
71 | %-PROP-START columnGap
72 |
73 | The `columnGap` property specifies the space between items in a single row; it overrides the `gap` value.
74 |
75 | ```xmlui-pg copy display name="Example: columnGap"
76 | ---app copy display
77 | <App>
78 | <FlowLayout columnGap="$space-8">
79 | <Stack width="25%" height="32px" backgroundColor="red" />
80 | <Stack width="25%" height="32px" backgroundColor="blue" />
81 | <Stack width="25%" height="32px" backgroundColor="green" />
82 | <Stack width="25%" height="32px" backgroundColor="yellow" />
83 | <Stack width="25%" height="32px" backgroundColor="maroon" />
84 | <Stack width="25%" height="32px" backgroundColor="teal" />
85 | <Stack width="25%" height="32px" backgroundColor="seagreen" />
86 | <Stack width="25%" height="32px" backgroundColor="olive" />
87 | </FlowLayout>
88 | </App>
89 | ---desc
90 | You can observe no gap between the rows of the `FlowLayout`, as `columnGap` keeps the space between rows intact:
91 | ```
92 |
93 | %-PROP-END
94 |
95 | %-PROP-START rowGap
96 |
97 | The `rowGap` property specifies the space between the `FlowLayout` rows; it overrides the `gap` value.
98 |
99 | ```xmlui-pg copy display name="Example: rowGap"
100 | ---app copy display
101 | <App>
102 | <FlowLayout rowGap="2px">
103 | <Stack width="25%" height="32px" backgroundColor="red" />
104 | <Stack width="25%" height="32px" backgroundColor="blue" />
105 | <Stack width="25%" height="32px" backgroundColor="green" />
106 | <Stack width="25%" height="32px" backgroundColor="yellow" />
107 | <Stack width="25%" height="32px" backgroundColor="maroon" />
108 | <Stack width="25%" height="32px" backgroundColor="teal" />
109 | <Stack width="25%" height="32px" backgroundColor="seagreen" />
110 | <Stack width="25%" height="32px" backgroundColor="olive" />
111 | </FlowLayout>
112 | </App>
113 | ---desc
114 | You can observe no gap between the items in a single row of the `FlowLayout`, as `rowGap` keeps the gap within a row intact:
115 | ```
116 |
117 | %-PROP-END
118 |
```