This is page 78 of 190. Use http://codebase.md/xmlui-org/xmlui/xmlui/tools/vscode/resources/xmlui-markup-syntax-highlighting.png?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── tender-llamas-dress.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
│ │ │ ├── Choose.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
│ │ ├── Choose
│ │ │ ├── Choose.md
│ │ │ ├── Choose.spec.ts
│ │ │ └── Choose.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/tests/components-core/scripts-runner/process-switch.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { createEvalContext, parseStatements } from "./test-helpers";
2 | import { describe, expect, it, assert } from "vitest";
3 | import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async";
4 |
5 | describe("Process switch statements (exp)", () => {
6 | it("no case", async () => {
7 | // --- Arrange
8 | const source = `
9 | let x = 0;
10 | switch (x) {
11 | }
12 | `;
13 | const evalContext = createEvalContext({});
14 | const statements = parseStatements(source);
15 |
16 | // --- Act
17 | await processStatementQueueAsync(statements, evalContext);
18 |
19 | // --- Assert
20 | const thread = evalContext.mainThread!;
21 | expect(thread.blocks!.length).equal(1);
22 | expect(thread.blocks![0].vars.x).equal(0);
23 | });
24 |
25 | it("no matching case #1", async () => {
26 | // --- Arrange
27 | const source = `
28 | let x = 0;
29 | let y = 0;
30 | switch (x) {
31 | case 3:
32 | y++;
33 | }
34 | `;
35 | const evalContext = createEvalContext({});
36 | const statements = parseStatements(source);
37 |
38 | // --- Act
39 | await processStatementQueueAsync(statements, evalContext);
40 |
41 | // --- Assert
42 | const thread = evalContext.mainThread!;
43 | expect(thread.blocks!.length).equal(1);
44 | expect(thread.blocks![0].vars.y).equal(0);
45 | });
46 |
47 | it("no matching case #2", async () => {
48 | // --- Arrange
49 | const source = `
50 | let x = 0;
51 | let y = 0;
52 | switch (x) {
53 | case 2:
54 | y++;
55 | break;
56 | case 3:
57 | y++;
58 | break;
59 | }
60 | `;
61 | const evalContext = createEvalContext({});
62 | const statements = parseStatements(source);
63 |
64 | // --- Act
65 | await processStatementQueueAsync(statements, evalContext);
66 |
67 | // --- Assert
68 | const thread = evalContext.mainThread!;
69 | expect(thread.blocks!.length).equal(1);
70 | expect(thread.blocks![0].vars.y).equal(0);
71 | });
72 |
73 | it("no matching case #3", async () => {
74 | // --- Arrange
75 | const source = `
76 | let x = 0;
77 | let y = 0;
78 | switch (x) {
79 | case 1:
80 | y++;
81 | case 2:
82 | y++;
83 | break;
84 | case 3:
85 | y++;
86 | break;
87 | }
88 | `;
89 | const evalContext = createEvalContext({});
90 | const statements = parseStatements(source);
91 |
92 | // --- Act
93 | await processStatementQueueAsync(statements, evalContext);
94 |
95 | // --- Assert
96 | const thread = evalContext.mainThread!;
97 | expect(thread.blocks!.length).equal(1);
98 | expect(thread.blocks![0].vars.y).equal(0);
99 | });
100 |
101 | it("matching case #1", async () => {
102 | // --- Arrange
103 | const source = `
104 | let x = 0;
105 | let y = 0;
106 | switch (x) {
107 | case 0:
108 | y++;
109 | break;
110 | case 1:
111 | y++;
112 | break;
113 | case 2:
114 | y++;
115 | break;
116 | case 3:
117 | y++;
118 | break;
119 | }
120 | `;
121 | const evalContext = createEvalContext({});
122 | const statements = parseStatements(source);
123 |
124 | // --- Act
125 | await processStatementQueueAsync(statements, evalContext);
126 |
127 | // --- Assert
128 | const thread = evalContext.mainThread!;
129 | expect(thread.blocks!.length).equal(1);
130 | expect(thread.blocks![0].vars.y).equal(1);
131 | });
132 |
133 | it("matching case #2", async () => {
134 | // --- Arrange
135 | const source = `
136 | let x = 0;
137 | let y = 0;
138 | switch (x) {
139 | default:
140 | y++;
141 | break;
142 | case 1:
143 | y++;
144 | break;
145 | case 2:
146 | y++;
147 | break;
148 | case 3:
149 | y++;
150 | break;
151 | }
152 | `;
153 | const evalContext = createEvalContext({});
154 | const statements = parseStatements(source);
155 |
156 | // --- Act
157 | await processStatementQueueAsync(statements, evalContext);
158 |
159 | // --- Assert
160 | const thread = evalContext.mainThread!;
161 | expect(thread.blocks!.length).equal(1);
162 | expect(thread.blocks![0].vars.y).equal(1);
163 | });
164 |
165 | it("matching case #3", async () => {
166 | // --- Arrange
167 | const source = `
168 | let x = 0;
169 | let y = 0;
170 | switch (x) {
171 | case 1:
172 | y++;
173 | break;
174 | default:
175 | y++;
176 | break;
177 | case 2:
178 | y++;
179 | break;
180 | case 3:
181 | y++;
182 | break;
183 | }
184 | `;
185 | const evalContext = createEvalContext({});
186 | const statements = parseStatements(source);
187 |
188 | // --- Act
189 | await processStatementQueueAsync(statements, evalContext);
190 |
191 | // --- Assert
192 | const thread = evalContext.mainThread!;
193 | expect(thread.blocks!.length).equal(1);
194 | expect(thread.blocks![0].vars.y).equal(1);
195 | });
196 |
197 | it("matching case #4", async () => {
198 | // --- Arrange
199 | const source = `
200 | let x = 0;
201 | let y = 0;
202 | switch (x) {
203 | case 1:
204 | y++;
205 | break;
206 | case 2:
207 | y++;
208 | break;
209 | default:
210 | y++;
211 | break;
212 | case 3:
213 | y++;
214 | break;
215 | }
216 | `;
217 | const evalContext = createEvalContext({});
218 | const statements = parseStatements(source);
219 |
220 | // --- Act
221 | await processStatementQueueAsync(statements, evalContext);
222 |
223 | // --- Assert
224 | const thread = evalContext.mainThread!;
225 | expect(thread.blocks!.length).equal(1);
226 | expect(thread.blocks![0].vars.y).equal(1);
227 | });
228 |
229 | it("matching case #5", async () => {
230 | // --- Arrange
231 | const source = `
232 | let x = 0;
233 | let y = 0;
234 | switch (x) {
235 | case 1:
236 | y++;
237 | break;
238 | case 2:
239 | y++;
240 | break;
241 | case 3:
242 | y++;
243 | break;
244 | default:
245 | y++;
246 | break;
247 | }
248 | `;
249 | const evalContext = createEvalContext({});
250 | const statements = parseStatements(source);
251 |
252 | // --- Act
253 | await processStatementQueueAsync(statements, evalContext);
254 |
255 | // --- Assert
256 | const thread = evalContext.mainThread!;
257 | expect(thread.blocks!.length).equal(1);
258 | expect(thread.blocks![0].vars.y).equal(1);
259 | });
260 |
261 | it("matching case, fall-through #1", async () => {
262 | // --- Arrange
263 | const source = `
264 | let x = 0;
265 | let y = 0;
266 | switch (x) {
267 | case 0:
268 | y++;
269 | case 1:
270 | y++;
271 | break;
272 | case 2:
273 | y++;
274 | break;
275 | case 3:
276 | y++;
277 | break;
278 | }
279 | `;
280 | const evalContext = createEvalContext({});
281 | const statements = parseStatements(source);
282 |
283 | // --- Act
284 | await processStatementQueueAsync(statements, evalContext);
285 |
286 | // --- Assert
287 | const thread = evalContext.mainThread!;
288 | expect(thread.blocks!.length).equal(1);
289 | expect(thread.blocks![0].vars.y).equal(2);
290 | });
291 |
292 | it("matching case, fall-through #2", async () => {
293 | // --- Arrange
294 | const source = `
295 | let x = 0;
296 | let y = 0;
297 | switch (x) {
298 | case 0:
299 | y++;
300 | case 1:
301 | y++;
302 | case 2:
303 | y++;
304 | break;
305 | case 3:
306 | y++;
307 | break;
308 | }
309 | `;
310 | const evalContext = createEvalContext({});
311 | const statements = parseStatements(source);
312 |
313 | // --- Act
314 | await processStatementQueueAsync(statements, evalContext);
315 |
316 | // --- Assert
317 | const thread = evalContext.mainThread!;
318 | expect(thread.blocks!.length).equal(1);
319 | expect(thread.blocks![0].vars.y).equal(3);
320 | });
321 |
322 | it("matching case, fall-through #3", async () => {
323 | // --- Arrange
324 | const source = `
325 | let x = 0;
326 | let y = 0;
327 | switch (x) {
328 | case 0:
329 | y++;
330 | case 1:
331 | y++;
332 | case 2:
333 | y++;
334 | case 3:
335 | y++;
336 | break;
337 | }
338 | `;
339 | const evalContext = createEvalContext({});
340 | const statements = parseStatements(source);
341 |
342 | // --- Act
343 | await processStatementQueueAsync(statements, evalContext);
344 |
345 | // --- Assert
346 | const thread = evalContext.mainThread!;
347 | expect(thread.blocks!.length).equal(1);
348 | expect(thread.blocks![0].vars.y).equal(4);
349 | });
350 |
351 | it("matching case, fall-through #4", async () => {
352 | // --- Arrange
353 | const source = `
354 | let x = 0;
355 | let y = 0;
356 | switch (x) {
357 | case 0:
358 | y++;
359 | case 1:
360 | y++;
361 | case 2:
362 | y++;
363 | case 3:
364 | y++;
365 | }
366 | `;
367 | const evalContext = createEvalContext({});
368 | const statements = parseStatements(source);
369 |
370 | // --- Act
371 | await processStatementQueueAsync(statements, evalContext);
372 |
373 | // --- Assert
374 | const thread = evalContext.mainThread!;
375 | expect(thread.blocks!.length).equal(1);
376 | expect(thread.blocks![0].vars.y).equal(4);
377 | });
378 |
379 | it("switch in loop #1", async () => {
380 | // --- Arrange
381 | const source = `
382 | let x = 0;
383 | let y = 0;
384 | while (x < 4) {
385 | switch (x) {
386 | case 0:
387 | y++;
388 | case 1:
389 | y++;
390 | case 2:
391 | y++;
392 | case 3:
393 | y++;
394 | }
395 | x++;
396 | }
397 | `;
398 | const evalContext = createEvalContext({});
399 | const statements = parseStatements(source);
400 |
401 | // --- Act
402 | await processStatementQueueAsync(statements, evalContext);
403 |
404 | // --- Assert
405 | const thread = evalContext.mainThread!;
406 | expect(thread.blocks!.length).equal(1);
407 | expect(thread.blocks![0].vars.y).equal(10);
408 | });
409 |
410 | it("switch in loop #2", async () => {
411 | // --- Arrange
412 | const source = `
413 | let x = 0;
414 | let y = 0;
415 | while (x < 4) {
416 | switch (x) {
417 | case 0:
418 | y++;
419 | case 1:
420 | y++;
421 | case 2:
422 | y++;
423 | case 3:
424 | y++;
425 | }
426 | x++;
427 | break;
428 | }
429 | `;
430 | const evalContext = createEvalContext({});
431 | const statements = parseStatements(source);
432 |
433 | // --- Act
434 | await processStatementQueueAsync(statements, evalContext);
435 |
436 | // --- Assert
437 | const thread = evalContext.mainThread!;
438 | expect(thread.blocks!.length).equal(1);
439 | expect(thread.blocks![0].vars.y).equal(4);
440 | });
441 |
442 | it("switch in loop #3", async () => {
443 | // --- Arrange
444 | const source = `
445 | let x = 0;
446 | let y = 0;
447 | while (x < 4) {
448 | switch (x) {
449 | case 0:
450 | y++;
451 | break;
452 | case 1:
453 | y++;
454 | break;
455 | case 2:
456 | y++;
457 | break;
458 | case 3:
459 | y++;
460 | break;
461 | }
462 | x++;
463 | }
464 | `;
465 | const evalContext = createEvalContext({});
466 | const statements = parseStatements(source);
467 |
468 | // --- Act
469 | await processStatementQueueAsync(statements, evalContext);
470 |
471 | // --- Assert
472 | const thread = evalContext.mainThread!;
473 | expect(thread.blocks!.length).equal(1);
474 | expect(thread.blocks![0].vars.y).equal(4);
475 | });
476 |
477 | it("switch in loop #4", async () => {
478 | // --- Arrange
479 | const source = `
480 | let x = 0;
481 | let y = 0;
482 | while (x < 4) {
483 | switch (x) {
484 | case 0:
485 | y++;
486 | break;
487 | case 1:
488 | y++;
489 | break;
490 | case 2:
491 | y++;
492 | case 3:
493 | y++;
494 | }
495 | x++;
496 | }
497 | `;
498 | const evalContext = createEvalContext({});
499 | const statements = parseStatements(source);
500 |
501 | // --- Act
502 | await processStatementQueueAsync(statements, evalContext);
503 |
504 | // --- Assert
505 | const thread = evalContext.mainThread!;
506 | expect(thread.blocks!.length).equal(1);
507 | expect(thread.blocks![0].vars.y).equal(5);
508 | });
509 |
510 | it("switch in loop #5 (continue)", async () => {
511 | // --- Arrange
512 | const source = `
513 | let x = 0;
514 | let y = 0;
515 | while (x < 4) {
516 | switch (x) {
517 | case 0:
518 | y++;
519 | break;
520 | case 1:
521 | y++;
522 | break;
523 | case 2:
524 | y++;
525 | x += 3;
526 | continue;
527 | case 3:
528 | y++;
529 | }
530 | x++;
531 | }
532 | `;
533 | const evalContext = createEvalContext({});
534 | const statements = parseStatements(source);
535 |
536 | // --- Act
537 | await processStatementQueueAsync(statements, evalContext);
538 |
539 | // --- Assert
540 | const thread = evalContext.mainThread!;
541 | expect(thread.blocks!.length).equal(1);
542 | expect(thread.blocks![0].vars.y).equal(3);
543 | });
544 |
545 | it("switch in loop #6 (return)", async () => {
546 | // --- Arrange
547 | const source = `
548 | let x = 0;
549 | let y = 0;
550 | while (x < 4) {
551 | switch (x) {
552 | case 0:
553 | y++;
554 | break;
555 | case 1:
556 | y++;
557 | break;
558 | case 2:
559 | y++;
560 | x += 3;
561 | return;
562 | case 3:
563 | y++;
564 | }
565 | x++;
566 | }
567 | `;
568 | const evalContext = createEvalContext({});
569 | const statements = parseStatements(source);
570 |
571 | // --- Act
572 | await processStatementQueueAsync(statements, evalContext);
573 |
574 | // --- Assert
575 | const thread = evalContext.mainThread!;
576 | expect(thread.blocks![0].vars.y).equal(3);
577 | });
578 |
579 | it("switch has its own scope", async () => {
580 | // --- Arrange
581 | const source = `
582 | let x = 0;
583 | let y = 0;
584 | let z = 0;
585 | switch (x) {
586 | case 0:
587 | let y = 3;
588 | z = y + 1;
589 | break;
590 | case 1:
591 | y++;
592 | break;
593 | }
594 | `;
595 | const evalContext = createEvalContext({});
596 | const statements = parseStatements(source);
597 |
598 | // --- Act
599 | await processStatementQueueAsync(statements, evalContext);
600 |
601 | // --- Assert
602 | const thread = evalContext.mainThread!;
603 | expect(thread.blocks![0].vars.y).equal(0);
604 | expect(thread.blocks![0].vars.z).equal(4);
605 | });
606 |
607 | it("switch fails with multiple let", async () => {
608 | // --- Arrange
609 | const source = `
610 | let x = 0;
611 | let y = 0;
612 | switch (x) {
613 | case 0:
614 | let z = 3;
615 | case 1:
616 | let z = 4;
617 | y++;
618 | break;
619 | }
620 | `;
621 | const evalContext = createEvalContext({});
622 | const statements = parseStatements(source);
623 |
624 | // --- Act/Assert
625 | try {
626 | await processStatementQueueAsync(statements, evalContext);
627 | } catch (err: any) {
628 | expect(err.toString().includes("already declared")).equal(true);
629 | return;
630 | }
631 | assert.fail("Exception expected");
632 | });
633 |
634 | it("switch fails with multiple const/let", async () => {
635 | // --- Arrange
636 | const source = `
637 | let x = 0;
638 | let y = 0;
639 | switch (x) {
640 | case 0:
641 | let z = 3;
642 | case 1:
643 | const z = 4;
644 | y++;
645 | break;
646 | }
647 | `;
648 | const evalContext = createEvalContext({});
649 | const statements = parseStatements(source);
650 |
651 | // --- Act/Assert
652 | try {
653 | await processStatementQueueAsync(statements, evalContext);
654 | } catch (err: any) {
655 | expect(err.toString().includes("already declared")).equal(true);
656 | return;
657 | }
658 | assert.fail("Exception expected");
659 | });
660 |
661 | it("switch fails with multiple const", async () => {
662 | // --- Arrange
663 | const source = `
664 | let x = 0;
665 | let y = 0;
666 | switch (x) {
667 | case 0:
668 | const z = 3;
669 | case 1:
670 | const z = 4;
671 | y++;
672 | break;
673 | }
674 | `;
675 | const evalContext = createEvalContext({});
676 | const statements = parseStatements(source);
677 |
678 | // --- Act/Assert
679 | try {
680 | await processStatementQueueAsync(statements, evalContext);
681 | } catch (err: any) {
682 | expect(err.toString().includes("already declared")).equal(true);
683 | return;
684 | }
685 | assert.fail("Exception expected");
686 | });
687 | });
688 |
```
--------------------------------------------------------------------------------
/docs/content/components/PasswordInput.md:
--------------------------------------------------------------------------------
```markdown
1 | # PasswordInput [#passwordinput]
2 |
3 | `Password` is a specialized [TextBox](/components/TextBox) that enables users to input and edit passwords.
4 |
5 | ## Properties [#properties]
6 |
7 | ### `autoFocus` (default: false) [#autofocus-default-false]
8 |
9 | If this property is set to `true`, the component gets the focus automatically when displayed.
10 |
11 | ### `enabled` (default: true) [#enabled-default-true]
12 |
13 | This boolean property value indicates whether the component responds to user events (`true`) or not (`false`).
14 |
15 | ### `endIcon` [#endicon]
16 |
17 | This property sets an optional icon to appear on the end (right side when the left-to-right direction is set) of the input.
18 |
19 | ### `endText` [#endtext]
20 |
21 | This property sets an optional text to appear on the end (right side when the left-to-right direction is set) of the input.
22 |
23 | ### `gap` [#gap]
24 |
25 | This property defines the gap between the adornments and the input area. If not set, the gap declared by the current theme is used.
26 |
27 | ### `initialValue` (default: "") [#initialvalue-default-]
28 |
29 | This property sets the component's initial value.
30 |
31 | ### `maxLength` [#maxlength]
32 |
33 | This property sets the maximum length of the input it accepts.
34 |
35 | ### `passwordHiddenIcon` (default: "eye-off") [#passwordhiddenicon-default-eye-off]
36 |
37 | The icon to display when the password is hidden (when showPasswordToggle is true).
38 |
39 | ### `passwordVisibleIcon` (default: "eye") [#passwordvisibleicon-default-eye]
40 |
41 | The icon to display when the password is visible (when showPasswordToggle is true).
42 |
43 | ### `placeholder` [#placeholder]
44 |
45 | An optional placeholder text that is visible in the input field when its empty.
46 |
47 | ### `readOnly` (default: false) [#readonly-default-false]
48 |
49 | Set this property to `true` to disallow changing the component value.
50 |
51 | ### `required` (default: false) [#required-default-false]
52 |
53 | Set this property to `true` to indicate it must have a value before submitting the containing form.
54 |
55 | ### `showPasswordToggle` (default: false) [#showpasswordtoggle-default-false]
56 |
57 | If `true`, a toggle button is displayed to switch between showing and hiding the password input.
58 |
59 | ### `startIcon` [#starticon]
60 |
61 | This property sets an optional icon to appear at the start (left side when the left-to-right direction is set) of the input.
62 |
63 | ### `startText` [#starttext]
64 |
65 | This property sets an optional text to appear at the start (left side when the left-to-right direction is set) of the input.
66 |
67 | ### `validationStatus` (default: "none") [#validationstatus-default-none]
68 |
69 | This property allows you to set the validation status of the input component.
70 |
71 | Available values:
72 |
73 | | Value | Description |
74 | | --- | --- |
75 | | `valid` | Visual indicator for an input that is accepted |
76 | | `warning` | Visual indicator for an input that produced a warning |
77 | | `error` | Visual indicator for an input that produced an error |
78 |
79 | ## Events [#events]
80 |
81 | ### `didChange` [#didchange]
82 |
83 | This event is triggered when value of TextBox has changed.
84 |
85 | ### `gotFocus` [#gotfocus]
86 |
87 | This event is triggered when the TextBox has received the focus.
88 |
89 | ### `lostFocus` [#lostfocus]
90 |
91 | This event is triggered when the TextBox has lost the focus.
92 |
93 | ## Exposed Methods [#exposed-methods]
94 |
95 | ### `focus` [#focus]
96 |
97 | This method sets the focus on the `TextBox` component.
98 |
99 | **Signature**: `focus(): void`
100 |
101 | ### `setValue` [#setvalue]
102 |
103 | This API sets the value of the `TextBox`. You can use it to programmatically change the value.
104 |
105 | **Signature**: `setValue(value: string): void`
106 |
107 | - `value`: The new value to set. If the value is empty, it will clear the input.
108 |
109 | ### `value` [#value]
110 |
111 | You can query the component's value. If no value is set, it will retrieve `undefined`.
112 |
113 | **Signature**: `get value(): string | undefined`
114 |
115 | ## Parts [#parts]
116 |
117 | The component has some parts that can be styled through layout properties and theme variables separately:
118 |
119 | - **`endAdornment`**: The adornment displayed at the end of the text box.
120 | - **`input`**: The text box input area.
121 | - **`label`**: The label displayed for the text box.
122 | - **`startAdornment`**: The adornment displayed at the start of the text box.
123 |
124 | **Default part**: `input`
125 |
126 | ## Styling [#styling]
127 |
128 | ### Theme Variables [#theme-variables]
129 |
130 | | Variable | Default Value (Light) | Default Value (Dark) |
131 | | --- | --- | --- |
132 | | [backgroundColor](../styles-and-themes/common-units/#color)-Input--disabled | $backgroundColor--disabled | $backgroundColor--disabled |
133 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--default | *none* | *none* |
134 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--default--focus | *none* | *none* |
135 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--default--hover | *none* | *none* |
136 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--disabled | *none* | *none* |
137 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--error | *none* | *none* |
138 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--error--focus | *none* | *none* |
139 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--error--hover | *none* | *none* |
140 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--success | *none* | *none* |
141 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--success--focus | *none* | *none* |
142 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--success--hover | *none* | *none* |
143 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--warning | *none* | *none* |
144 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--warning--focus | *none* | *none* |
145 | | [backgroundColor](../styles-and-themes/common-units/#color)-TextBox--warning--hover | *none* | *none* |
146 | | [borderColor](../styles-and-themes/common-units/#color)-Input--default | $borderColor-Input-default | $borderColor-Input-default |
147 | | [borderColor](../styles-and-themes/common-units/#color)-Input--default--hover | $borderColor-Input-default--hover | $borderColor-Input-default--hover |
148 | | [borderColor](../styles-and-themes/common-units/#color)-Input--disabled | $borderColor--disabled | $borderColor--disabled |
149 | | [borderColor](../styles-and-themes/common-units/#color)-Input--error | $borderColor-Input-default--error | $borderColor-Input-default--error |
150 | | [borderColor](../styles-and-themes/common-units/#color)-Input--success | $borderColor-Input-default--success | $borderColor-Input-default--success |
151 | | [borderColor](../styles-and-themes/common-units/#color)-Input--warning | $borderColor-Input-default--warning | $borderColor-Input-default--warning |
152 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--default | *none* | *none* |
153 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--default--focus | *none* | *none* |
154 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--default--hover | *none* | *none* |
155 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--disabled | *none* | *none* |
156 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--error | *none* | *none* |
157 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--error--focus | *none* | *none* |
158 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--error--hover | *none* | *none* |
159 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--success | *none* | *none* |
160 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--success--focus | *none* | *none* |
161 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--success--hover | *none* | *none* |
162 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--warning | *none* | *none* |
163 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--warning--focus | *none* | *none* |
164 | | [borderColor](../styles-and-themes/common-units/#color)-TextBox--warning--hover | *none* | *none* |
165 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-Input | $borderRadius | $borderRadius |
166 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-TextBox--default | *none* | *none* |
167 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-TextBox--error | *none* | *none* |
168 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-TextBox--success | *none* | *none* |
169 | | [borderRadius](../styles-and-themes/common-units/#border-rounding)-TextBox--warning | *none* | *none* |
170 | | [borderStyle](../styles-and-themes/common-units/#border-style)-Input | solid | solid |
171 | | [borderStyle](../styles-and-themes/common-units/#border-style)-TextBox--default | *none* | *none* |
172 | | [borderStyle](../styles-and-themes/common-units/#border-style)-TextBox--error | *none* | *none* |
173 | | [borderStyle](../styles-and-themes/common-units/#border-style)-TextBox--success | *none* | *none* |
174 | | [borderStyle](../styles-and-themes/common-units/#border-style)-TextBox--warning | *none* | *none* |
175 | | [borderWidth](../styles-and-themes/common-units/#size)-Input | 1px | 1px |
176 | | [borderWidth](../styles-and-themes/common-units/#size)-TextBox--default | *none* | *none* |
177 | | [borderWidth](../styles-and-themes/common-units/#size)-TextBox--error | *none* | *none* |
178 | | [borderWidth](../styles-and-themes/common-units/#size)-TextBox--success | *none* | *none* |
179 | | [borderWidth](../styles-and-themes/common-units/#size)-TextBox--warning | *none* | *none* |
180 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--default | *none* | *none* |
181 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--default--focus | *none* | *none* |
182 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--default--hover | *none* | *none* |
183 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--error | *none* | *none* |
184 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--error--focus | *none* | *none* |
185 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--error--hover | *none* | *none* |
186 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--success | *none* | *none* |
187 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--success--focus | *none* | *none* |
188 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--success--hover | *none* | *none* |
189 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--warning | *none* | *none* |
190 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--warning--focus | *none* | *none* |
191 | | [boxShadow](../styles-and-themes/common-units/#boxShadow)-TextBox--warning--hover | *none* | *none* |
192 | | [color](../styles-and-themes/common-units/#color)-adornment-Input | $textColor-subtitle | $textColor-subtitle |
193 | | [color](../styles-and-themes/common-units/#color)-adornment-TextBox--default | *none* | *none* |
194 | | [color](../styles-and-themes/common-units/#color)-adornment-TextBox--error | *none* | *none* |
195 | | [color](../styles-and-themes/common-units/#color)-adornment-TextBox--success | *none* | *none* |
196 | | [color](../styles-and-themes/common-units/#color)-adornment-TextBox--warning | *none* | *none* |
197 | | [color](../styles-and-themes/common-units/#color)-passwordToggle-Input | $textColor-subtitle | $textColor-subtitle |
198 | | [color](../styles-and-themes/common-units/#color)-passwordToggle-TextBox | *none* | *none* |
199 | | [color](../styles-and-themes/common-units/#color)-passwordToggle-TextBox--focus | *none* | *none* |
200 | | [color](../styles-and-themes/common-units/#color)-passwordToggle-TextBox--hover | *none* | *none* |
201 | | [fontSize](../styles-and-themes/common-units/#size)-placeholder-TextBox--default | *none* | *none* |
202 | | [fontSize](../styles-and-themes/common-units/#size)-placeholder-TextBox--error | *none* | *none* |
203 | | [fontSize](../styles-and-themes/common-units/#size)-placeholder-TextBox--success | *none* | *none* |
204 | | [fontSize](../styles-and-themes/common-units/#size)-placeholder-TextBox--warning | *none* | *none* |
205 | | [fontSize](../styles-and-themes/common-units/#size)-TextBox--default | *none* | *none* |
206 | | [fontSize](../styles-and-themes/common-units/#size)-TextBox--error | *none* | *none* |
207 | | [fontSize](../styles-and-themes/common-units/#size)-TextBox--success | *none* | *none* |
208 | | [fontSize](../styles-and-themes/common-units/#size)-TextBox--warning | *none* | *none* |
209 | | [gap](../styles-and-themes/common-units/#size)-adornment-Input | $space-2 | $space-2 |
210 | | [gap](../styles-and-themes/common-units/#size)-adornment-TextBox | *none* | *none* |
211 | | [minHeight](../styles-and-themes/common-units/#size)-Input | 39px | 39px |
212 | | [outlineColor](../styles-and-themes/common-units/#color)-Input--focus | $outlineColor--focus | $outlineColor--focus |
213 | | [outlineColor](../styles-and-themes/common-units/#color)-TextBox--default--focus | *none* | *none* |
214 | | [outlineColor](../styles-and-themes/common-units/#color)-TextBox--error--focus | *none* | *none* |
215 | | [outlineColor](../styles-and-themes/common-units/#color)-TextBox--success--focus | *none* | *none* |
216 | | [outlineColor](../styles-and-themes/common-units/#color)-TextBox--warning--focus | *none* | *none* |
217 | | [outlineOffset](../styles-and-themes/common-units/#size)-Input--focus | $outlineOffset--focus | $outlineOffset--focus |
218 | | [outlineOffset](../styles-and-themes/common-units/#size)-TextBox--default--focus | *none* | *none* |
219 | | [outlineOffset](../styles-and-themes/common-units/#size)-TextBox--error--focus | *none* | *none* |
220 | | [outlineOffset](../styles-and-themes/common-units/#size)-TextBox--success--focus | *none* | *none* |
221 | | [outlineOffset](../styles-and-themes/common-units/#size)-TextBox--warning--focus | *none* | *none* |
222 | | [outlineStyle](../styles-and-themes/common-units/#border)-Input--focus | $outlineStyle--focus | $outlineStyle--focus |
223 | | [outlineStyle](../styles-and-themes/common-units/#border)-TextBox--default--focus | *none* | *none* |
224 | | [outlineStyle](../styles-and-themes/common-units/#border)-TextBox--error--focus | *none* | *none* |
225 | | [outlineStyle](../styles-and-themes/common-units/#border)-TextBox--success--focus | *none* | *none* |
226 | | [outlineStyle](../styles-and-themes/common-units/#border)-TextBox--warning--focus | *none* | *none* |
227 | | [outlineWidth](../styles-and-themes/common-units/#size)-Input--focus | $outlineWidth--focus | $outlineWidth--focus |
228 | | [outlineWidth](../styles-and-themes/common-units/#size)-TextBox--default--focus | *none* | *none* |
229 | | [outlineWidth](../styles-and-themes/common-units/#size)-TextBox--error--focus | *none* | *none* |
230 | | [outlineWidth](../styles-and-themes/common-units/#size)-TextBox--success--focus | *none* | *none* |
231 | | [outlineWidth](../styles-and-themes/common-units/#size)-TextBox--warning--focus | *none* | *none* |
232 | | [padding](../styles-and-themes/common-units/#size)-TextBox | *none* | *none* |
233 | | [paddingBottom](../styles-and-themes/common-units/#size)-TextBox | *none* | *none* |
234 | | [paddingHorizontal](../styles-and-themes/common-units/#size)-TextBox | $space-2 | $space-2 |
235 | | [paddingLeft](../styles-and-themes/common-units/#size)-passwordToggle-TextBox | *none* | *none* |
236 | | [paddingLeft](../styles-and-themes/common-units/#size)-TextBox | *none* | *none* |
237 | | [paddingRight](../styles-and-themes/common-units/#size)-passwordToggle-TextBox | *none* | *none* |
238 | | [paddingRight](../styles-and-themes/common-units/#size)-TextBox | *none* | *none* |
239 | | [paddingTop](../styles-and-themes/common-units/#size)-TextBox | *none* | *none* |
240 | | [paddingVertical](../styles-and-themes/common-units/#size)-TextBox | $space-2 | $space-2 |
241 | | [textColor](../styles-and-themes/common-units/#color)-Input | $textColor-primary | $textColor-primary |
242 | | [textColor](../styles-and-themes/common-units/#color)-Input--disabled | $textColor--disabled | $textColor--disabled |
243 | | [textColor](../styles-and-themes/common-units/#color)-placeholder-Input | $textColor-subtitle | $textColor-subtitle |
244 | | [textColor](../styles-and-themes/common-units/#color)-placeholder-TextBox--default | *none* | *none* |
245 | | [textColor](../styles-and-themes/common-units/#color)-placeholder-TextBox--error | *none* | *none* |
246 | | [textColor](../styles-and-themes/common-units/#color)-placeholder-TextBox--success | *none* | *none* |
247 | | [textColor](../styles-and-themes/common-units/#color)-placeholder-TextBox--warning | *none* | *none* |
248 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--default | *none* | *none* |
249 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--default--focus | *none* | *none* |
250 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--default--hover | *none* | *none* |
251 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--disabled | *none* | *none* |
252 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--error | *none* | *none* |
253 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--error--focus | *none* | *none* |
254 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--error--hover | *none* | *none* |
255 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--success | *none* | *none* |
256 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--success--focus | *none* | *none* |
257 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--success--hover | *none* | *none* |
258 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--warning | *none* | *none* |
259 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--warning--focus | *none* | *none* |
260 | | [textColor](../styles-and-themes/common-units/#color)-TextBox--warning--hover | *none* | *none* |
261 |
```
--------------------------------------------------------------------------------
/docs/public/resources/files/tutorials/p2do/api.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ApiInterceptorDefinition } from "xmlui";
2 |
3 | const today = new Date();
4 | today.setHours(23, 59, 59, 999);
5 | const tomorrow = new Date(today);
6 | tomorrow.setDate(tomorrow.getDate() + 1);
7 | const twoDaysLater = new Date(today);
8 | twoDaysLater.setDate(twoDaysLater.getDate() + 2);
9 | const threeDaysLater = new Date(today);
10 | threeDaysLater.setDate(threeDaysLater.getDate() + 3);
11 | const weekLater = new Date(today);
12 | weekLater.setDate(weekLater.getDate() + 7);
13 | const monthLater = new Date(today);
14 | monthLater.setMonth(monthLater.getMonth() + 1);
15 | const yesterday = new Date(today);
16 | yesterday.setDate(yesterday.getDate() - 1);
17 | const twoDaysEarlier = new Date(today);
18 | twoDaysEarlier.setDate(twoDaysEarlier.getDate() - 2);
19 | const threeDaysEarlier = new Date(today);
20 | threeDaysEarlier.setDate(threeDaysEarlier.getDate() - 3);
21 | const weekEarlier = new Date(today);
22 | weekEarlier.setDate(weekEarlier.getDate() - 7);
23 | const monthEarlier = new Date(today);
24 | monthEarlier.setMonth(monthEarlier.getMonth() - 1);
25 |
26 | const mock: ApiInterceptorDefinition = {
27 | type: "db",
28 | config: {
29 | database: "xmluiTodoApp",
30 | version: 2,
31 | },
32 | apiUrl: "/api",
33 | initialData: {
34 | users: [
35 | {
36 | id: 1,
37 | displayName: "John Smith",
38 | avatarUrl: "resource:avatar-user",
39 | },
40 | {
41 | id: 2,
42 | displayName: "Jane Smith",
43 | avatarUrl: "resource:avatar-user",
44 | },
45 | ],
46 | topics: [
47 | {
48 | id: 1,
49 | name: "Work",
50 | color: "#3455eb",
51 | },
52 | {
53 | id: 2,
54 | name: "Personal",
55 | color: "#ebc334",
56 | },
57 | {
58 | id: 3,
59 | name: "Shopping",
60 | color: "#2a9660",
61 | },
62 | {
63 | id: 4,
64 | name: "Health",
65 | color: "#ff5733",
66 | },
67 | {
68 | id: 5,
69 | name: "Fitness",
70 | color: "#34eb67",
71 | },
72 | {
73 | id: 6,
74 | name: "Education",
75 | color: "#a934eb",
76 | },
77 | {
78 | id: 7,
79 | name: "Hobbies",
80 | color: "#eb34b3",
81 | },
82 | {
83 | id: 8,
84 | name: "Travel",
85 | color: "#33ff57",
86 | },
87 | ],
88 | tasks: [
89 | {
90 | id: 1,
91 | title: "Buy vegetables",
92 | topicId: 3,
93 | description: "Buy assorted vegetables from the market",
94 | createdDate: twoDaysEarlier.toISOString(),
95 | dueDate: today.toISOString(),
96 | completed: false,
97 | completedDate: null,
98 | assignedTo: 1,
99 | },
100 | {
101 | id: 2,
102 | title: "Finish presentation",
103 | topicId: 1,
104 | description: "Finalize the presentation slides for the client meeting",
105 | createdDate: threeDaysEarlier.toISOString(),
106 | dueDate: today.toISOString(),
107 | completed: true,
108 | completedDate: today.toISOString(),
109 | assignedTo: 1,
110 | },
111 | {
112 | id: 3,
113 | title: "Schedule dentist appointment",
114 | topicId: 4,
115 | description: "Book an appointment with the dentist for a check-up",
116 | createdDate: threeDaysEarlier.toISOString(),
117 | dueDate: null,
118 | completed: true,
119 | completedDate: yesterday.toISOString(),
120 | assignedTo: 1,
121 | },
122 | {
123 | id: 4,
124 | title: "Run 5k",
125 | topicId: 5,
126 | description: "Complete a 5-kilometer run in the park",
127 | createdDate: today.toISOString(),
128 | dueDate: twoDaysLater.toISOString(),
129 | completed: false,
130 | completedDate: null,
131 | assignedTo: 1,
132 | },
133 | {
134 | id: 5,
135 | title: "Read chapter 3",
136 | topicId: 6,
137 | description: "Read and summarize chapter 3 of the textbook",
138 | createdDate: threeDaysEarlier.toISOString(),
139 | dueDate: weekLater.toISOString(),
140 | completed: false,
141 | completedDate: null,
142 | assignedTo: 1,
143 | },
144 | {
145 | id: 6,
146 | title: "Painting session",
147 | topicId: 7,
148 | description: "Spend an hour painting a landscape",
149 | createdDate: yesterday.toISOString(),
150 | dueDate: twoDaysLater.toISOString(),
151 | completed: false,
152 | completedDate: null,
153 | assignedTo: 1,
154 | },
155 | {
156 | id: 7,
157 | title: "Plan weekend trip",
158 | topicId: 8,
159 | description: "Research and plan activities for the upcoming weekend trip",
160 | createdDate: today.toISOString(),
161 | dueDate: null,
162 | completed: false,
163 | completedDate: null,
164 | assignedTo: 1,
165 | },
166 | {
167 | id: 8,
168 | title: "Yoga session",
169 | topicId: 5,
170 | description: "Attend a yoga class for relaxation",
171 | createdDate: yesterday.toISOString(),
172 | dueDate: tomorrow.toISOString(),
173 | completed: false,
174 | completedDate: null,
175 | assignedTo: 1,
176 | },
177 | {
178 | id: 9,
179 | title: "Complete online course",
180 | topicId: 6,
181 | description: "Finish the remaining modules of the online course",
182 | createdDate: threeDaysEarlier.toISOString(),
183 | dueDate: monthLater.toISOString(),
184 | completed: false,
185 | completedDate: null,
186 | assignedTo: 1,
187 | },
188 | {
189 | id: 10,
190 | title: "Buy birthday gift",
191 | topicId: 3,
192 | description: "Purchase a gift for a friend's birthday",
193 | createdDate: twoDaysEarlier.toISOString(),
194 | dueDate: monthLater.toISOString(),
195 | completed: false,
196 | completedDate: null,
197 | assignedTo: 1,
198 | },
199 | {
200 | id: 11,
201 | title: "Submit expense report",
202 | topicId: 1,
203 | description: "Compile and submit the monthly expense report",
204 | createdDate: threeDaysEarlier.toISOString(),
205 | dueDate: yesterday.toISOString(),
206 | completed: false,
207 | completedDate: null,
208 | assignedTo: 1,
209 | },
210 | {
211 | id: 12,
212 | title: "Visit gym",
213 | topicId: 5,
214 | description: "Hit the gym for a strength training session",
215 | createdDate: monthEarlier.toISOString(),
216 | dueDate: null,
217 | completed: false,
218 | completedDate: null,
219 | assignedTo: 1,
220 | },
221 | {
222 | id: 13,
223 | title: "Study for exam",
224 | topicId: 6,
225 | description: "Review notes and prepare for the upcoming exam",
226 | createdDate: monthEarlier.toISOString(),
227 | dueDate: yesterday.toISOString(),
228 | completed: false,
229 | completedDate: null,
230 | assignedTo: 1,
231 | },
232 | {
233 | id: 14,
234 | title: "Cook dinner",
235 | topicId: 2,
236 | description: "Prepare a healthy dinner at home",
237 | createdDate: today.toISOString(),
238 | dueDate: today.toISOString(),
239 | completed: true,
240 | completedDate: today.toISOString(),
241 | assignedTo: 1,
242 | },
243 | {
244 | id: 15,
245 | title: "Buy groceries",
246 | topicId: 3,
247 | description: "Stock up on essential groceries for the week",
248 | createdDate: yesterday.toISOString(),
249 | dueDate: twoDaysLater.toISOString(),
250 | completed: false,
251 | completedDate: null,
252 | assignedTo: 1,
253 | },
254 | {
255 | id: 16,
256 | title: "Write blog post",
257 | topicId: 1,
258 | description: "Draft a blog post on industry trends",
259 | createdDate: weekEarlier.toISOString(),
260 | dueDate: null,
261 | completed: false,
262 | completedDate: null,
263 | assignedTo: 1,
264 | },
265 | {
266 | id: 17,
267 | title: "Go for a walk",
268 | topicId: 5,
269 | description: "Take a brisk walk in the neighborhood for exercise",
270 | createdDate: yesterday.toISOString(),
271 | dueDate: null,
272 | completed: false,
273 | completedDate: null,
274 | assignedTo: 1,
275 | },
276 | {
277 | id: 18,
278 | title: "Research new hobby",
279 | topicId: 7,
280 | description: "Explore potential new hobbies and interests",
281 | createdDate: monthEarlier.toISOString(),
282 | dueDate: null,
283 | completed: true,
284 | completedDate: weekEarlier.toISOString(),
285 | assignedTo: 1,
286 | },
287 | {
288 | id: 19,
289 | title: "Book flight tickets",
290 | topicId: 8,
291 | description: "Book flight tickets for the upcoming vacation",
292 | createdDate: weekEarlier.toISOString(),
293 | dueDate: today.toISOString(),
294 | completed: true,
295 | completedDate: today.toISOString(),
296 | assignedTo: 1,
297 | },
298 | {
299 | id: 20,
300 | title: "Review health goals",
301 | topicId: 4,
302 | description: "Review and adjust personal health and fitness goals",
303 | createdDate: threeDaysEarlier.toISOString(),
304 | dueDate: weekLater.toISOString(),
305 | completed: false,
306 | completedDate: null,
307 | assignedTo: 1,
308 | },
309 | {
310 | id: 21,
311 | title: "Attend networking event",
312 | topicId: 1,
313 | description: "Attend a networking event to expand professional contacts",
314 | createdDate: twoDaysEarlier.toISOString(),
315 | dueDate: yesterday.toISOString(),
316 | completed: true,
317 | completedDate: today.toISOString(),
318 | assignedTo: 1,
319 | },
320 | {
321 | id: 22,
322 | title: "Practice meditation",
323 | topicId: 2,
324 | description: "Dedicate time to practice mindfulness meditation",
325 | createdDate: weekEarlier.toISOString(),
326 | dueDate: null,
327 | completed: false,
328 | completedDate: null,
329 | assignedTo: 1,
330 | },
331 | {
332 | id: 23,
333 | title: "Buy new running shoes",
334 | topicId: 5,
335 | description: "Purchase a new pair of running shoes for better support",
336 | createdDate: yesterday.toISOString(),
337 | dueDate: weekLater.toISOString(),
338 | completed: false,
339 | completedDate: null,
340 | assignedTo: 1,
341 | },
342 | {
343 | id: 24,
344 | title: "Learn a new recipe",
345 | topicId: 2,
346 | description: "Experiment with a new recipe for dinner",
347 | createdDate: monthEarlier.toISOString(),
348 | dueDate: null,
349 | completed: true,
350 | completedDate: threeDaysEarlier.toISOString(),
351 | assignedTo: 1,
352 | },
353 | {
354 | id: 25,
355 | title: "Explore local art gallery",
356 | topicId: 7,
357 | description: "Visit a local art gallery to appreciate artworks",
358 | createdDate: threeDaysEarlier.toISOString(),
359 | dueDate: null,
360 | completed: false,
361 | completedDate: null,
362 | assignedTo: 1,
363 | },
364 | {
365 | id: 26,
366 | title: "Update resume",
367 | topicId: 1,
368 | description: "Review and update the resume with recent achievements",
369 | createdDate: twoDaysEarlier.toISOString(),
370 | dueDate: twoDaysLater.toISOString(),
371 | completed: false,
372 | completedDate: null,
373 | assignedTo: 1,
374 | },
375 | {
376 | id: 27,
377 | title: "Organize closet",
378 | topicId: 2,
379 | description: "Declutter and organize the wardrobe",
380 | createdDate: "2024-04-19T00:00:00Z",
381 | dueDate: "2024-06-20T23:59:59Z",
382 | completed: false,
383 | completedDate: null,
384 | assignedTo: 1,
385 | },
386 | {
387 | id: 28,
388 | title: "Plan outdoor picnic",
389 | topicId: 7,
390 | description: "Plan a picnic outing with friends or family",
391 | createdDate: "2024-04-19T00:00:00Z",
392 | dueDate: "2024-06-22T23:59:59Z",
393 | completed: false,
394 | completedDate: null,
395 | assignedTo: 1,
396 | },
397 | {
398 | id: 29,
399 | title: "Research new travel destination",
400 | topicId: 8,
401 | description: "Research and plan a trip to a new travel destination",
402 | createdDate: "2024-04-19T00:00:00Z",
403 | dueDate: "2024-06-25T23:59:59Z",
404 | completed: false,
405 | completedDate: null,
406 | assignedTo: 1,
407 | },
408 | {
409 | id: 30,
410 | title: "Update health tracker",
411 | topicId: 4,
412 | description: "Update the health tracker app with recent activities",
413 | createdDate: "2024-04-19T00:00:00Z",
414 | dueDate: "2024-06-28T23:59:59Z",
415 | completed: false,
416 | completedDate: null,
417 | assignedTo: 1,
418 | },
419 | ],
420 | },
421 |
422 | helpers: {
423 | Assertions: {
424 | ensureTask: `(taskId) => {
425 | const foundTask = $db.$tasks.byId(taskId);
426 | if (!foundTask) {
427 | throw Errors.NotFound404("Task id:" + taskId + " not found");
428 | };
429 | return foundTask;
430 | }`,
431 | },
432 | getSection: `(task) => {
433 | if (task.completed) return "Completed";
434 | if (!task.dueDate) return "NoDueDate";
435 | if (isToday(task.dueDate)) return "Today";
436 | return getDate(task.dueDate) < getDate()
437 | ? "Overdue"
438 | : "Upcoming";
439 | }`,
440 | },
441 |
442 | auth: {
443 | defaultLoggedInUser: {
444 | id: 1,
445 | },
446 | },
447 |
448 | operations: {
449 | login: {
450 | url: "/login",
451 | method: "post",
452 | queryParamTypes: {
453 | userId: "integer",
454 | },
455 | handler: "$authService.login({id: $queryParams.userId})",
456 | },
457 |
458 | loadMe: {
459 | url: "/users/me",
460 | method: "get",
461 | handler: "$db.$users.byId($loggedInUser.id)",
462 | },
463 |
464 | "users-list": {
465 | url: "/users",
466 | method: "get",
467 | responseShape: "user[]",
468 | handler: `$db.$users.toArray()`,
469 | },
470 |
471 | "task-list": {
472 | url: "/tasks",
473 | method: "get",
474 | responseShape: "task[]",
475 | handler: `$db.$tasks.toArray();`,
476 | },
477 |
478 | "task-list-recent": {
479 | url: "/tasks-recent",
480 | method: "get",
481 | responseShape: "task[]",
482 | handler: `$db.$tasks.orderBy(t => t.id, true).take(5).toArray();`,
483 | },
484 |
485 | "task-list-overdue": {
486 | url: "/tasks/overdue",
487 | method: "get",
488 | responseShape: "task[]",
489 | handler: `$db.$tasks.whereAsArray(t => getSection(t) === 'Overdue');`,
490 | },
491 |
492 | "task-list-today": {
493 | url: "/tasks/today",
494 | method: "get",
495 | responseShape: "task[]",
496 | handler: `$db.$tasks.whereAsArray(t =>
497 | !t.completed && t.dueDate &&
498 | $env.getDate(t.dueDate).setHours(0,0,0,0) === $env.getDate().setHours(0,0,0,0)
499 | );`,
500 | },
501 |
502 | "task-list-upcoming": {
503 | url: "/tasks/upcoming",
504 | method: "get",
505 | responseShape: "task[]",
506 | handler: `$db.$tasks.whereAsArray(t => getSection(t) === 'Upcoming');`,
507 | },
508 |
509 | "task-list-completed": {
510 | url: "/tasks/completed",
511 | method: "get",
512 | responseShape: "task[]",
513 | handler: `$db.$tasks.whereAsArray(t => getSection(t) === 'Completed');`,
514 | },
515 |
516 | "task-counts": {
517 | url: "/taskcounts",
518 | method: "get",
519 | handler: `return {
520 | all: $db.$tasks.toArray().length,
521 | overdue: $db.$tasks.whereAsArray(t => getSection(t) === 'Overdue').length,
522 | today: $db.$tasks.whereAsArray(t =>
523 | !t.completed && t.dueDate &&
524 | $env.getDate(t.dueDate).setHours(0,0,0,0) === $env.getDate().setHours(0,0,0,0)
525 | ).length,
526 | upcoming: $db.$tasks.whereAsArray(t => getSection(t) === 'Upcoming').length,
527 | completed: $db.$tasks.whereAsArray(t => getSection(t) === 'Completed').length
528 | }`,
529 | },
530 |
531 | // --- Retrieve a particular task data
532 | "tasks-read": {
533 | url: "/tasks/:taskId",
534 | method: "get",
535 | responseShape: "task",
536 | pathParamTypes: {
537 | taskId: "integer",
538 | },
539 | handler: "Assertions.ensureTask($pathParams.taskId)",
540 | },
541 |
542 | "tasks-delete": {
543 | url: "/tasks/:taskId",
544 | method: "delete",
545 | pathParamTypes: {
546 | taskId: "integer",
547 | },
548 | handler: `
549 | const taskId = $pathParams.taskId;
550 | const task = Assertions.ensureTask(taskId);
551 | if (task) {
552 | $db.$tasks.deleteById(taskId);
553 | }
554 | `,
555 | },
556 |
557 | "tasks-edit": {
558 | url: "/tasks/:taskId",
559 | pathParamTypes: {
560 | taskId: "integer",
561 | },
562 | method: "put",
563 | handler: `
564 | // --- Get task attributes
565 | const taskId = $pathParams.taskId;
566 | const {
567 | title,
568 | topicId,
569 | description,
570 | createdDate,
571 | dueDate,
572 | assignedTo,
573 | completed,
574 | completedDate
575 | } = $requestBody;
576 |
577 | // --- Update the task
578 | const originalTask = Assertions.ensureTask(taskId);
579 | $db.$tasks.update({
580 | ...originalTask,
581 | title,
582 | topicId,
583 | description,
584 | dueDate,
585 | assignedTo,
586 | completed,
587 | completedDate
588 | });
589 |
590 | // --- Done.
591 | return $db.$tasks.byId(taskId);
592 | `,
593 | },
594 |
595 | "tasks-create": {
596 | url: "/tasks",
597 | method: "post",
598 | successStatusCode: 201,
599 | responseShape: "task?",
600 | handler: `
601 | const { title, topicId, description, dueDate } = $requestBody;
602 | if (title === undefined || title.length === 0) {
603 | throw Error('Task without title is not permitted')
604 | }
605 |
606 | // --- Create the task with defaults
607 | const task = $db.$tasks.insert({
608 | title,
609 | topicId: topicId ? Number(topicId) : null,
610 | description,
611 | createdDate: $env.getDate().toISOString(),
612 | dueDate,
613 | assignedTo: $loggedInUser.id,
614 | });
615 |
616 | // --- Read back task data to create the DTO
617 | const taskDto = $db.$tasks.byId(task.id);
618 | return taskDto;
619 | `,
620 | },
621 |
622 | "tasks-summary": {
623 | url: "/charts/tasks-summary",
624 | method: "get",
625 | handler: `
626 | delay(1000);
627 | return [
628 | { "id": "Overdue", "value": 12 },
629 | { "id": "Today", "value": 2 },
630 | { "id": "Upcoming", "value": 5 },
631 | { "id": "Completed", "value": 7 }
632 | ]
633 | `,
634 | },
635 |
636 | "topics-list": {
637 | url: "/topics",
638 | method: "get",
639 | handler: `$db.$topics.toArray()`,
640 | },
641 | },
642 | };
643 |
644 | export default mock;
645 |
```
--------------------------------------------------------------------------------
/xmlui/scripts/e2e-test-summary.js:
--------------------------------------------------------------------------------
```javascript
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * XMLUI E2E Test Summary Generator
5 | *
6 | * This script analyzes all XMLUI components and their corresponding end-to-end tests,
7 | * providing a comprehensive summary table showing test coverage and results.
8 | *
9 | * Features:
10 | * - Extracts XMLUI components from collectedComponentMetadata.ts
11 | * - Maps components to their e2e test files
12 | * - Runs all e2e tests and collects results
13 | * - Displays a summary table with test statistics
14 | * - Shows test coverage percentage
15 | *
16 | * Usage:
17 | * npm run test:e2e-summary # Run all tests and show results
18 | * npm run test:e2e-summary -- --dry-run # Show coverage without running tests
19 | *
20 | * Options:
21 | * --dry-run, --coverage-only Show test coverage analysis without running tests
22 | * --help Show this help message
23 | */
24 |
25 | const fs = require('fs');
26 | const path = require('path');
27 | const { execSync } = require('child_process');
28 |
29 | function showHelp() {
30 | console.log(`
31 | XMLUI E2E Test Summary Generator
32 |
33 | This script analyzes XMLUI components and their end-to-end test coverage.
34 |
35 | Usage:
36 | node scripts/e2e-test-summary.js [options]
37 |
38 | Options:
39 | --dry-run, --coverage-only Show test coverage analysis without running tests
40 | --help Show this help message
41 |
42 | Examples:
43 | npm run test:e2e-summary # Run all tests and show results
44 | npm run test:e2e-summary -- --dry-run # Show coverage only
45 | node scripts/e2e-test-summary.js --help # Show this help
46 |
47 | The script will:
48 | 1. Extract all XMLUI components from metadata
49 | 2. Map components to their e2e test files
50 | 3. Run playwright e2e tests (unless --dry-run)
51 | 4. Display a comprehensive summary table
52 | `);
53 | }
54 |
55 | /**
56 | * Extract component names from collectedComponentMetadata.ts
57 | */
58 | function getXMLUIComponents() {
59 | const metadataPath = path.join(__dirname, '../src/components/collectedComponentMetadata.ts');
60 | const metadataContent = fs.readFileSync(metadataPath, 'utf8');
61 |
62 | // Extract the export object to get component names
63 | const exportMatch = metadataContent.match(/export const collectedComponentMetadata[^{]*{([^}]+)}/s);
64 | if (!exportMatch) {
65 | throw new Error('Could not parse collectedComponentMetadata.ts');
66 | }
67 |
68 | const exportContent = exportMatch[1];
69 | const componentNames = [];
70 |
71 | // Extract component names (keys from the export object)
72 | const lines = exportContent.split('\n');
73 | for (const line of lines) {
74 | const trimmed = line.trim();
75 | if (trimmed && !trimmed.startsWith('//') && trimmed.includes(':')) {
76 | const match = trimmed.match(/^\s*([A-Za-z][A-Za-z0-9]*)\s*:/);
77 | if (match) {
78 | const componentName = match[1];
79 | // Filter out HTML components (lowercase names) and focus on XMLUI components
80 | if (componentName[0] === componentName[0].toUpperCase() &&
81 | !componentName.startsWith('Html') &&
82 | componentName !== 'CODE' &&
83 | componentName !== 'EM') {
84 | componentNames.push(componentName);
85 | }
86 | }
87 | }
88 | }
89 |
90 | return componentNames.sort();
91 | }
92 |
93 | /**
94 | * Get list of component test files in the xmlui/src/components directories
95 | */
96 | function getE2ETestFiles() {
97 | const componentsDir = path.join(__dirname, '../src/components');
98 |
99 | if (!fs.existsSync(componentsDir)) {
100 | return [];
101 | }
102 |
103 | const testFiles = [];
104 |
105 | // Get all subdirectories in components
106 | const componentDirs = fs.readdirSync(componentsDir, { withFileTypes: true })
107 | .filter(dirent => dirent.isDirectory())
108 | .map(dirent => dirent.name);
109 |
110 | // Check each component directory for spec files
111 | for (const componentDir of componentDirs) {
112 | const componentPath = path.join(componentsDir, componentDir);
113 | const files = fs.readdirSync(componentPath);
114 |
115 | const specFiles = files.filter(file => file.endsWith('.spec.ts'));
116 | for (const specFile of specFiles) {
117 | testFiles.push({
118 | componentName: componentDir,
119 | fileName: path.basename(specFile, '.spec.ts'),
120 | fullPath: path.join(componentPath, specFile)
121 | });
122 | }
123 | }
124 |
125 | return testFiles;
126 | }
127 |
128 | /**
129 | * Map component names to their corresponding test files
130 | */
131 | function mapComponentsToTestFiles(components, testFiles) {
132 | const componentTestMap = new Map();
133 |
134 | for (const component of components) {
135 | // Find test files for this component
136 | const matchingTests = testFiles.filter(testFile => {
137 | // Direct component directory match
138 | if (testFile.componentName === component) {
139 | return true;
140 | }
141 |
142 | // Check various naming patterns
143 | const componentLower = component.toLowerCase();
144 | const testComponentLower = testFile.componentName.toLowerCase();
145 |
146 | // Exact match (case insensitive)
147 | if (testComponentLower === componentLower) {
148 | return true;
149 | }
150 |
151 | // Check if test file name matches component
152 | const testFileNameLower = testFile.fileName.toLowerCase();
153 | if (testFileNameLower === componentLower) {
154 | return true;
155 | }
156 |
157 | // Handle compound components like CHStack, CVStack -> Stack
158 | if (componentLower.endsWith('stack') && testComponentLower === 'stack') {
159 | return true;
160 | }
161 |
162 | // Handle H1, H2, etc. -> Heading
163 | if (componentLower.match(/^h[1-6]$/) && testComponentLower === 'heading') {
164 | return true;
165 | }
166 |
167 | return false;
168 | });
169 |
170 | componentTestMap.set(component, matchingTests);
171 | }
172 |
173 | return componentTestMap;
174 | }
175 |
176 | /**
177 | * Run playwright tests and get JSON results
178 | */
179 | function runPlaywrightTests() {
180 | const workingDir = __dirname; // xmlui directory
181 |
182 | try {
183 | console.log('Running e2e tests...');
184 | console.log(`Working directory: ${workingDir}`);
185 |
186 | // Run the npm script for e2e tests
187 | const result = execSync(
188 | `npm run test:e2e-non-smoke -- --reporter=json`,
189 | {
190 | cwd: workingDir,
191 | encoding: 'utf8',
192 | maxBuffer: 10 * 1024 * 1024 // 10MB buffer for large output
193 | }
194 | );
195 |
196 | // Extract JSON from npm output (skip npm prefix lines)
197 | const lines = result.split('\n');
198 | const jsonStartIndex = lines.findIndex(line => line.trim().startsWith('{'));
199 | if (jsonStartIndex === -1) {
200 | throw new Error('No JSON output found in test results');
201 | }
202 | const jsonOutput = lines.slice(jsonStartIndex).join('\n');
203 |
204 | return JSON.parse(jsonOutput);
205 | } catch (error) {
206 | // Tests may fail, but we still want to process results if available
207 | console.log('Tests completed (some may have failed)');
208 |
209 | // Try to parse the output even if the command failed
210 | if (error.stdout) {
211 | try {
212 | // Extract JSON from npm output (skip npm prefix lines)
213 | const lines = error.stdout.split('\n');
214 | const jsonStartIndex = lines.findIndex(line => line.trim().startsWith('{'));
215 | if (jsonStartIndex === -1) {
216 | console.error('No JSON output found in error output');
217 | return null;
218 | }
219 | const jsonOutput = lines.slice(jsonStartIndex).join('\n');
220 |
221 | return JSON.parse(jsonOutput);
222 | } catch (parseError) {
223 | console.error('Could not parse test results JSON:', parseError.message);
224 | }
225 | }
226 |
227 | console.log('No test results available - showing component coverage only');
228 | }
229 |
230 | return null;
231 | }
232 |
233 | /**
234 | * Parse test results and group by component
235 | */
236 | function parseTestResults(results) {
237 | if (!results || !results.suites) {
238 | return new Map();
239 | }
240 |
241 | const componentResults = new Map();
242 |
243 | function processSpec(spec, filePath) {
244 | if (!spec.tests) return;
245 |
246 | // Extract component name from file path
247 | const pathMatch = filePath.match(/\/src\/components\/([^\/]+)\//);
248 | const componentName = pathMatch ? pathMatch[1] : path.basename(path.dirname(filePath));
249 |
250 | if (!componentResults.has(componentName)) {
251 | componentResults.set(componentName, {
252 | passed: 0,
253 | failed: 0,
254 | skipped: 0,
255 | total: 0
256 | });
257 | }
258 |
259 | const componentResult = componentResults.get(componentName);
260 |
261 | for (const test of spec.tests) {
262 | for (const result of test.results || []) {
263 | componentResult.total++;
264 |
265 | switch (result.status) {
266 | case 'passed':
267 | componentResult.passed++;
268 | break;
269 | case 'failed':
270 | componentResult.failed++;
271 | break;
272 | case 'skipped':
273 | componentResult.skipped++;
274 | break;
275 | default:
276 | componentResult.skipped++;
277 | }
278 | }
279 | }
280 | }
281 |
282 | function processSuite(suite, inheritedFilePath = '') {
283 | const currentFilePath = suite.file || inheritedFilePath;
284 |
285 | if (suite.specs) {
286 | for (const spec of suite.specs) {
287 | processSpec(spec, currentFilePath);
288 | }
289 | }
290 |
291 | if (suite.suites) {
292 | for (const subSuite of suite.suites) {
293 | processSuite(subSuite, currentFilePath);
294 | }
295 | }
296 | }
297 |
298 | for (const suite of results.suites) {
299 | processSuite(suite);
300 | }
301 |
302 | return componentResults;
303 | }
304 |
305 | /**
306 | * Create and display the summary table
307 | */
308 | function displaySummary(components, componentTestMap, testResults) {
309 | console.log('\n=== XMLUI Components E2E Test Summary ===\n');
310 |
311 | // Table header
312 | console.log('┌───────────────────────┬───────────┬────────┬────────┬─────────┬─────────────────┐');
313 | console.log('│ Component Name │ Tests Run │ Passed │ Failed │ Skipped │ Status │');
314 | console.log('├───────────────────────┼───────────┼────────┼────────┼─────────┼─────────────────┤');
315 |
316 | let totalComponents = 0;
317 | let componentsWithTests = 0;
318 | let totalTests = 0;
319 | let totalPassed = 0;
320 | let totalFailed = 0;
321 | let totalSkipped = 0;
322 |
323 | for (const component of components) {
324 | totalComponents++;
325 | const testFiles = componentTestMap.get(component);
326 |
327 | if (!testFiles || testFiles.length === 0) {
328 | // No e2e tests
329 | console.log(`│ ${component.padEnd(21)} │ ${'-'.padStart(9)} │ ${'-'.padStart(6)} │ ${'-'.padStart(6)} │ ${'-'.padStart(7)} │ No E2E Tests │`);
330 | } else {
331 | componentsWithTests++;
332 | const results = testResults ? testResults.get(component) : null;
333 |
334 | if (!results) {
335 | // Test files exist but no results (dry-run mode or test execution failed)
336 | const status = testResults === null ? 'Has E2E Tests' : 'Not Run/Failed';
337 | console.log(`│ ${component.padEnd(21)} │ ${'-'.padStart(9)} │ ${'-'.padStart(6)} │ ${'-'.padStart(6)} │ ${'-'.padStart(7)} │ ${status.padEnd(15)} │`);
338 | } else {
339 | totalTests += results.total;
340 | totalPassed += results.passed;
341 | totalFailed += results.failed;
342 | totalSkipped += results.skipped;
343 |
344 | const status = results.failed > 0 ? 'Some Failed' :
345 | results.total === 0 ? 'No Tests' : 'All Passed';
346 |
347 | console.log(`│ ${component.padEnd(21)} │ ${results.total.toString().padStart(9)} │ ${results.passed.toString().padStart(6)} │ ${results.failed.toString().padStart(6)} │ ${results.skipped.toString().padStart(7)} │ ${status.padEnd(15)} │`);
348 | }
349 | }
350 | }
351 |
352 | console.log('└───────────────────────┴───────────┴────────┴────────┴─────────┴─────────────────┘');
353 |
354 | // Summary statistics
355 | console.log('\n=== Summary Statistics ===');
356 | console.log(`Total XMLUI Components: ${totalComponents}`);
357 | console.log(`Components with E2E Tests: ${componentsWithTests}`);
358 | console.log(`Components without E2E Tests: ${totalComponents - componentsWithTests}`);
359 |
360 | if (testResults) {
361 | console.log(`Total E2E Tests Run: ${totalTests}`);
362 | console.log(`Total Passed: ${totalPassed}`);
363 | console.log(`Total Failed: ${totalFailed}`);
364 | console.log(`Total Skipped: ${totalSkipped}`);
365 |
366 | if (totalTests > 0) {
367 | const passRate = ((totalPassed / totalTests) * 100).toFixed(1);
368 | console.log(`Pass Rate: ${passRate}%`);
369 | }
370 | } else {
371 | console.log(`E2E tests were not run - showing test coverage only`);
372 | }
373 |
374 | console.log(`\nTest Coverage: ${((componentsWithTests / totalComponents) * 100).toFixed(1)}% of components have E2E tests`);
375 | }
376 |
377 | /**
378 | * Generate and save markdown summary to dev-docs/e2e-summary.md
379 | */
380 | function saveMarkdownSummary(components, componentTestMap, testResults) {
381 | const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
382 | const devDocsPath = path.join(__dirname, '..', 'dev-docs', 'e2e-summary.md');
383 |
384 | let markdown = `# XMLUI E2E Test Summary\n\n`;
385 | markdown += `*Generated on: ${timestamp} UTC*\n\n`;
386 |
387 | // Summary statistics
388 | let totalComponents = 0;
389 | let componentsWithTests = 0;
390 | let totalTests = 0;
391 | let totalPassed = 0;
392 | let totalFailed = 0;
393 | let totalSkipped = 0;
394 |
395 | for (const component of components) {
396 | totalComponents++;
397 | const testFiles = componentTestMap.get(component);
398 |
399 | if (testFiles && testFiles.length > 0) {
400 | componentsWithTests++;
401 | const results = testResults ? testResults.get(component) : null;
402 |
403 | if (results) {
404 | totalTests += results.total;
405 | totalPassed += results.passed;
406 | totalFailed += results.failed;
407 | totalSkipped += results.skipped;
408 | }
409 | }
410 | }
411 |
412 | markdown += `## Summary Statistics\n\n`;
413 | markdown += `- **Total XMLUI Components**: ${totalComponents}\n`;
414 | markdown += `- **Components with E2E Tests**: ${componentsWithTests}\n`;
415 | markdown += `- **Components without E2E Tests**: ${totalComponents - componentsWithTests}\n`;
416 | markdown += `- **Test Coverage**: ${((componentsWithTests / totalComponents) * 100).toFixed(1)}% of components have E2E tests\n\n`;
417 |
418 | if (testResults) {
419 | markdown += `- **Total E2E Tests Run**: ${totalTests}\n`;
420 | markdown += `- **Total Passed**: ${totalPassed}\n`;
421 | markdown += `- **Total Failed**: ${totalFailed}\n`;
422 | markdown += `- **Total Skipped**: ${totalSkipped}\n`;
423 |
424 | if (totalTests > 0) {
425 | const passRate = ((totalPassed / totalTests) * 100).toFixed(1);
426 | markdown += `- **Pass Rate**: ${passRate}%\n`;
427 | }
428 | markdown += `\n`;
429 | } else {
430 | markdown += `- **Note**: E2E tests were not run - showing test coverage only\n\n`;
431 | }
432 |
433 | // Component details table
434 | markdown += `## Component Test Details\n\n`;
435 | markdown += `| Component Name | Tests Run | Passed | Failed | Skipped | Status |\n`;
436 | markdown += `|----------------|-----------|--------|--------|---------|--------|\n`;
437 |
438 | for (const component of components) {
439 | const testFiles = componentTestMap.get(component);
440 |
441 | if (!testFiles || testFiles.length === 0) {
442 | markdown += `| ${component} | - | - | - | - | No E2E Tests |\n`;
443 | } else {
444 | const results = testResults ? testResults.get(component) : null;
445 |
446 | if (!results) {
447 | const status = testResults === null ? 'Has E2E Tests' : 'Not Run/Failed';
448 | markdown += `| ${component} | - | - | - | - | ${status} |\n`;
449 | } else {
450 | const status = results.failed > 0 ? 'Some Failed' :
451 | results.total === 0 ? 'No Tests' : 'All Passed';
452 |
453 | markdown += `| ${component} | ${results.total} | ${results.passed} | ${results.failed} | ${results.skipped} | ${status} |\n`;
454 | }
455 | }
456 | }
457 |
458 | // Write the markdown file
459 | try {
460 | fs.writeFileSync(devDocsPath, markdown, 'utf8');
461 | console.log(`\n✅ E2E test summary saved to: ${devDocsPath}`);
462 | } catch (error) {
463 | console.error(`❌ Failed to save markdown summary: ${error.message}`);
464 | }
465 | }
466 |
467 | /**
468 | * Main execution function
469 | */
470 | function main() {
471 | try {
472 | // Check for help option
473 | if (process.argv.includes('--help') || process.argv.includes('-h')) {
474 | showHelp();
475 | return;
476 | }
477 |
478 | console.log('XMLUI E2E Test Summary Generator');
479 | console.log('=================================\n');
480 |
481 | // Check for dry-run option
482 | const dryRun = process.argv.includes('--dry-run') || process.argv.includes('--coverage-only');
483 |
484 | if (dryRun) {
485 | console.log('Running in dry-run mode (coverage analysis only)\n');
486 | }
487 |
488 | // Get XMLUI components from metadata
489 | console.log('1. Extracting XMLUI components from metadata...');
490 | const components = getXMLUIComponents();
491 | console.log(` Found ${components.length} XMLUI components`);
492 |
493 | // Get available test files
494 | console.log('2. Scanning for e2e test files...');
495 | const testFiles = getE2ETestFiles();
496 | console.log(` Found ${testFiles.length} e2e test files`);
497 |
498 | // Map components to test files
499 | console.log('3. Mapping components to test files...');
500 | const componentTestMap = mapComponentsToTestFiles(components, testFiles);
501 |
502 | let testResults = null;
503 |
504 | if (!dryRun) {
505 | // Run playwright tests
506 | console.log('4. Running playwright e2e tests...');
507 | testResults = runPlaywrightTests();
508 |
509 | // Parse results
510 | console.log('5. Parsing test results...');
511 | const parsedResults = parseTestResults(testResults);
512 | testResults = parsedResults;
513 | }
514 |
515 | // Display summary
516 | displaySummary(components, componentTestMap, testResults);
517 |
518 | // Save markdown summary
519 | saveMarkdownSummary(components, componentTestMap, testResults);
520 |
521 | } catch (error) {
522 | console.error('Error generating e2e test summary:', error.message);
523 | process.exit(1);
524 | }
525 | }
526 |
527 | // Run the script
528 | if (require.main === module) {
529 | main();
530 | }
531 |
532 | module.exports = {
533 | getXMLUIComponents,
534 | getE2ETestFiles,
535 | mapComponentsToTestFiles,
536 | runPlaywrightTests,
537 | parseTestResults,
538 | displaySummary,
539 | saveMarkdownSummary
540 | };
541 |
```