This is page 55 of 189. Use http://codebase.md/xmlui-org/xmlui/xmlui/mockApiDef.js?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ ├── cyan-tools-design.md
│ ├── every-moments-teach.md
│ ├── fancy-laws-drop.md
│ ├── full-symbols-accept.md
│ └── tricky-zoos-crash.md
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog-optimized.yml
│ ├── deploy-blog-swa.yml
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs-swa.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.gif
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── codefence-runner.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
│ │ ├── staticwebapp.config.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
│ │ │ │ ├── debounce-user-input-for-api-calls.md
│ │ │ │ ├── debounce-with-changelistener.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.module.scss
│ │ │ ├── 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.tsx
│ │ │ ├── StandalonePlayground.tsx
│ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ ├── ThemeSwitcher.module.scss
│ │ │ ├── ThemeSwitcher.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
│ ├── component-metadata.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
│ ├── theme-variables-refactoring.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── extract-component-metadata.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
│ ├── generate-metadata-markdown.js
│ ├── 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.module.scss
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ └── 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
│ │ │ └── test-padding.xmlui
│ │ ├── 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.json
├── tsdown.config.ts
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/language-server/services/completion.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { MarkupContent, CompletionItem, Position } from "vscode-languageserver";
2 | import { CompletionItemKind, MarkupKind } from "vscode-languageserver";
3 | import type { GetText, ParseResult } from "../../parsers/xmlui-parser/parser";
4 | import { findTokenAtPos } from "../../parsers/xmlui-parser/utils";
5 | import { SyntaxKind } from "../../parsers/xmlui-parser/syntax-kind";
6 | import type { Node } from "../../parsers/xmlui-parser/syntax-node";
7 | import * as docGen from "./common/docs-generation";
8 | import {
9 | compNameForTagNameNode,
10 | findTagNameNodeInStack,
11 | getFirstNodeFromAncestorChain,
12 | insideClosingTag,
13 | visitAncestorsInChain,
14 | } from "./common/syntax-node-utilities";
15 | import {
16 | addOnPrefix,
17 | type AttributeKind,
18 | type MetadataProvider,
19 | type TaggedAttribute,
20 | } from "./common/metadata-utils";
21 |
22 | type Override<Type, NewType extends { [key in keyof Type]?: NewType[key] }> = Omit<
23 | Type,
24 | keyof NewType
25 | > &
26 | NewType;
27 |
28 | /**
29 | * Additional data that a completion item contains.
30 | * with that, a completion item can be resolved, thus
31 | * the sever can query the documentation of a component,
32 | * prop, event etc...
33 | */
34 | type XmluiCompletionData = {
35 | metadataAccessInfo: {
36 | componentName: string;
37 | attribute?: TaggedAttribute;
38 | };
39 | };
40 |
41 | export type XmluiCompletionItem = Override<CompletionItem, { data?: XmluiCompletionData }>;
42 |
43 | type CompletionResolveContext = {
44 | item: XmluiCompletionItem;
45 | metaByComp: MetadataProvider;
46 | };
47 |
48 | export function handleCompletionResolve({
49 | item,
50 | metaByComp: metaByComp,
51 | }: CompletionResolveContext): CompletionItem {
52 | const metadataAccessInfo = item?.data?.metadataAccessInfo;
53 | if (metadataAccessInfo) {
54 | const { componentName, attribute } = metadataAccessInfo;
55 | const componentMeta = metaByComp.getComponent(componentName);
56 | if (!componentMeta) {
57 | return null;
58 | }
59 | if (attribute) {
60 | const attributeMetadata = componentMeta.getAttrForKind(attribute);
61 | item.documentation = markupContent(
62 | docGen.generateAttrDescription(attribute.name, attributeMetadata),
63 | );
64 | } else {
65 | item.documentation = markupContent(
66 | docGen.generateCompNameDescription(componentName, componentMeta.getMetadata()),
67 | );
68 | }
69 | }
70 | return item;
71 | }
72 |
73 | type CompletionContext = {
74 | parseResult: ParseResult;
75 | getText: GetText;
76 | metaByComp: MetadataProvider;
77 | offsetToPos: (offset: number) => Position;
78 | };
79 |
80 | export function handleCompletion(
81 | { parseResult: { node }, getText, metaByComp, offsetToPos }: CompletionContext,
82 | position: number,
83 | ): XmluiCompletionItem[] | null {
84 | const findRes = findTokenAtPos(node, position);
85 | if (!findRes) {
86 | return null;
87 | }
88 | const { chainAtPos, chainBeforePos } = findRes;
89 |
90 | if (findRes.chainBeforePos === undefined) {
91 | return handleCompletionInsideToken(chainAtPos, position, metaByComp, getText);
92 | }
93 |
94 | const nodeBefore = chainBeforePos.at(-1);
95 | switch (nodeBefore.kind) {
96 | case SyntaxKind.OpenNodeStart:
97 | const defaultCompNames = allComponentNames(metaByComp);
98 | const closestElementNodeSuspect = chainBeforePos.at(-2);
99 |
100 | if (closestElementNodeSuspect && closestElementNodeSuspect.kind === SyntaxKind.ElementNode) {
101 | const matchingNode = getFirstNodeFromAncestorChain(
102 | chainBeforePos.slice(0, -3),
103 | SyntaxKind.ElementNode,
104 | );
105 | if (!matchingNode) return defaultCompNames;
106 |
107 | const compName = getNameFromElement(matchingNode, getText);
108 | if (!compName) return defaultCompNames;
109 |
110 | const compNameSuggestion = closingComponentCompletionItem(compName, offsetToPos, position);
111 | return [compNameSuggestion, ...defaultCompNames.map((c) => ({ ...c, sortText: "1" }))];
112 | }
113 | return defaultCompNames;
114 |
115 | case SyntaxKind.CloseNodeStart: {
116 | //TODO: this can be substituted for an function that finds the first ElementNode up the tree
117 | const closestElementNodeSuspect = chainBeforePos.at(-2);
118 | if (closestElementNodeSuspect && closestElementNodeSuspect.kind === SyntaxKind.ElementNode) {
119 | const compName = getNameFromElement(closestElementNodeSuspect, getText);
120 | if (!compName) return allComponentNames(metaByComp);
121 | return [componentCompletionItem(compName)];
122 | } else {
123 | return allComponentNames(metaByComp);
124 | }
125 | }
126 | case SyntaxKind.Identifier:
127 | const pathToElementNode = visitAncestorsInChain(
128 | chainBeforePos,
129 | (n) => n.kind === SyntaxKind.ElementNode,
130 | );
131 | const parentOfnodeBefore = chainBeforePos.at(-2);
132 | const completeCompName =
133 | parentOfnodeBefore?.kind === SyntaxKind.TagNameNode && position === nodeBefore.end;
134 | if (completeCompName) {
135 | if (pathToElementNode && insideClosingTag(pathToElementNode)) {
136 | const compName = getNameFromElement(pathToElementNode.at(-1), getText);
137 | if (!compName) return allComponentNames(metaByComp);
138 | return [componentCompletionItem(compName)];
139 | }
140 | return allComponentNames(metaByComp);
141 | }
142 | }
143 |
144 | const completeForProp = chainBeforePos.some(
145 | (n) =>
146 | n.kind === SyntaxKind.AttributeKeyNode ||
147 | n.kind === SyntaxKind.TagNameNode ||
148 | n.kind === SyntaxKind.AttributeNode,
149 | );
150 |
151 | if (completeForProp) {
152 | const tagNameNode = findTagNameNodeInStack(chainBeforePos);
153 | if (!tagNameNode) {
154 | return null;
155 | }
156 | const compName = compNameForTagNameNode(tagNameNode, getText);
157 | if (!compName) {
158 | return null;
159 | }
160 | return completionForNewAttr(compName, metaByComp);
161 | }
162 | return null;
163 | }
164 |
165 | function allComponentNames(md: MetadataProvider): XmluiCompletionItem[] {
166 | return md.componentNames().map(componentCompletionItem);
167 | }
168 |
169 | /**
170 | * Retrieves the name from an ElementNode
171 | * @param elementNode has to point to a ElementNode
172 | * @returns
173 | */
174 | function getNameFromElement(elementNode: Node, getText: GetText): string | null {
175 | const nameNode = elementNode.children!.find((c) => c.kind === SyntaxKind.TagNameNode);
176 | if (nameNode === undefined) {
177 | return null;
178 | }
179 | // --- Handle namespaces
180 | const colonIdx = nameNode.children!.findIndex((c) => c.kind === SyntaxKind.Colon);
181 | let nameSpace: string | undefined = undefined;
182 | let nameIdentSearchSpace = nameNode.children!;
183 | let name: string | undefined = undefined;
184 | if (colonIdx !== -1) {
185 | nameIdentSearchSpace = nameNode.children!.slice(colonIdx + 1);
186 | const nameSpaceIdx = nameNode.children!.findIndex((c) => c.kind === SyntaxKind.Identifier);
187 | if (nameSpaceIdx < colonIdx) {
188 | nameSpace = getText(nameNode.children![nameSpaceIdx]);
189 | }
190 | }
191 | const nameIdent = nameIdentSearchSpace.find((c) => c.kind === SyntaxKind.Identifier);
192 | if (nameIdent === undefined) {
193 | return null;
194 | }
195 | name = getText(nameIdent);
196 | const value = nameSpace !== undefined ? nameSpace + ":" + name : name;
197 | return value;
198 | }
199 |
200 | function handleCompletionInsideToken(
201 | chainAtPos: Node[],
202 | position: number,
203 | metaByComp: MetadataProvider,
204 | getText: (n: Node) => string,
205 | ): CompletionItem[] | null {
206 | const parent = chainAtPos.at(-2);
207 | if (!parent) {
208 | return null;
209 | }
210 | switch (parent.kind) {
211 | case SyntaxKind.TagNameNode: {
212 | const tagNameNodeParent = chainAtPos.at(-3);
213 | const tagNameNodeIdx = tagNameNodeParent.children!.findIndex((c) => c === parent);
214 | if (tagNameNodeIdx <= 0) {
215 | return null;
216 | }
217 | const previousNode = tagNameNodeParent.children![tagNameNodeIdx - 1];
218 | if (
219 | previousNode.kind === SyntaxKind.CloseNodeStart &&
220 | tagNameNodeParent.kind === SyntaxKind.ElementNode
221 | ) {
222 | const compName = getNameFromElement(tagNameNodeParent, getText);
223 | if (!compName) return allComponentNames(metaByComp);
224 | return [componentCompletionItem(compName)];
225 | }
226 | return allComponentNames(metaByComp);
227 | }
228 | case SyntaxKind.AttributeKeyNode: {
229 | const tagNameNode = findTagNameNodeInStack(chainAtPos);
230 | const compName = compNameForTagNameNode(tagNameNode, getText);
231 | return completionForNewAttr(compName, metaByComp);
232 | }
233 | }
234 | return null;
235 | }
236 |
237 | function completionForNewAttr(
238 | compName: string,
239 | metaByComp: MetadataProvider,
240 | ): CompletionItem[] | null {
241 | const metadata = metaByComp.getComponent(compName);
242 | if (!metadata) {
243 | return null;
244 | }
245 |
246 | const completionItemFromAttr = attributeCompletionItem.bind({}, compName);
247 | return metadata.getAllAttributes().map(completionItemFromAttr);
248 | }
249 |
250 | function attrKindToCompletionItemKind(attrKind: AttributeKind) {
251 | switch (attrKind) {
252 | case "api":
253 | return CompletionItemKind.Function;
254 | case "event":
255 | return CompletionItemKind.Event;
256 | case "layout":
257 | return CompletionItemKind.Unit;
258 | case "prop":
259 | case "implicit":
260 | return CompletionItemKind.Property;
261 | default:
262 | const _exhaustiveCheck: never = attrKind;
263 | return _exhaustiveCheck;
264 | }
265 | }
266 |
267 | function attributeCompletionItem(
268 | componentName: string,
269 | attribute: TaggedAttribute,
270 | ): XmluiCompletionItem {
271 | const label = attribute.kind === "event" ? addOnPrefix(attribute.name) : attribute.name;
272 | return {
273 | label,
274 | kind: attrKindToCompletionItemKind(attribute.kind),
275 | sortText: attributeDisplayOrder(attribute.kind) + label,
276 | data: {
277 | metadataAccessInfo: {
278 | componentName,
279 | attribute,
280 | },
281 | },
282 | };
283 | }
284 |
285 | function componentCompletionItem(
286 | componentName: string,
287 | sortingOrder?: number,
288 | ): XmluiCompletionItem {
289 | const sortText =
290 | sortingOrder !== undefined &&
291 | sortingOrder !== 0 &&
292 | sortingOrder !== null &&
293 | !isNaN(sortingOrder)
294 | ? sortingOrder.toString()
295 | : "";
296 | return {
297 | label: componentName,
298 | kind: CompletionItemKind.Constructor,
299 | sortText,
300 | data: {
301 | metadataAccessInfo: {
302 | componentName,
303 | },
304 | },
305 | };
306 | }
307 |
308 | function closingComponentCompletionItem(
309 | componentName: string,
310 | offsetToPos: (offset: number) => Position,
311 | cursorOffset: number,
312 | ): XmluiCompletionItem {
313 | const cursorPos = offsetToPos(cursorOffset);
314 | const updatedText = /* TODO globals.clientSupports.insertReplaceEdit ? Handle this case : */ {
315 | newText: `/${componentName}>`,
316 | range: { start: cursorPos, end: cursorPos },
317 | };
318 | return {
319 | label: `/${componentName}`,
320 | kind: CompletionItemKind.Constructor,
321 | sortText: "0",
322 | textEdit: updatedText,
323 | data: {
324 | metadataAccessInfo: {
325 | componentName,
326 | },
327 | },
328 | };
329 | }
330 |
331 | function markupContent(content: string): MarkupContent {
332 | return {
333 | kind: MarkupKind.Markdown,
334 | value: content,
335 | };
336 | }
337 | function attributeDisplayOrder(kind: AttributeKind): string {
338 | switch (kind) {
339 | case "api":
340 | case "prop":
341 | case "implicit":
342 | case "event":
343 | return "0";
344 |
345 | case "layout":
346 | return "1";
347 | default: {
348 | const _exhaustiveCheck: never = kind;
349 | return _exhaustiveCheck;
350 | }
351 | }
352 | }
353 |
```
--------------------------------------------------------------------------------
/xmlui/dev-docs/next/generating-component-reference.md:
--------------------------------------------------------------------------------
```markdown
1 | # Generate Component Reference Documentation
2 |
3 | This document describes how to generate and maintain component reference documentation for XMLUI components, including automated documentation extraction from source code and manual documentation creation.
4 |
5 | ## Overview
6 |
7 | XMLUI component reference documentation is generated from multiple sources:
8 | - **Component Metadata** - The XMLUI framework has a particular metadata structure that describes an XMLUI component including its properties, events, exposed methods, and other component traits. Each component must define its metadata.
9 | - **Component Documentation Files** - Markdown files with directive-based content injection. These files can declare additional metadata content in markdown format, such as code samples, tables, additional explanations, etc. While the component metadata is available within the framework and its tools (and also for external tools), the content in component documentation files is just for generating the reference documentation of components.
10 |
11 | ## Prerequisites
12 |
13 | All documentation generation commands should be run from the `xmlui` subfolder:
14 |
15 | ```bash
16 | cd xmlui
17 | npm run generate-all-docs
18 | ```
19 |
20 | ## Documentation Generation Workflow
21 |
22 | 1. **Build component metadata** (`npm run build:xmlui-metadata`) - Export component metadata to a JSON file using Vite's metadata build mode
23 | 2. **Build extension metadata** (`npm run build:ext-meta`) - Process extension package metadata
24 | 3. **Generate component docs** (`npm run generate-docs`) - Merge component metadata with component documentation files using the DocsGenerator
25 | 4. **Generate summary files** (`npm run generate-docs-summaries`) - Create overview documents and metadata files
26 | 5. **Output to docs folder** - Place generated documents into `docs/content/components/`
27 |
28 | > **Note**
29 | > The `npm run generate-all-docs` command runs the complete pipeline in the correct order: metadata extraction, extension processing, documentation generation, and summary creation.
30 |
31 | ### Available Scripts
32 |
33 | - `npm run build:xmlui-metadata` - Extract component metadata only
34 | - `npm run generate-docs` - Generate docs from existing metadata
35 | - `npm run generate-docs-summaries` - Generate overview and meta files
36 | - `npm run generate-all-docs` - Complete documentation generation pipeline (recommended)
37 |
38 | ## Sample Component Folder Structure
39 |
40 | ```bash
41 | xmlui/src/components/
42 | ├── ...
43 | ├── Button/
44 | │ ├── Button.tsx # Component implementation (includes metadata) 📖 used for docs
45 | │ ├── ButtonNative.tsx # Underlying React component
46 | │ ├── Button.md # Component documentation file 📖 used for docs
47 | │ ├── Button.module.scss # Component styles
48 | │ ├── Button.spec.ts # Component tests
49 | │ └── Button-style.spec.ts # Style-specific tests
50 | ├── ...
51 | ├── Text/
52 | │ ├── Text.tsx # Component implementation (includes metadata) 📖 used for docs
53 | │ ├── TextNative.tsx # Underlying React component
54 | │ ├── Text.md # Component documentation file 📖 used for docs
55 | │ ├── Text.module.scss # Component styles
56 | │ └── Text.spec.ts # Component tests
57 | └── ...
58 | ```
59 |
60 | The system uses a sophisticated multi-stage process involving npm scripts and Node.js modules to automatically generate comprehensive component documentation.
61 |
62 | ## Component Documentation Patterns: Metadata-Driven vs. Markdown-Driven
63 |
64 | XMLUI supports two main patterns for generating component reference documentation, depending on where the component is located in the codebase:
65 |
66 | ### 1. Metadata-Driven Documentation (`components-core`)
67 |
68 | - Components in `xmlui/src/components-core/` (such as `Fragment`) define their documentation primarily through metadata in their TypeScript source files.
69 | - The metadata is created using the `createMetadata()` function, which includes descriptions, property definitions, events, and more.
70 | - Example (in `Fragment.tsx`):
71 | ```ts
72 | export const FragmentMd = createMetadata({
73 | description: "...",
74 | props: {
75 | when: {
76 | description: "...",
77 | valueType: "boolean | expression"
78 | }
79 | }
80 | });
81 | ```
82 | - The documentation generator uses this metadata to produce the reference docs. If a Markdown file exists for the component, it will merge in any matching %-PROP-START/END blocks, but only for properties present in the metadata.
83 |
84 | ### 2. Markdown-Driven Documentation (`components`)
85 |
86 | - Components in `xmlui/src/components/` (such as `Image`) have a dedicated Markdown file (e.g., `Image.md`) located alongside the component source.
87 | - This Markdown file contains detailed documentation, examples, and property/event explanations using directive blocks:
88 | ```
89 | %-PROP-START propertyName
90 | ...documentation and examples...
91 | %-PROP-END
92 | ```
93 | - The documentation generator merges the Markdown content with the component’s metadata, allowing for rich, user-friendly docs.
94 |
95 | ### Key Differences
96 |
97 | - **Location**:
98 | - `components-core`: Metadata-driven, TypeScript source is primary.
99 | - `components`: Markdown-driven, `.md` file is primary.
100 | - **Customization**:
101 | - `components-core`: Customization is limited to what’s in the metadata unless a Markdown file is added.
102 | - `components`: Full Markdown flexibility for examples, tables, and extended explanations.
103 | - **Doc Generation**:
104 | - In both cases, the doc generator merges metadata and Markdown, but only properties/events present in the metadata will be documented.
105 |
106 | ### Best Practices
107 |
108 | - For new user-facing components, prefer the Markdown-driven approach for richer documentation.
109 | - For foundational or internal components, metadata-driven docs may be sufficient, but you can always add a Markdown file for more detail.
110 | - Always ensure that any property or event you want documented in Markdown is also present in the component’s metadata.
111 |
112 |
113 | ## Sample Component Metadata
114 |
115 | ```typescript
116 | export const AvatarMd = createMetadata({
117 | description:
118 | "`Avatar` displays a user or entity's profile picture as a circular image, " +
119 | "with automatic fallback to initials when no image is provided. It's commonly " +
120 | "used in headers, user lists, comments, and anywhere you need to represent a " +
121 | "person or organization.",
122 | props: {
123 | size: {
124 | description: `This property defines the display size of the Avatar.`,
125 | availableValues: sizeMd,
126 | valueType: "string",
127 | defaultValue: defaultProps.size,
128 | },
129 | name: {
130 | description:
131 | `This property sets the name value the Avatar uses to display initials. If neither ` +
132 | "this property nor \`url\` is defined, an empty avatar is displayed.",
133 | valueType: "string",
134 | },
135 | url: {
136 | description:
137 | `This property specifies the URL of the image to display in the Avatar. ` +
138 | "If neither this property nor \`name\` is defined, an empty avatar is displayed.",
139 | valueType: "string",
140 | },
141 | },
142 | events: {
143 | click: d("This event is triggered when the avatar is clicked."),
144 | },
145 | themeVars: parseScssVar(styles.themeVars),
146 | defaultThemeVars: {
147 | [`borderRadius-Avatar`]: "4px",
148 | [`boxShadow-Avatar`]: "inset 0 0 0 1px rgba(4,32,69,0.1)",
149 | [`textColor-Avatar`]: "$textColor-secondary",
150 | [`fontWeight-Avatar`]: "$fontWeight-bold",
151 | [`border-Avatar`]: "0px solid $color-surface-400A80",
152 | [`backgroundColor-Avatar`]: "$color-surface-100",
153 | },
154 | });
155 | ```
156 |
157 | This metadata structure includes:
158 |
159 | - **`description`** - A comprehensive description of the component's purpose and usage
160 | - **`props`** - Component properties with descriptions, value types, available values, and default values
161 | - **`events`** - Event handlers the component supports (e.g., click events)
162 | - **`themeVars`** - Theme variables extracted from the component's SCSS module
163 | - **`defaultThemeVars`** - Default values for theme variables used for styling customization
164 |
165 | The metadata is created using the `createMetadata()` function and exported so it can be collected and processed during documentation generation.
166 |
167 | ## Technical Implementation
168 |
169 | The documentation generation system consists of several key components:
170 |
171 | - **Metadata Extraction**: Uses Vite in metadata mode to extract component metadata from TypeScript files
172 | - **DocsGenerator Class**: Processes component metadata and documentation files to create final docs
173 | - **Directive Processing**: Merges manual content with auto-generated metadata using special markers
174 | - **Output Management**: Handles file cleanup, organization, and metadata file generation
175 |
176 | The main entry point is `scripts/generate-docs/get-docs.mjs`, which orchestrates the entire process.
177 |
178 | ## Component Documentation File Directives
179 |
180 | Component documentation files (e.g., `Button.md`) use special directive markers to inject auto-generated content from the component metadata. These directives allow manual documentation to be seamlessly merged with extracted metadata.
181 |
182 | **Directive Format:**
183 | ```markdown
184 | %-SECTION_NAME-START [optional_parameter]
185 | Content goes here
186 | %-SECTION_NAME-END
187 | ```
188 |
189 | **Available Directives:**
190 |
191 | - **`%-DESC-START` / `%-DESC-END`** - Component description from metadata
192 | - **`%-PROP-START propName` / `%-PROP-END`** - Specific property documentation with details from metadata
193 | - **`%-EVENT-START eventName` / `%-EVENT-END`** - Event documentation from metadata
194 | - **`%-API-START apiName` / `%-API-END`** - API method documentation from metadata
195 | - **`%-STYLE-START` / `%-STYLE-END`** - Styling information and theme variables
196 |
197 | **Example Usage:**
198 |
199 | Here's the actual content from `Avatar.md` showing how directives are used:
200 |
201 | ````text
202 | %-DESC-START
203 |
204 | **Key features:**
205 | - **Automatic fallback**: Shows initials when no image URL is provided or image fails to load
206 | - **Multiple sizes**: From `xs` (extra small) to `lg` (large) to fit different contexts
207 | - **Clickable**: Supports click events for profile actions, modals, or navigation
208 | - **Accessible**: Automatically generates appropriate alt text from the name
209 |
210 | %-DESC-END
211 |
212 | %-PROP-START name
213 |
214 | ```xmlui-pg copy display name="Example: name"
215 | <App>
216 | <Avatar name="John, Doe" />
217 | </App>
218 | ```
219 | %-PROP-END
220 |
221 | %-PROP-START size
222 |
223 | ```xmlui-pg copy display name="Example: size"
224 | <App>
225 | <HStack>
226 | <Avatar name="Dorothy Ellen Fuller" />
227 | <Avatar name="Xavier Schiller" size="xs" />
228 | <Avatar name="Sebastien Moore" size="sm" />
229 | <Avatar name="Molly Dough" size="md" />
230 | <Avatar name="Lynn Gilbert" size="lg" />
231 | </HStack>
232 | </App>
233 | ```
234 |
235 | %-PROP-END
236 |
237 | %-PROP-START url
238 |
239 | ```xmlui-pg copy display name="Example: url"
240 | <App>
241 | <Avatar url="https://i.pravatar.cc/100" size="md" />
242 | </App>
243 | ```
244 |
245 | %-PROP-END
246 |
247 | %-EVENT-START click
248 |
249 | ```xmlui-pg copy display name="Example: click"
250 | <App>
251 | <HStack verticalAlignment="center">
252 | <Avatar name="Molly Dough" size="md" onClick="toast('Avatar clicked')" />
253 | Click the avatar!
254 | </HStack>
255 | </App>
256 | ```
257 |
258 | %-EVENT-END
259 | ````
260 |
261 |
```
--------------------------------------------------------------------------------
/xmlui/src/abstractions/AppContextDefs.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { To, NavigateOptions } from "react-router-dom";
2 | import type { QueryClient } from "@tanstack/react-query";
3 |
4 | import type {
5 | ToastOptions,
6 | Renderable,
7 | ValueOrFunction,
8 | DefaultToastOptions,
9 | Toast,
10 | } from "react-hot-toast";
11 | import type { ActionFunction } from "./ActionDefs";
12 | import type { SetupWorker } from "msw/browser";
13 | import type { ApiInterceptor } from "../components-core/interception/ApiInterceptor";
14 |
15 | // This interface defines the properties and services of an app context that the
16 | // application components can use when implementing their behavior.
17 | export type AppContextObject = {
18 | // Accept other methods
19 | [x: string]: unknown;
20 |
21 | // ==============================================================================================
22 | // Engine-realated
23 |
24 | version: string;
25 |
26 | // ==============================================================================================
27 | // Actions namespace
28 |
29 | Actions: Record<string, ActionFunction>;
30 |
31 | // ==============================================================================================
32 | // App-Specific
33 |
34 | // This property returns the context object of the API interceptor.
35 | apiInterceptorContext: IApiInterceptorContext;
36 |
37 | // This property returns a hash object containing all application-global settings
38 | // defined in the app's configuration file.
39 | appGlobals?: Record<string, any>;
40 |
41 | // Indicates that the application is running in debug-enabled mode.
42 | debugEnabled?: boolean;
43 |
44 | // Indicates that components are decorated with test IDs used for e2e tests.
45 | decorateComponentsWithTestId?: boolean;
46 |
47 | // This property returns an object with some properties of the current environment.
48 | environment: { isWindowFocused: boolean };
49 |
50 | // This property returns an object with information about the current media size.
51 | mediaSize: MediaSize;
52 |
53 | // The `QueryClient` object of the react-query library XMLUI uses for data
54 | // fetching purposes
55 | queryClient: QueryClient | null;
56 |
57 | // This property returns `true` if the app is a standalone XMLUI app; otherwise
58 | // (for example, as part of a website), it returns `false`.
59 | standalone?: boolean;
60 |
61 | // Indicates that the app is running in a shadow DOM.
62 | appIsInShadowDom?: boolean;
63 |
64 | // ==============================================================================================
65 | // Date Utilities
66 |
67 | // This function formats the specified value's date part into a local date string
68 | // (according to the machine's local settings).
69 | formatDate: (date: string | Date) => string | undefined;
70 |
71 | // This function formats the specified value into a local date and time string
72 | // (according to the machine's local settings).
73 | formatDateTime: (date: any) => string | undefined;
74 |
75 | // This function formats the specified value's date part (without year)
76 | // into a local date string (according to the machine's local settings).
77 | formatDateWithoutYear: (date: string | Date) => string | undefined;
78 |
79 | // This function formats the specified value's time part into a local date
80 | // string (according to the machine's local settings).
81 | formatTime: (date: any) => string | undefined;
82 |
83 | // This function formats the specified value's time part (without seconds)
84 | // into a local date string (according to the machine's local settings).
85 | formatTimeWithoutSeconds: (date: string | Date) => string | undefined;
86 |
87 | // This function creates a date from the specified input value. If no input
88 | // is provided, it returns the current date and time.
89 | getDate: (date?: string | number | Date) => Date;
90 |
91 | // This function calculates the difference between the current date and the
92 | // provided one and returns it in a human-readable form, such as "1 month",
93 | // "2 weeks", etc.
94 |
95 | getDateUntilNow: (date?: string | number | Date, nowLabel?: string, time?: string) => string;
96 |
97 | // This function converts the input string into a date value and returns
98 | // the ISO 8601 string representation of the date. It can pass dates between
99 | // the UI and backend APIs in a standard format.
100 | isoDateString: (date?: string) => string;
101 |
102 | // This function checks if the specified date is today.
103 | isToday: (date: string | Date) => boolean;
104 |
105 | // This function checks if the specified date is tomorrow.
106 | isTomorrow: (date: string | Date) => boolean;
107 |
108 | // This function checks if the specified date is yesterday.
109 | isYesterday: (date: string | Date) => boolean;
110 |
111 | // This function checks the date value provided for some particular
112 | // values and returns accordingly. Otherwise, returns it as `formatDate` would.
113 | smartFormatDate: (date?: string | number | Date) => string;
114 |
115 | // This function checks the date value provided for some particular values and
116 | // returns accordingly. Otherwise, returns it as `formatDateTime` would.
117 | smartFormatDateTime: (date: string | Date) => string | undefined;
118 |
119 | // This functions creates the difference between two dates in minutes.
120 | differenceInMinutes: (date1: number | Date, date2: number | Date) => number;
121 |
122 | // This function checks if the specified dates are on the same day.
123 | isSameDay: (dateLeft: number | Date, dateRight: number | Date) => boolean;
124 |
125 | // This function checks if the specified date is in the current calendar year.
126 | // True, if the date is in the current year; otherwise, false.
127 | isThisYear: (date: Date | number) => boolean;
128 |
129 | // Formats a date into a human-readable elapsed time string.
130 | // Returns strings like "now", "12 seconds ago", "3 hours ago",
131 | // "today", "yesterday", "3 weeks ago", etc.
132 | formatHumanElapsedTime: (date: string | Date) => string;
133 |
134 | // ==============================================================================================
135 | // Math Utilities
136 |
137 | // This function calculates the average of the specified values and returns it.
138 | avg: (values: number[], decimals?: number) => number;
139 |
140 | // This function calculates the sum of the specified values and returns it.
141 | sum: (values: number[]) => number;
142 |
143 | // ==============================================================================================
144 | // File Utilities
145 |
146 | // This function returns the specified file size in a compact form, such as
147 | // "112 B", "2.0 KiB", "23.4 KiB", "2.3 MiB", etc.
148 | formatFileSizeInBytes: (bytes: number) => string | undefined;
149 |
150 | // This function returns the type of the specified file.
151 | getFileExtension: (fileName: string) => string | undefined;
152 |
153 | // ==============================================================================================
154 | // Navigation Utilities
155 |
156 | // This function navigates to the specified `url`.
157 | navigate: (url: To, options?: NavigateOptions) => void;
158 |
159 | // This property determines the base name used for the router.
160 | routerBaseName: string;
161 |
162 | // ==============================================================================================
163 | // Notifications and Dialogs
164 |
165 | // Instructs the browser to display a dialog with an optional message, and to
166 | // wait until the user either confirms or cancels the dialog. It returns a
167 | // boolean indicating whether OK (`true`) or Cancel (`false`) was selected.
168 | confirm: (title: string, message?: string, actionLabel?: string) => Promise<boolean>;
169 |
170 | // This method displays the specified `error` (error message) on the UI.
171 | signError(error: Error | string): void;
172 |
173 | // The toast service that displays messages in the UI.
174 | toast: {
175 | (message: Message, opts?: ToastOptions): string;
176 | error: ToastHandler;
177 | success: ToastHandler;
178 | loading: ToastHandler;
179 | custom: ToastHandler;
180 | dismiss(toastId?: string): void;
181 | remove(toastId?: string): void;
182 | promise<T>(
183 | promise: Promise<T>,
184 | msgs: {
185 | loading: Renderable;
186 | success: ValueOrFunction<Renderable, T>;
187 | error: ValueOrFunction<Renderable, any>;
188 | },
189 | opts?: DefaultToastOptions,
190 | ): Promise<T>;
191 | };
192 |
193 | // ==============================================================================================
194 | // Theme-related
195 |
196 | // This property returns the ID of the currently active theme.
197 | activeThemeId: string;
198 |
199 | // This property returns the tone of the currently active theme ("light" or "dark").
200 | activeThemeTone: "light" | "dark";
201 |
202 | // This property returns an array of all available theme IDs.
203 | availableThemeIds: string[];
204 |
205 | // This function sets the current theme to the one with the specified `themeId`.
206 | setTheme: (themId: string) => void;
207 |
208 | // This function sets the current theme tone to the specified `tone` value
209 | // ("light" or "dark").
210 | setThemeTone: (newTone: "light" | "dark") => void;
211 |
212 | // This function toggles the current theme tone from "light" to "dark" or vice versa.
213 | toggleThemeTone: () => void;
214 |
215 | // ==============================================================================================
216 | // Users
217 |
218 | // This property gets the information about the logged-in user. If `null`, no user is
219 | // logged in. The user information may have any value; the app must be able to
220 | // leverage this information.
221 | loggedInUser: LoggedInUserDto | null;
222 |
223 | // This function sets the information about the logged-in user. The user information
224 | // may have any value; the app must be able to leverage this information.
225 | setLoggedInUser: (loggedInUser: any) => void;
226 |
227 | readonly resources?: Record<string, string>;
228 |
229 | // ==============================================================================================
230 | // Various Utilities
231 |
232 | capitalize: (s?: string) => string;
233 | pluralize: (number: number, singular: string, plural: string) => string;
234 | delay: (timeInMs: number, callback?: any) => Promise<void>;
235 | debounce: <F extends (...args: any[]) => any>(
236 | delayMs: number,
237 | func: F,
238 | ...args: any[]
239 | ) => void;
240 | toHashObject: (arr: any[], keyProp: string, valueProp: string) => any;
241 | findByField: (arr: any[], field: string, value: any) => any;
242 | readonly embed: { isInIFrame: boolean };
243 | distinct: (arr: any[]) => any[];
244 | forceRefreshAnchorScroll: () => void;
245 | };
246 |
247 | export const MediaBreakpointKeys = ["xs", "sm", "md", "lg", "xl", "xxl"] as const;
248 | export type MediaBreakpointType = (typeof MediaBreakpointKeys)[number];
249 |
250 | export type MediaSize = {
251 | phone: boolean;
252 | landscapePhone: boolean;
253 | tablet: boolean;
254 | desktop: boolean;
255 | largeDesktop: boolean;
256 | xlDesktop: boolean;
257 | smallScreen: boolean;
258 | largeScreen: boolean;
259 | size: MediaBreakpointType;
260 | sizeIndex: number;
261 | };
262 |
263 | export type LoggedInUserDto = {
264 | id: number;
265 | email: string;
266 | name: string;
267 | imageRelativeUrl: string;
268 | permissions: Record<string, string>;
269 | };
270 |
271 | export interface IApiInterceptorContext {
272 | isMocked: (url: string) => boolean;
273 | initialized: boolean;
274 | forceInitialize: ()=>void;
275 | interceptorWorker: SetupWorker | null;
276 | apiInstance: ApiInterceptor | null;
277 | }
278 |
279 | type Message = ValueOrFunction<Renderable, Toast>;
280 | type ToastHandler = (message: Message, options?: ToastOptions) => string;
281 |
```
--------------------------------------------------------------------------------
/docs/content/components/ModalDialog.md:
--------------------------------------------------------------------------------
```markdown
1 | # ModalDialog [#modaldialog]
2 |
3 | `ModalDialog` creates overlay dialogs that appear on top of the main interface, ideal for forms, confirmations, detailed views, or any content that requires focused user attention. Dialogs are programmatically opened using the `open()` method and can receive parameters for dynamic content.
4 |
5 | **Key features:**
6 | - **Overlay presentation**: Appears above existing content with backdrop dimming
7 | - **Programmatic control**: Open and close via exposed methods like `open()` and `close()`
8 | - **Parameter passing**: Accept data when opened for dynamic dialog content
9 | - **Focus management**: Automatically handles focus trapping and accessibility
10 | - **Form integration**: When containing Form components, automatically closes on form submission or cancellation (unless overridden)
11 |
12 | ## Using the Component [#using-the-component]
13 |
14 | >[!INFO]
15 | > When using the examples in this article, pop them out to the full screen to check how they work.
16 |
17 | Opening and closing the modal dialog can be done in two ways depending on circumstances.
18 |
19 | ### With Imperative API [#with-imperative-api]
20 |
21 | Event-driven display of the `ModalDialog` dialog is also possible using imperative API.
22 |
23 | This method is a good way to toggle the display of the `ModalDialog` if no deep linking is necessary.
24 | It also lends to itself that these events can be triggered programmatically from codebehind.
25 |
26 | Note the `id` property of the `ModalDialog` in the example below and how it is used to call the [`open`](#open-api) and [`close`](#close-api)
27 | operations of the component in the `onClick` event handlers.
28 |
29 | ```xmlui-pg copy display name="Example: imperative API" height="220px"
30 | <App>
31 | <ModalDialog id="dialog" title="Example Dialog">
32 | <Button label="Close Dialog" onClick="dialog.close()" />
33 | </ModalDialog>
34 | <Button label="Open Dialog" onClick="dialog.open()" />
35 | </App>
36 | ```
37 |
38 | >[!INFO]
39 | > The imperative approach is perhaps the most intuitive way to display and hide modal dialogs.
40 |
41 | ### With `when` [#with-when]
42 |
43 | The `when` property accepts a primitive boolean or a binding expression resolving to a boolean value to toggle the display of a component.
44 |
45 | Using the `when` property in a `ModalDialog` dialog component is commonly used with deep linking:
46 | showing the modal in conjunction with an updated URL so that the opened state of the modal dialog is referable.
47 |
48 | ```xmlui-pg height="220px"
49 | ---app copy display name="Example: when"
50 | <App>
51 | <variable name="isDialogShown" value="{false}"/>
52 | <Button label="Open Dialog" onClick="isDialogShown = true" />
53 | <ModalDialog
54 | when="{isDialogShown}"
55 | title="Example Dialog"
56 | onClose="isDialogShown = false" />
57 | </App>
58 | ---desc
59 | Click on the button in the demo below to open the modal dialog. Click anywhere outside the opened dialog or the close button to close it.
60 | ```
61 |
62 | Setting the `when` property is the most straightforward way for deep-linked modals. If you use deep links with query parameters to show a particular dialog, you can set the `when` property to show or hide the dialog according to parameter values.
63 |
64 | ### The `ModalDialog` as a Container [#the-modaldialog-as-a-container]
65 |
66 | The `ModalDialog` component is also a container such as the [`Card`](/components/Card), that it also accepts child components.
67 |
68 | ```xmlui-pg copy {3-8} display name="Example: children" height="340px"
69 | <App>
70 | <Button label="Open Dialog" onClick="dialog.open()" />
71 | <ModalDialog id="dialog" title="Example Dialog">
72 | <Form data="{{ firstName: 'Billy', lastName: 'Bob' }}">
73 | <FormItem bindTo="firstName" required="true" />
74 | <FormItem bindTo="lastName" required="true" />
75 | </Form>
76 | </ModalDialog>
77 | </App>
78 | ```
79 |
80 | >[!INFO]
81 | > When a form is nested into a modal dialog, closing the form (canceling it or completing its submit action) automatically closes the dialog.
82 |
83 | **Context variables available during execution:**
84 |
85 | - `$param`: First parameter passed to the `open()` method
86 | - `$params`: Array of all parameters passed to `open()` method (access with `$params[0]`, `$params[1]`, etc.)
87 |
88 | ## Properties [#properties]
89 |
90 | ### `closeButtonVisible` (default: true) [#closebuttonvisible-default-true]
91 |
92 | Shows (`true`) or hides (`false`) the visibility of the close button on the dialog.
93 |
94 | ```xmlui-pg height="220px"
95 | ---app copy display name="Example: closeButtonVisible"
96 | <App>
97 | <Button label="Open Dialog" onClick="dialog.open()" />
98 | <ModalDialog id="dialog" closeButtonVisible="false" title="Example Dialog" />
99 | </App>
100 | ---desc
101 | Click outside the dialog to close it.
102 | ```
103 |
104 | ### `fullScreen` (default: false) [#fullscreen-default-false]
105 |
106 | Toggles whether the dialog encompasses the whole UI (`true`) or not and has a minimum width and height (`false`).
107 |
108 | ```xmlui-pg height="220px"
109 | ---app copy display name="Example: fullScreen"
110 | <App>
111 | <Button label="Open Dialog" onClick="dialog.open()" />
112 | <ModalDialog id="dialog" fullScreen="true" title="Example Dialog" />
113 | </App>
114 | ---desc
115 | Click the button to display a full-screen dialog. The icon at the top-right corner of the dialog allows you to close it.
116 | ```
117 |
118 | ### `title` [#title]
119 |
120 | Provides a prestyled heading to display the intent of the dialog.
121 |
122 | ```xmlui-pg copy {3} display name="Example: title" height="220px"
123 | <App>
124 | <Button label="Open Dialog" onClick="dialog.open()" />
125 | <ModalDialog id="dialog" title="Example Title" />
126 | </App>
127 | ```
128 |
129 | ## Events [#events]
130 |
131 | ### `close` [#close]
132 |
133 | This event is fired when the close button is pressed or the user clicks outside the `ModalDialog`.
134 |
135 | In this example, the `close` event counts how many times you closed the dialog:
136 |
137 | ```xmlui-pg height="220px"
138 | ---app copy {6-8} display name="Example: open/close events"
139 | <App>
140 | <Button label="Open Dialog" onClick="myDialog.open()" />
141 | <ModalDialog
142 | id="myDialog"
143 | title="Example Dialog"
144 | var.counter="{0}"
145 | onClose="counter++">
146 | <Text value="Dialog closed {counter} number of times." />
147 | </ModalDialog>
148 | </App>
149 | ---desc
150 | Open and close the dialog several times to test that it changes the counter.
151 | ```
152 |
153 | ### `open` [#open]
154 |
155 | This event is fired when the `ModalDialog` is opened either via a `when` or an imperative API call (`open()`).
156 |
157 | In this example, the `open` event counts how many times you opened the dialog:
158 |
159 | ```xmlui-pg height="220px"
160 | ---app copy {6-8} display name="Example: open/close events"
161 | <App>
162 | <Button label="Open Dialog" onClick="myDialog.open()" />
163 | <ModalDialog
164 | id="myDialog"
165 | title="Example Dialog"
166 | var.counter="{0}"
167 | onOpen="counter++">
168 | <Text value="Dialog opened {counter} number of times." />
169 | </ModalDialog>
170 | </App>
171 | ---desc
172 | Open and close the dialog several times to test that it changes the counter.
173 | ```
174 |
175 | ## Exposed Methods [#exposed-methods]
176 |
177 | ### `close` [#close]
178 |
179 | This method is used to close the `ModalDialog`. Invoke it using `modalId.close()` where `modalId` refers to a `ModalDialog` component.
180 |
181 | **Signature**: `close(): void`
182 |
183 | See the [\`With Imperative API\`](#with-imperative-api) subsection for an example.
184 |
185 | ### `open` [#open]
186 |
187 | This method imperatively opens the modal dialog. You can pass an arbitrary number of parameters to the method. In the `ModalDialog` instance, you can access those with the `$param` and `$params` context values.
188 |
189 | **Signature**: `open(...params: any[]): void`
190 |
191 | - `params`: An arbitrary number of parameters that can be used to pass data to the dialog.
192 |
193 | See the [\`With Imperative API\`](#with-imperative-api) subsection for an example.
194 |
195 | ## Parts [#parts]
196 |
197 | The component has some parts that can be styled through layout properties and theme variables separately:
198 |
199 | - **`content`**: The main content area of the modal dialog.
200 | - **`title`**: The title area of the modal dialog.
201 |
202 | ## Styling [#styling]
203 |
204 | ### Theme Variables [#theme-variables]
205 |
206 | | Variable | Default Value (Light) | Default Value (Dark) |
207 | | --- | --- | --- |
208 | | [backgroundColor](../styles-and-themes/common-units/#color)-ModalDialog | $backgroundColor-primary | $backgroundColor-primary |
209 | | [backgroundColor](../styles-and-themes/common-units/#color)-ModalDialog | $backgroundColor-primary | $backgroundColor-primary |
210 | | [backgroundColor](../styles-and-themes/common-units/#color)-overlay-ModalDialog | $backgroundColor-overlay | $backgroundColor-overlay |
211 | | [backgroundColor](../styles-and-themes/common-units/#color)-overlay-ModalDialog | $backgroundColor-overlay | $backgroundColor-overlay |
212 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-ModalDialog | $borderRadius | $borderRadius |
213 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-ModalDialog | $borderRadius | $borderRadius |
214 | | [fontFamily](../styles-and-themes/common-units/#fontFamily)-ModalDialog | $fontFamily | $fontFamily |
215 | | [fontFamily](../styles-and-themes/common-units/#fontFamily)-ModalDialog | $fontFamily | $fontFamily |
216 | | [marginBottom](../styles-and-themes/common-units/#size)-title-ModalDialog | 0 | 0 |
217 | | [marginBottom](../styles-and-themes/common-units/#size)-title-ModalDialog | 0 | 0 |
218 | | [maxWidth](../styles-and-themes/common-units/#size)-ModalDialog | 450px | 450px |
219 | | [maxWidth](../styles-and-themes/common-units/#size)-ModalDialog | 450px | 450px |
220 | | [minWidth](../styles-and-themes/common-units/#size)-ModalDialog | *none* | *none* |
221 | | [padding](../styles-and-themes/common-units/#size)-ModalDialog | $space-7 | $space-7 |
222 | | [padding](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
223 | | [paddingBottom](../styles-and-themes/common-units/#size)-ModalDialog | $paddingVertical-ModalDialog | $paddingVertical-ModalDialog |
224 | | [paddingBottom](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
225 | | [paddingHorizontal](../styles-and-themes/common-units/#size)-ModalDialog | *none* | *none* |
226 | | [paddingHorizontal](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
227 | | [paddingLeft](../styles-and-themes/common-units/#size)-ModalDialog | $paddingHorizontal-ModalDialog | $paddingHorizontal-ModalDialog |
228 | | [paddingLeft](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
229 | | [paddingRight](../styles-and-themes/common-units/#size)-ModalDialog | $paddingHorizontal-ModalDialog | $paddingHorizontal-ModalDialog |
230 | | [paddingRight](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
231 | | [paddingTop](../styles-and-themes/common-units/#size)-ModalDialog | $paddingVertical-ModalDialog | $paddingVertical-ModalDialog |
232 | | [paddingTop](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
233 | | [paddingVertical](../styles-and-themes/common-units/#size)-ModalDialog | *none* | *none* |
234 | | [paddingVertical](../styles-and-themes/common-units/#size)-overlay-ModalDialog | *none* | *none* |
235 | | [textColor](../styles-and-themes/common-units/#color)-ModalDialog | $textColor-primary | $textColor-primary |
236 | | [textColor](../styles-and-themes/common-units/#color)-ModalDialog | $textColor-primary | $textColor-primary |
237 |
```
--------------------------------------------------------------------------------
/xmlui/src/components/App/App.md:
--------------------------------------------------------------------------------
```markdown
1 | %-DESC-START
2 |
3 | **Essential features:**
4 |
5 | - **Layout templates**: Choose from 7 predefined layouts (horizontal, vertical, condensed, etc.) with sticky navigation options
6 | - **Routing**: Built-in page routing via the [Pages](/components/Pages) component
7 |
8 | %-DESC-END
9 |
10 | %-PROP-START layout
11 |
12 | Here are a few samples demonstrating the usage of the `layout` property. All samples use this markup, except the value of `App`'s layout and a few marked code snippets:
13 |
14 | ```xmlui
15 | <App layout="(specific layout value)">
16 | <!-- AppHeader omitted for "vertical" and "vertical-sticky" -->
17 | <AppHeader>
18 | <property name="logoTemplate">
19 | <Heading level="h3" value="Example App"/>
20 | </property>
21 | </AppHeader>
22 | <NavPanel>
23 | <NavLink label="Home" to="/" icon="home"/>
24 | <NavLink label="Page 1" to="/page1"/>
25 | <NavLink label="Page 2" to="/page2"/>
26 | </NavPanel>
27 | <Pages fallbackPath="/">
28 | <Page url="/">
29 | <List data="https://api.spacexdata.com/v3/history">
30 | <property name="itemTemplate">
31 | <Card title="{$item.title}" subtitle="{$item.details}"/>
32 | </property>
33 | </List>
34 | </Page>
35 | <Page url="/page1">
36 | <Text value="Page 1" />
37 | </Page>
38 | <Page url="/page2">
39 | <Text value="Page 2" />
40 | </Page>
41 | </Pages>
42 | <Footer>Powered by XMLUI</Footer>
43 | </App>
44 | ```
45 |
46 | #### `horizontal`
47 |
48 | ```xmlui-pg copy name="Example: 'horizontal' layout" height="350px"
49 | <App layout="horizontal">
50 | <AppHeader>
51 | <property name="logoTemplate">
52 | <Heading level="h3" value="Example App"/>
53 | </property>
54 | </AppHeader>
55 | <NavPanel>
56 | <NavLink label="Home" to="/" icon="home"/>
57 | <NavLink label="Page 1" to="/page1"/>
58 | <NavLink label="Page 2" to="/page2"/>
59 | </NavPanel>
60 | <Pages fallbackPath="/">
61 | <Page url="/">
62 | <List data="https://api.spacexdata.com/v3/history">
63 | <property name="itemTemplate">
64 | <Card title="{$item.title}" subtitle="{$item.details}"/>
65 | </property>
66 | </List>
67 | </Page>
68 | <Page url="/page1">
69 | <Text value="Page 1" />
70 | </Page>
71 | <Page url="/page2">
72 | <Text value="Page 2" />
73 | </Page>
74 | </Pages>
75 | <Footer>Powered by XMLUI</Footer>
76 | </App>
77 | ```
78 |
79 | #### `horizontal-sticky`
80 |
81 | ```xmlui-pg copy name="Example: 'horizontal-sticky' layout" height="350px"
82 | <App layout="horizontal-sticky">
83 | <AppHeader>
84 | <property name="logoTemplate">
85 | <Heading level="h3" value="Example App"/>
86 | </property>
87 | </AppHeader>
88 | <NavPanel>
89 | <NavLink label="Home" to="/" icon="home"/>
90 | <NavLink label="Page 1" to="/page1"/>
91 | <NavLink label="Page 2" to="/page2"/>
92 | </NavPanel>
93 | <Pages fallbackPath="/">
94 | <Page url="/">
95 | <List data="https://api.spacexdata.com/v3/history">
96 | <property name="itemTemplate">
97 | <Card title="{$item.title}" subtitle="{$item.details}"/>
98 | </property>
99 | </List>
100 | </Page>
101 | <Page url="/page1">
102 | <Text value="Page 1" />
103 | </Page>
104 | <Page url="/page2">
105 | <Text value="Page 2" />
106 | </Page>
107 | </Pages>
108 | <Footer>Powered by XMLUI</Footer>
109 | </App>
110 | ```
111 |
112 | #### `condensed`
113 |
114 | ```xmlui-pg copy name="Example: 'condensed' layout" height="350px"
115 | <App layout="condensed">
116 | <property name="logoTemplate">
117 | <Heading level="h3" value="Example App"/>
118 | </property>
119 | <NavPanel>
120 | <NavLink label="Home" to="/" icon="home"/>
121 | <NavLink label="Page 1" to="/page1"/>
122 | <NavLink label="Page 2" to="/page2"/>
123 | </NavPanel>
124 | <Pages fallbackPath="/">
125 | <Page url="/">
126 | <List data="https://api.spacexdata.com/v3/history">
127 | <property name="itemTemplate">
128 | <Card title="{$item.title}" subtitle="{$item.details}"/>
129 | </property>
130 | </List>
131 | </Page>
132 | <Page url="/page1">
133 | <Text value="Page 1" />
134 | </Page>
135 | <Page url="/page2">
136 | <Text value="Page 2" />
137 | </Page>
138 | </Pages>
139 | <Footer>Powered by XMLUI</Footer>
140 | </App>
141 | ```
142 |
143 | #### `condensed-sticky`
144 |
145 | ```xmlui-pg copy name="Example: 'condensed-sticky' layout" height="350px"
146 | <App layout="condensed-sticky">
147 | <property name="logoTemplate">
148 | <Heading level="h3" value="Example App"/>
149 | </property>
150 | <NavPanel>
151 | <NavLink label="Home" to="/" icon="home"/>
152 | <NavLink label="Page 1" to="/page1"/>
153 | <NavLink label="Page 2" to="/page2"/>
154 | </NavPanel>
155 | <Pages fallbackPath="/">
156 | <Page url="/">
157 | <List data="https://api.spacexdata.com/v3/history">
158 | <property name="itemTemplate">
159 | <Card title="{$item.title}" subtitle="{$item.details}"/>
160 | </property>
161 | </List>
162 | </Page>
163 | <Page url="/page1">
164 | <Text value="Page 1" />
165 | </Page>
166 | <Page url="/page2">
167 | <Text value="Page 2" />
168 | </Page>
169 | </Pages>
170 | <Footer>Powered by XMLUI</Footer>
171 | </App>
172 | ```
173 |
174 | #### `vertical`
175 |
176 | ```xmlui-pg copy name="Example: 'vertical' layout" height="300px"
177 | <App layout="vertical">
178 | <property name="logoTemplate">
179 | <Heading level="h3" value="Example App"/>
180 | </property>
181 | <NavPanel>
182 | <NavLink label="Home" to="/" icon="home"/>
183 | <NavLink label="Page 1" to="/page1"/>
184 | <NavLink label="Page 2" to="/page2"/>
185 | </NavPanel>
186 | <Pages fallbackPath="/">
187 | <Page url="/">
188 | <List data="https://api.spacexdata.com/v3/history">
189 | <property name="itemTemplate">
190 | <Card title="{$item.title}" subtitle="{$item.details}"/>
191 | </property>
192 | </List>
193 | </Page>
194 | <Page url="/page1">
195 | <Text value="Page 1" />
196 | </Page>
197 | <Page url="/page2">
198 | <Text value="Page 2" />
199 | </Page>
200 | </Pages>
201 | <Footer>Powered by XMLUI</Footer>
202 | </App>
203 | ```
204 |
205 | #### `vertical-sticky`
206 |
207 | ```xmlui-pg copy name="Example: 'vertical-sticky' layout" height="300px"
208 | <App layout="vertical-sticky">
209 | <property name="logoTemplate">
210 | <Heading level="h3" value="Example App"/>
211 | </property>
212 | <NavPanel>
213 | <NavLink label="Home" to="/" icon="home"/>
214 | <NavLink label="Page 1" to="/page1"/>
215 | <NavLink label="Page 2" to="/page2"/>
216 | </NavPanel>
217 | <Pages fallbackPath="/">
218 | <Page url="/">
219 | <List data="https://api.spacexdata.com/v3/history">
220 | <property name="itemTemplate">
221 | <Card title="{$item.title}" subtitle="{$item.details}"/>
222 | </property>
223 | </List>
224 | </Page>
225 | <Page url="/page1">
226 | <Text value="Page 1" />
227 | </Page>
228 | <Page url="/page2">
229 | <Text value="Page 2" />
230 | </Page>
231 | </Pages>
232 | <Footer>Powered by XMLUI</Footer>
233 | </App>
234 | ```
235 |
236 | #### `vertical-full-header`
237 |
238 | ```xmlui-pg copy name="Example: 'vertical-full-header' layout" height="300px"
239 | <App layout="vertical-full-header">
240 | <AppHeader>
241 | <property name="logoTemplate">
242 | <Heading level="h3" value="Example App"/>
243 | </property>
244 | </AppHeader>
245 | <NavPanel>
246 | <NavLink label="Home" to="/" icon="home"/>
247 | <NavLink label="Page 1" to="/page1"/>
248 | <NavLink label="Page 2" to="/page2"/>
249 | </NavPanel>
250 | <Pages fallbackPath="/">
251 | <Page url="/">
252 | <List data="https://api.spacexdata.com/v3/history">
253 | <property name="itemTemplate">
254 | <Card title="{$item.title}" subtitle="{$item.details}"/>
255 | </property>
256 | </List>
257 | </Page>
258 | <Page url="/page1">
259 | <Text value="Page 1" />
260 | </Page>
261 | <Page url="/page2">
262 | <Text value="Page 2" />
263 | </Page>
264 | </Pages>
265 | <Footer>Powered by XMLUI</Footer>
266 | </App>
267 | ```
268 |
269 | #### `desktop`
270 |
271 | ```xmlui-pg copy name="Example: 'desktop' layout" height="300px"
272 | <App layout="desktop">
273 | <AppHeader>
274 | <property name="logoTemplate">
275 | <Heading level="h3" value="Example App"/>
276 | </property>
277 | </AppHeader>
278 | <Pages fallbackPath="/">
279 | <Page url="/">
280 | <List data="https://api.spacexdata.com/v3/history">
281 | <property name="itemTemplate">
282 | <Card title="{$item.title}" subtitle="{$item.details}"/>
283 | </property>
284 | </List>
285 | </Page>
286 | <Page url="/page1">
287 | <Text value="Page 1" />
288 | </Page>
289 | <Page url="/page2">
290 | <Text value="Page 2" />
291 | </Page>
292 | </Pages>
293 | <Footer>Powered by XMLUI</Footer>
294 | </App>
295 | ```
296 |
297 | The `desktop` layout is designed for full-screen desktop applications. It stretches the app to fill the entire browser viewport with zero padding and margins. The header (if present) docks to the top, the footer (if present) docks to the bottom, and the main content area stretches to fill all remaining vertical and horizontal space. This layout ignores all max-width constraints and scrollbar gutter settings to ensure edge-to-edge display.
298 |
299 | %-PROP-END
300 |
301 | %-PROP-START scrollWholePage
302 |
303 | This boolean property specifies whether the whole page should scroll (true) or just the content area (false).
304 | The default value is `true`.
305 |
306 | ```xmlui-pg copy display name="Example: scrollWholePage" height="150px"
307 | <App scrollWholePage="false">
308 | <NavPanel>
309 | <NavLink label="Home" to="/" icon="home"/>
310 | </NavPanel>
311 | <Pages fallbackPath="/">
312 | <Page url="/">
313 | <Text>
314 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
315 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
316 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
317 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
318 | </Text>
319 | </Page>
320 | </Pages>
321 | </App>
322 | ```
323 |
324 | %-PROP-END
325 |
326 | %-PROP-START loggedInUser
327 |
328 | Stores information about the currently logged in user.
329 | Currently, there is no restriction on what the user data must look like.
330 |
331 | ```xmlui-pg copy display name="Example: loggedInUser" height="180px"
332 | <App loggedInUser="{{ name: 'Joe', token: '1234' }}">
333 | <NavPanel>
334 | <NavLink label="Home" to="/" icon="home"/>
335 | </NavPanel>
336 | <Pages fallbackPath="/">
337 | <Page url="/">
338 | <Text value="User name: {loggedInUser.name}" />
339 | <Text value="User token: {loggedInUser.token}" />
340 | </Page>
341 | </Pages>
342 | </App>
343 | ```
344 |
345 | %-PROP-END
346 |
347 | %-EVENT-START ready
348 |
349 | This event fires when the `App` component finishes rendering on the page.
350 | Use it as `onReady` when inlining it on the component.
351 |
352 | ```xmlui-pg copy display name="Example: ready"
353 | <App onReady="isAppReady = true">
354 | <variable name="isAppReady" value="{false}"/>
355 | <Text value="{isAppReady ? 'App is ready' : 'Sadly, App is not ready'}" />
356 | </App>
357 | ```
358 |
359 | %-EVENT-END
360 |
361 | %-EVENT-START messageReceived
362 |
363 | The event handler method has two parameters. The first is the message sent; the second is the entire native event object.
364 |
365 | ```xmlui-pg copy display name="Example: messageReceived" /onMessageReceived/ /window.postMessage/
366 | <App
367 | var.message = "<none>"
368 | onMessageReceived="(msg, ev) => {
369 | message = JSON.stringify(msg);
370 | console.log('Message event received:', ev);
371 | }">
372 | <Button label="Send a message"
373 | onClick="window.postMessage({type: 'message', messages:'Here you are!'})" />
374 | <Text>Message received: {message}</Text>
375 | </App>
376 | ```
377 |
378 | %-EVENT-END
```
--------------------------------------------------------------------------------
/xmlui/src/components/Markdown/Markdown.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 | $themeVars: t.composeTextVars($themeVars, "Text") !global;
11 |
12 | $paddingTop-MarkDown: createThemeVar("paddingTop-Markdown");
13 | $paddingBottom-MarkDown: createThemeVar("paddingBottom-Markdown");
14 | $backgroundColor-MarkDown: createThemeVar("backgroundColor-Markdown");
15 |
16 | $themeVars: t.composePaddingVars($themeVars, "Blockquote");
17 | $themeVars: t.composeBorderVars($themeVars, "Blockquote");
18 | $color-accent-Blockquote: createThemeVar("color-accent-Blockquote");
19 | $width-accent-Blockquote: createThemeVar("width-accent-Blockquote");
20 | $backgroundColor-Blockquote: createThemeVar("backgroundColor-Blockquote");
21 | $paddingLeft-Blockquote: createThemeVar("paddingLeft-Blockquote");
22 | $borderRadius-Blockquote: createThemeVar("borderRadius-Blockquote");
23 | $marginTop-Blockquote: createThemeVar("marginTop-Blockquote");
24 | $marginBottom-Blockquote: createThemeVar("marginBottom-Blockquote");
25 |
26 | $themeVars: t.composePaddingVars($themeVars, "Admonition");
27 | $themeVars: t.composeBorderVars($themeVars, "Admonition");
28 | $backgroundColor-Admonition: createThemeVar("backgroundColor-Admonition");
29 | $borderRadius-Admonition: createThemeVar("borderRadius-Admonition");
30 | $size-icon-Admonition: createThemeVar("size-icon-Admonition");
31 | $marginTop-Admonition: createThemeVar("marginTop-Admonition");
32 | $marginBottom-Admonition: createThemeVar("marginBottom-Admonition");
33 | $marginLeft-Admonition-content: createThemeVar("marginLeft-Admonition-content");
34 |
35 | $marginTop-HtmlVideo: createThemeVar("marginTop-HtmlVideo");
36 | $marginBottom-HtmlVideo: createThemeVar("marginBottom-HtmlVideo");
37 |
38 | // Variables for @layer section - Text-markdown
39 | $marginTop-Text-markdown: createThemeVar("marginTop-Text-markdown");
40 | $marginBottom-Text-markdown: createThemeVar("marginBottom-Text-markdown");
41 | $marginLeft-Text-markdown: createThemeVar("marginLeft-Text-markdown");
42 | $marginRight-Text-markdown: createThemeVar("marginRight-Text-markdown");
43 |
44 | // Variables for @layer section - Heading margins in markdown
45 | $marginTop-H1-markdown: createThemeVar("marginTop-H1-markdown");
46 | $marginBottom-H1-markdown: createThemeVar("marginBottom-H1-markdown");
47 | $fontSize-H1-markdown: createThemeVar("fontSize-H1-markdown");
48 | $marginTop-H2-markdown: createThemeVar("marginTop-H2-markdown");
49 | $marginBottom-H2-markdown: createThemeVar("marginBottom-H2-markdown");
50 | $marginTop-H3-markdown: createThemeVar("marginTop-H3-markdown");
51 | $marginBottom-H3-markdown: createThemeVar("marginBottom-H3-markdown");
52 | $marginTop-H4-markdown: createThemeVar("marginTop-H4-markdown");
53 | $marginBottom-H4-markdown: createThemeVar("marginBottom-H4-markdown");
54 | $marginTop-H5-markdown: createThemeVar("marginTop-H5-markdown");
55 | $marginBottom-H5-markdown: createThemeVar("marginBottom-H5-markdown");
56 | $marginTop-H6-markdown: createThemeVar("marginTop-H6-markdown");
57 | $marginBottom-H6-markdown: createThemeVar("marginBottom-H6-markdown");
58 |
59 | // Variables for @layer section - Image in markdown
60 | $marginTop-Image-markdown: createThemeVar("marginTop-Image-markdown");
61 | $marginBottom-Image-markdown: createThemeVar("marginBottom-Image-markdown");
62 | $marginLeft-Image-markdown: createThemeVar("marginLeft-Image-markdown");
63 | $marginRight-Image-markdown: createThemeVar("marginRight-Image-markdown");
64 |
65 | // Variables for @layer section - Admonition variants
66 | $backgroundColor-Admonition-info: createThemeVar("backgroundColor-Admonition-info");
67 | $borderColor-Admonition-info: createThemeVar("borderColor-Admonition-info");
68 | $backgroundColor-Admonition-warning: createThemeVar("backgroundColor-Admonition-warning");
69 | $borderColor-Admonition-warning: createThemeVar("borderColor-Admonition-warning");
70 | $backgroundColor-Admonition-danger: createThemeVar("backgroundColor-Admonition-danger");
71 | $borderColor-Admonition-danger: createThemeVar("borderColor-Admonition-danger");
72 | $backgroundColor-Admonition-note: createThemeVar("backgroundColor-Admonition-note");
73 | $borderColor-Admonition-note: createThemeVar("borderColor-Admonition-note");
74 | $backgroundColor-Admonition-tip: createThemeVar("backgroundColor-Admonition-tip");
75 | $borderColor-Admonition-tip: createThemeVar("borderColor-Admonition-tip");
76 |
77 | // Variables for @layer section - HorizontalRule
78 | $borderColor-HorizontalRule: createThemeVar("borderColor-HorizontalRule");
79 | $borderStyle-HorizontalRule: createThemeVar("borderStyle-HorizontalRule");
80 | $borderWidth-HorizontalRule: createThemeVar("borderWidth-HorizontalRule");
81 |
82 | @layer components {
83 | .markdownContent {
84 | padding-top: $paddingTop-MarkDown;
85 | padding-bottom: $paddingBottom-MarkDown;
86 | background-color: $backgroundColor-MarkDown;
87 | min-width: 0;
88 | width: 100%;
89 | @include t.textVars($themeVars, "Text");
90 |
91 | .markdown {
92 | $component: "Text";
93 | $variantName: "Text-markdown";
94 | $themeVars: t.composePaddingVars($themeVars, variantName);
95 | $themeVars: t.composeBorderVars($themeVars, $variantName);
96 | $themeVars: t.composeTextVars($themeVars, $variantName, $component);
97 | @include t.paddingVars($themeVars, $variantName);
98 | @include t.borderVars($themeVars, $variantName);
99 | @include t.textVars($themeVars, $variantName);
100 | margin-top: $marginTop-Text-markdown;
101 | margin-bottom: $marginBottom-Text-markdown;
102 | margin-left: $marginLeft-Text-markdown;
103 | margin-right: $marginRight-Text-markdown;
104 | overflow: visible;
105 | display: block;
106 | }
107 |
108 | // --- Additional Heading styles
109 | h1 {
110 | margin-top: $marginTop-H1-markdown !important;
111 | margin-bottom: $marginBottom-H1-markdown !important;
112 | font-size: $fontSize-H1-markdown !important;
113 | }
114 |
115 | h2 {
116 | margin-top: $marginTop-H2-markdown !important;
117 | margin-bottom: $marginBottom-H2-markdown !important;
118 | }
119 |
120 | h3 {
121 | margin-top: $marginTop-H3-markdown !important;
122 | margin-bottom: $marginBottom-H3-markdown !important;
123 | }
124 |
125 | h4 {
126 | margin-top: $marginTop-H4-markdown !important;
127 | margin-bottom: $marginBottom-H4-markdown !important;
128 | }
129 |
130 | h5 {
131 | margin-top: $marginTop-H5-markdown !important;
132 | margin-bottom: $marginBottom-H5-markdown !important;
133 | }
134 |
135 | h6 {
136 | margin-top: $marginTop-H6-markdown !important;
137 | margin-bottom: $marginBottom-H6-markdown !important;
138 | }
139 |
140 | // --- Image
141 | .block {
142 | margin-top: $marginTop-Image-markdown;
143 | margin-bottom: $marginBottom-Image-markdown;
144 | margin-left: $marginLeft-Image-markdown;
145 | margin-right: $marginRight-Image-markdown;
146 | }
147 |
148 | // --- Blockquote
149 |
150 | .blockquote {
151 | position: relative;
152 | margin-top: $marginTop-Blockquote;
153 | margin-bottom: $marginBottom-Blockquote;
154 | background-color: $backgroundColor-Blockquote;
155 |
156 | &::before {
157 | background-color: $color-accent-Blockquote;
158 | position: absolute;
159 | top: 0;
160 | left: 0;
161 | display: block;
162 | content: "";
163 | height: 100%;
164 | width: $width-accent-Blockquote;
165 | }
166 | }
167 |
168 | .blockquoteContainer {
169 | @include t.borderVars($themeVars, "Blockquote");
170 | @include t.paddingVars($themeVars, "Blockquote");
171 | }
172 |
173 | .admonitionBlockquote {
174 | margin-top: $marginTop-Admonition;
175 | margin-bottom: $marginBottom-Admonition;
176 | background-color: $backgroundColor-Admonition;
177 | border-radius: $borderRadius-Admonition;
178 | @include t.borderVars($themeVars, "Admonition");
179 | @include t.paddingVars($themeVars, "Admonition");
180 |
181 | &.info {
182 | background-color: $backgroundColor-Admonition-info;
183 | border-color: $borderColor-Admonition-info;
184 | }
185 | &.warning {
186 | background-color: $backgroundColor-Admonition-warning;
187 | border-color: $borderColor-Admonition-warning;
188 | }
189 | &.danger {
190 | background-color: $backgroundColor-Admonition-danger;
191 | border-color: $borderColor-Admonition-danger;
192 | }
193 | &.note {
194 | background-color: $backgroundColor-Admonition-note;
195 | border-color: $borderColor-Admonition-note;
196 | }
197 | &.tip {
198 | background-color: $backgroundColor-Admonition-tip;
199 | border-color: $borderColor-Admonition-tip;
200 | }
201 | }
202 |
203 | .admonitionContainer {
204 | padding: 0.5rem;
205 | display: flex;
206 | align-items: flex-start;
207 | }
208 |
209 | .admonitionIcon {
210 | font-size: $size-icon-Admonition;
211 | line-height: 1;
212 | }
213 |
214 | .admonitionContent {
215 | margin-left: $marginLeft-Admonition-content;
216 | flex: 1;
217 | min-width: 0;
218 | }
219 |
220 | .admonitionBlockquote {
221 | .admonitionContent {
222 | [class*="text_"][class*="markdown_"],
223 | ul,
224 | ol {
225 | margin-top: 0;
226 | margin-bottom: 0;
227 | }
228 | }
229 | }
230 |
231 | .horizontalRule {
232 | border-top-color: $borderColor-HorizontalRule;
233 | border-top-style: $borderStyle-HorizontalRule;
234 | border-top-width: $borderWidth-HorizontalRule;
235 | }
236 |
237 | li:has(> input[type="checkbox"]),
238 | li:has(> input[type="checkbox"]) {
239 | display: flex;
240 | align-items: flex-start;
241 |
242 | > input[type="checkbox"] {
243 | margin-right: 8px;
244 | margin-top: 4px;
245 | flex-shrink: 0;
246 | }
247 | }
248 |
249 | // First element should have no top margin
250 | > *:first-child {
251 | margin-top: 0;
252 | }
253 |
254 | // Last element should have no bottom margin
255 | > *:last-child {
256 | margin-bottom: 0;
257 | }
258 |
259 | // --- Table
260 | .tableScrollContainer {
261 | overflow-x: auto;
262 | width: 100%;
263 | }
264 |
265 | // --- Details adornment
266 | .detailsAdornment {
267 | margin-top: $marginTop-Admonition;
268 | margin-bottom: $marginBottom-Admonition;
269 | border-radius: $borderRadius-Admonition;
270 | background-color: $backgroundColor-Admonition;
271 | @include t.borderVars($themeVars, "Admonition");
272 |
273 | // Override ExpandableItem styles for better integration
274 | :global(.summary) {
275 | padding: 0.5rem 1rem;
276 | font-weight: 600; // Make summary text bold
277 | }
278 |
279 | :global(.content) {
280 | padding: 0 1rem 0.5rem 1rem;
281 | }
282 | }
283 | }
284 |
285 | // --- UnorderedList
286 | /*
287 |
288 | $paddingLeft-UnorderedList: createThemeVar("paddingLeft-UnorderedList");
289 |
290 | // the basic <ul> and <ol> styles are the same in tabler.io too
291 | .unorderedList {
292 | list-style-type: revert;
293 | list-style-position: outside;
294 | padding-left: $paddingLeft-UnorderedList;
295 | }
296 |
297 | // --- OrderedList
298 | $paddingLeft-OrderedList: createThemeVar("paddingLeft-OrderedList");
299 |
300 | // the basic <ul> and <ol> styles are the same in tabler.io too
301 | .orderedList {
302 | list-style-type: revert;
303 | list-style-position: outside;
304 | padding-left: $paddingLeft-OrderedList;
305 | }
306 |
307 | // --- ListItem
308 |
309 | $paddingLeft-ListItem: createThemeVar("paddingLeft-ListItem");
310 | $color-marker-ListItem: createThemeVar("color-marker-ListItem");
311 |
312 | .listItem {
313 | padding-left: $paddingLeft-ListItem;
314 | }
315 |
316 | .listItem::marker {
317 | color: $color-marker-ListItem;
318 | }
319 | */
320 | }
321 |
322 | // --- We export the theme variables to add them to the component renderer
323 | :export {
324 | themeVars: t.json-stringify($themeVars);
325 | }
326 |
```