├── .github
└── workflows
│ ├── doc-build.yml
│ ├── go.yml
│ ├── vuetifyjs-build.yml
│ └── vuetifyjs-test.yml
├── .gitignore
├── LICENSE
├── exchange
├── container.go
├── csv.go
├── dig
│ └── ecvariants
│ │ └── main.go
├── example_test.go
├── exporter.go
├── exporter_test.go
├── importer.go
├── importer_test.go
├── meta.go
├── options.go
├── setup_test.go
└── utils.go
├── go.mod
├── go.sum
├── i18n
├── dyna.go
├── i18n-transfer
│ ├── README.md
│ ├── csv
│ │ ├── csv_to_translations_map.go
│ │ └── translations_map_to_csv.go
│ ├── main.go
│ ├── parser
│ │ ├── export_to_translation_map.go
│ │ ├── import_from_translation_map.go
│ │ ├── parse_dir.go
│ │ ├── utils.go
│ │ └── visitor.go
│ └── test
│ │ ├── mock
│ │ ├── main.go
│ │ ├── messages
│ │ │ ├── message.go
│ │ │ └── phone.go
│ │ ├── out_messages
│ │ │ └── message.go
│ │ └── test_import.csv
│ │ └── transfer_test.go
├── i18n.go
└── i18n_test.go
├── login
├── assets.go
├── assets
│ ├── style.css
│ └── zxcvbn.js
├── buildcss.sh
├── builder.go
├── claims.go
├── example
│ └── main.go
├── flash.go
├── helpers.go
├── helpers_test.go
├── messages.go
├── middleware.go
├── oauth_user.go
├── primary_field.go
├── session_secure.go
├── tailwind.config.js
├── user_pass.go
├── utils.go
├── view_common.go
├── view_helper.go
└── views.go
├── oss
├── .gitignore
├── aliyun
│ ├── README.md
│ ├── aliyun.go
│ └── aliyun_test.go
├── filesystem
│ ├── filesystem.go
│ └── filesystem_test.go
├── oss.go
├── qiniu
│ ├── README.md
│ ├── qiniu.go
│ └── qiniu_test.go
├── s3
│ ├── README.md
│ ├── s3.go
│ └── s3_test.go
├── tencent
│ ├── tencent.go
│ ├── tencent_test.go
│ └── util.go
└── tests
│ ├── sample.txt
│ └── tests.go
├── perm
├── builder.go
├── db_policy.go
├── integration_test.go
├── matcher.go
├── policy.go
└── verifier.go
├── sitemap
├── README.md
├── http.go
├── ping.go
├── robots.go
├── robots_test.go
├── sitemap.go
├── sitemap_test.go
└── xml.go
└── ui
├── build_package_json.sh
├── cropper
├── cropper.go
├── cropperjs
│ ├── .gitignore
│ ├── README.md
│ ├── dist
│ │ ├── cropperjs.css
│ │ └── cropperjs.umd.cjs
│ ├── index.html
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── public
│ │ └── favicon.ico
│ ├── src
│ │ ├── demo
│ │ │ ├── App.vue
│ │ │ ├── components
│ │ │ │ └── CropperExample.vue
│ │ │ ├── main.ts
│ │ │ └── vite-env.d.ts
│ │ └── lib
│ │ │ ├── Cropper.vue
│ │ │ ├── main.ts
│ │ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
└── embed.go
├── fileicons
├── embed.go
└── icons
│ ├── ai.svg
│ ├── avi.svg
│ ├── css.svg
│ ├── csv.svg
│ ├── dbf.svg
│ ├── doc.svg
│ ├── dwg.svg
│ ├── exe.svg
│ ├── file.svg
│ ├── fla.svg
│ ├── html.svg
│ ├── iso.svg
│ ├── js.svg
│ ├── json.svg
│ ├── mp3.svg
│ ├── mp4.svg
│ ├── pdf.svg
│ ├── ppt.svg
│ ├── psd.svg
│ ├── rtf.svg
│ ├── svg.svg
│ ├── txt.svg
│ ├── xls.svg
│ ├── xml.svg
│ └── zip.svg
├── redactor
├── embed.go
├── redactor.go
└── redactorjs
│ ├── .gitignore
│ ├── README.md
│ ├── dist
│ ├── redactor.css
│ └── redactorjs.umd.cjs
│ ├── index.html
│ ├── jsconfig.json
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── public
│ └── favicon.ico
│ ├── src
│ ├── demo
│ │ ├── App.vue
│ │ └── main.js
│ └── lib
│ │ ├── RichTextRedactor.vue
│ │ ├── main.js
│ │ ├── redactor.min.css
│ │ └── redactor.min.js
│ └── vite.config.js
├── rm_node_modules.sh
├── tiptap
├── build.sh
├── tiptap.go
├── tiptapjs.go
└── tiptapjs
│ ├── .gitignore
│ ├── README.md
│ ├── dist
│ ├── tiptap.css
│ └── tiptapjs.umd.cjs
│ ├── index.html
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── public
│ └── favicon.ico
│ ├── src
│ ├── demo
│ │ ├── App.vue
│ │ ├── components
│ │ │ └── EditorExample.vue
│ │ ├── main.ts
│ │ └── vite-env.d.ts
│ └── lib
│ │ ├── Editor.vue
│ │ ├── Icon.vue
│ │ ├── icons
│ │ ├── add_col_after.svg
│ │ ├── add_col_before.svg
│ │ ├── add_row_after.svg
│ │ ├── add_row_before.svg
│ │ ├── bold.svg
│ │ ├── checklist.svg
│ │ ├── code.svg
│ │ ├── code_block.svg
│ │ ├── combine_cells.svg
│ │ ├── delete_col.svg
│ │ ├── delete_row.svg
│ │ ├── delete_table.svg
│ │ ├── github.svg
│ │ ├── hr.svg
│ │ ├── image.svg
│ │ ├── italic.svg
│ │ ├── link.svg
│ │ ├── mention.svg
│ │ ├── ol.svg
│ │ ├── paragraph.svg
│ │ ├── quote.svg
│ │ ├── redo.svg
│ │ ├── remove.svg
│ │ ├── strike.svg
│ │ ├── table.svg
│ │ ├── ul.svg
│ │ ├── underline.svg
│ │ └── undo.svg
│ │ ├── main.ts
│ │ ├── sass
│ │ ├── editor.scss
│ │ ├── main.scss
│ │ ├── menubar.scss
│ │ ├── menububble.scss
│ │ └── variables.scss
│ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── update_package_json.sh
├── vuetify
├── alert-title.go
├── alert.go
├── app-bar-nav-icon.go
├── app-bar-title.go
├── app-bar.go
├── app.go
├── autocomplete.go
├── avatar.go
├── badge.go
├── banner-actions.go
├── banner-text.go
├── banner.go
├── bottom-navigation.go
├── bottom-sheet.go
├── breadcrumbs-divider.go
├── breadcrumbs-item.go
├── breadcrumbs.go
├── btn-group.go
├── btn-toggle.go
├── btn.go
├── calendar-day.go
├── calendar-header.go
├── calendar-interval-event.go
├── calendar-interval.go
├── calendar-month-day.go
├── calendar.go
├── card-actions.go
├── card-item.go
├── card-subtitle.go
├── card-text.go
├── card-title.go
├── card.go
├── carousel-item.go
├── carousel.go
├── checkbox-btn.go
├── checkbox.go
├── chip-group.go
├── chip.go
├── class-icon.go
├── code.go
├── col.go
├── color-picker.go
├── combobox.go
├── component-icon.go
├── confirm-edit.go
├── container.go
├── counter.go
├── data-iterator.go
├── data-table-footer.go
├── data-table-headers.go
├── data-table-row.go
├── data-table-rows.go
├── data-table-server.go
├── data-table-virtual.go
├── data-table.go
├── date-input.go
├── date-picker-controls.go
├── date-picker-header.go
├── date-picker-month.go
├── date-picker-months.go
├── date-picker-years.go
├── date-picker.go
├── defaults-provider.go
├── dialog-bottom-transition.go
├── dialog-top-transition.go
├── dialog-transition.go
├── dialog.go
├── divider.go
├── empty-state.go
├── expand-transition.go
├── expand-x-transition.go
├── expansion-panel-text.go
├── expansion-panel-title.go
├── expansion-panel.go
├── expansion-panels.go
├── fab-transition.go
├── fab.go
├── fade-transition.go
├── field-label.go
├── field.go
├── file-input.go
├── fix-autocomplete.go
├── fix-btn.go
├── fix-checkbox.go
├── fix-chip-group.go
├── fix-color-theme.go
├── fix-consts.go
├── fix-container.go
├── fix-file-input.go
├── fix-icon.go
├── fix-list-item.go
├── fix-option-item.go
├── fix-radio-group.go
├── fix-select.go
├── fix-slider.go
├── fix-switch.go
├── fix-table.go
├── fix-text-field.go
├── fix-textarea.go
├── fix-toolbar-title.go
├── fix-toolbar.go
├── fix-utils.go
├── footer.go
├── form.go
├── gen_vuetify.sh
├── hover.go
├── icon.go
├── img.go
├── infinite-scroll.go
├── input.go
├── item-group.go
├── item.go
├── kbd.go
├── label.go
├── layout-item.go
├── layout.go
├── lazy.go
├── ligature-icon.go
├── list-group.go
├── list-img.go
├── list-item-action.go
├── list-item-media.go
├── list-item-subtitle.go
├── list-item-title.go
├── list-item.go
├── list-subheader.go
├── list.go
├── locale-provider.go
├── main.go
├── menu.go
├── messages.go
├── navigation-drawer.go
├── no-ssr.go
├── number-input.go
├── otp-input.go
├── overlay.go
├── pagination.go
├── parallax.go
├── picker-title.go
├── picker.go
├── progress-circular.go
├── progress-linear.go
├── pull-to-refresh.go
├── radio-group.go
├── radio.go
├── range-slider.go
├── rating.go
├── responsive.go
├── row.go
├── scale-transition.go
├── scroll-x-reverse-transition.go
├── scroll-x-transition.go
├── scroll-y-reverse-transition.go
├── scroll-y-transition.go
├── select.go
├── selection-control-group.go
├── selection-control.go
├── sheet.go
├── skeleton-loader.go
├── slide-group-item.go
├── slide-group.go
├── slide-x-reverse-transition.go
├── slide-x-transition.go
├── slide-y-reverse-transition.go
├── slide-y-transition.go
├── slider.go
├── snackbar-queue.go
├── snackbar.go
├── spacer.go
├── sparkline.go
├── speed-dial.go
├── stepper-actions.go
├── stepper-header.go
├── stepper-item.go
├── stepper-vertical-actions.go
├── stepper-vertical-item.go
├── stepper-vertical.go
├── stepper-window-item.go
├── stepper-window.go
├── stepper.go
├── svg-icon.go
├── switch.go
├── system-bar.go
├── tab.go
├── table.go
├── tabs-window-item.go
├── tabs-window.go
├── tabs.go
├── text-field.go
├── textarea.go
├── theme-provider.go
├── time-picker-clock.go
├── time-picker-controls.go
├── time-picker.go
├── timeline-item.go
├── timeline.go
├── toolbar-items.go
├── toolbar-title.go
├── toolbar.go
├── tooltip.go
├── treeview-group.go
├── treeview-item.go
├── treeview.go
├── validation.go
├── virtual-scroll.go
├── window-item.go
└── window.go
└── vuetifyx
├── README.md
├── autocomplete-fix.go
├── autocomplete.go
├── build.sh
├── buildinAsserts
└── vuetify.min.3.7.18.css
├── card.go
├── data-table.go
├── detail-info.go
├── draggable.go
├── filter.go
├── filter_test.go
├── key-info.go
├── linkage-select-remote-fix.go
├── linkage-select-remote.go
├── linkage-select.go
├── message-listener.go
├── overlay.go
├── picker.go
├── readonly-field.go
├── scroll-iframe.go
├── select-many.go
├── table-pagination.go
├── text-field.go
├── tiptap-editor.go
├── vuetifyxjs.go
├── vuetifyxjs
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── .prettierrc.json
├── README.md
├── components.d.ts
├── dist
│ ├── assets
│ │ ├── materialdesignicons-webfont.eot
│ │ ├── materialdesignicons-webfont.ttf
│ │ ├── materialdesignicons-webfont.woff
│ │ ├── materialdesignicons-webfont.woff2
│ │ └── vuetifyx.min.css
│ └── vuetifyx.min.js
├── docs
│ ├── .vitepress
│ │ ├── config.ts
│ │ └── theme
│ │ │ ├── index.ts
│ │ │ ├── myLayout.ts
│ │ │ ├── register-components.ts
│ │ │ ├── shared.js
│ │ │ ├── theme-default
│ │ │ ├── Layout.vue
│ │ │ ├── NotFound.vue
│ │ │ ├── components
│ │ │ │ ├── VPAlgoliaSearchBox.vue
│ │ │ │ ├── VPBackdrop.vue
│ │ │ │ ├── VPBadge.vue
│ │ │ │ ├── VPButton.vue
│ │ │ │ ├── VPCarbonAds.vue
│ │ │ │ ├── VPContent.vue
│ │ │ │ ├── VPDoc.vue
│ │ │ │ ├── VPDocAside.vue
│ │ │ │ ├── VPDocAsideCarbonAds.vue
│ │ │ │ ├── VPDocAsideOutline.vue
│ │ │ │ ├── VPDocAsideSponsors.vue
│ │ │ │ ├── VPDocFooter.vue
│ │ │ │ ├── VPDocFooterLastUpdated.vue
│ │ │ │ ├── VPDocOutlineItem.vue
│ │ │ │ ├── VPFeature.vue
│ │ │ │ ├── VPFeatures.vue
│ │ │ │ ├── VPFlyout.vue
│ │ │ │ ├── VPFooter.vue
│ │ │ │ ├── VPHero.vue
│ │ │ │ ├── VPHome.vue
│ │ │ │ ├── VPHomeContent.vue
│ │ │ │ ├── VPHomeFeatures.vue
│ │ │ │ ├── VPHomeHero.vue
│ │ │ │ ├── VPHomeSponsors.vue
│ │ │ │ ├── VPImage.vue
│ │ │ │ ├── VPLink.vue
│ │ │ │ ├── VPLocalNav.vue
│ │ │ │ ├── VPLocalNavOutlineDropdown.vue
│ │ │ │ ├── VPLocalSearchBox.vue
│ │ │ │ ├── VPMenu.vue
│ │ │ │ ├── VPMenuGroup.vue
│ │ │ │ ├── VPMenuLink.vue
│ │ │ │ ├── VPNav.vue
│ │ │ │ ├── VPNavBar.vue
│ │ │ │ ├── VPNavBarAppearance.vue
│ │ │ │ ├── VPNavBarExtra.vue
│ │ │ │ ├── VPNavBarHamburger.vue
│ │ │ │ ├── VPNavBarMenu.vue
│ │ │ │ ├── VPNavBarMenuGroup.vue
│ │ │ │ ├── VPNavBarMenuLink.vue
│ │ │ │ ├── VPNavBarSearch.vue
│ │ │ │ ├── VPNavBarSearchButton.vue
│ │ │ │ ├── VPNavBarSocialLinks.vue
│ │ │ │ ├── VPNavBarTitle.vue
│ │ │ │ ├── VPNavBarTranslations.vue
│ │ │ │ ├── VPNavScreen.vue
│ │ │ │ ├── VPNavScreenAppearance.vue
│ │ │ │ ├── VPNavScreenMenu.vue
│ │ │ │ ├── VPNavScreenMenuGroup.vue
│ │ │ │ ├── VPNavScreenMenuGroupLink.vue
│ │ │ │ ├── VPNavScreenMenuGroupSection.vue
│ │ │ │ ├── VPNavScreenMenuLink.vue
│ │ │ │ ├── VPNavScreenSocialLinks.vue
│ │ │ │ ├── VPNavScreenTranslations.vue
│ │ │ │ ├── VPPage.vue
│ │ │ │ ├── VPSidebar.vue
│ │ │ │ ├── VPSidebarGroup.vue
│ │ │ │ ├── VPSidebarItem.vue
│ │ │ │ ├── VPSkipLink.vue
│ │ │ │ ├── VPSocialLink.vue
│ │ │ │ ├── VPSocialLinks.vue
│ │ │ │ ├── VPSponsors.vue
│ │ │ │ ├── VPSponsorsGrid.vue
│ │ │ │ ├── VPSwitch.vue
│ │ │ │ ├── VPSwitchAppearance.vue
│ │ │ │ ├── VPTeamMembers.vue
│ │ │ │ ├── VPTeamMembersItem.vue
│ │ │ │ ├── VPTeamPage.vue
│ │ │ │ ├── VPTeamPageSection.vue
│ │ │ │ ├── VPTeamPageTitle.vue
│ │ │ │ └── icons
│ │ │ │ │ ├── VPIconAlignJustify.vue
│ │ │ │ │ ├── VPIconAlignLeft.vue
│ │ │ │ │ ├── VPIconAlignRight.vue
│ │ │ │ │ ├── VPIconArrowLeft.vue
│ │ │ │ │ ├── VPIconArrowRight.vue
│ │ │ │ │ ├── VPIconChevronDown.vue
│ │ │ │ │ ├── VPIconChevronLeft.vue
│ │ │ │ │ ├── VPIconChevronRight.vue
│ │ │ │ │ ├── VPIconChevronUp.vue
│ │ │ │ │ ├── VPIconEdit.vue
│ │ │ │ │ ├── VPIconHeart.vue
│ │ │ │ │ ├── VPIconLanguages.vue
│ │ │ │ │ ├── VPIconMinus.vue
│ │ │ │ │ ├── VPIconMinusSquare.vue
│ │ │ │ │ ├── VPIconMoon.vue
│ │ │ │ │ ├── VPIconMoreHorizontal.vue
│ │ │ │ │ ├── VPIconPlus.vue
│ │ │ │ │ ├── VPIconPlusSquare.vue
│ │ │ │ │ └── VPIconSun.vue
│ │ │ ├── composables
│ │ │ │ ├── aside.js
│ │ │ │ ├── data.js
│ │ │ │ ├── edit-link.js
│ │ │ │ ├── flyout.js
│ │ │ │ ├── langs.js
│ │ │ │ ├── local-nav.js
│ │ │ │ ├── nav.js
│ │ │ │ ├── outline.js
│ │ │ │ ├── prev-next.js
│ │ │ │ ├── sidebar.js
│ │ │ │ └── sponsor-grid.js
│ │ │ ├── custom.scss
│ │ │ ├── fonts
│ │ │ │ ├── inter-italic-cyrillic-ext.woff2
│ │ │ │ ├── inter-italic-cyrillic.woff2
│ │ │ │ ├── inter-italic-greek-ext.woff2
│ │ │ │ ├── inter-italic-greek.woff2
│ │ │ │ ├── inter-italic-latin-ext.woff2
│ │ │ │ ├── inter-italic-latin.woff2
│ │ │ │ ├── inter-italic-vietnamese.woff2
│ │ │ │ ├── inter-roman-cyrillic-ext.woff2
│ │ │ │ ├── inter-roman-cyrillic.woff2
│ │ │ │ ├── inter-roman-greek-ext.woff2
│ │ │ │ ├── inter-roman-greek.woff2
│ │ │ │ ├── inter-roman-latin-ext.woff2
│ │ │ │ ├── inter-roman-latin.woff2
│ │ │ │ └── inter-roman-vietnamese.woff2
│ │ │ ├── index.js
│ │ │ ├── styles
│ │ │ │ ├── base.css
│ │ │ │ ├── components
│ │ │ │ │ ├── custom-block.css
│ │ │ │ │ ├── vp-code-group.css
│ │ │ │ │ ├── vp-code.css
│ │ │ │ │ ├── vp-doc.css
│ │ │ │ │ └── vp-sponsor.css
│ │ │ │ ├── fonts.css
│ │ │ │ ├── icons.css
│ │ │ │ ├── utils.css
│ │ │ │ └── vars.css
│ │ │ ├── support
│ │ │ │ ├── lru.js
│ │ │ │ ├── sidebar.js
│ │ │ │ ├── translation.js
│ │ │ │ └── utils.js
│ │ │ └── theme.ts
│ │ │ └── without-fonts.js
│ ├── Components
│ │ ├── AutoComplete
│ │ │ └── index.md
│ │ ├── Filter
│ │ │ └── index.md
│ │ ├── Navigator
│ │ │ └── index.md
│ │ ├── Overlay
│ │ │ └── index.md
│ │ ├── ScrollIframe
│ │ │ └── index.md
│ │ ├── TiptapEditor
│ │ │ └── index.md
│ │ ├── VXAvatar
│ │ │ └── index.md
│ │ ├── VXBreadcrumbs
│ │ │ └── index.md
│ │ ├── VXBtn
│ │ │ └── index.md
│ │ ├── VXBtnGroup
│ │ │ └── index.md
│ │ ├── VXChart
│ │ │ └── index.md
│ │ ├── VXCheckbox
│ │ │ └── index.md
│ │ ├── VXChip
│ │ │ └── index.md
│ │ ├── VXDatePicker
│ │ │ └── index.md
│ │ ├── VXDialog
│ │ │ └── index.md
│ │ ├── VXField
│ │ │ ├── index.md
│ │ │ └── sendVariables.md
│ │ ├── VXLabel
│ │ │ └── index.md
│ │ ├── VXModelConverter
│ │ │ └── index.md
│ │ ├── VXModelProxy
│ │ │ └── index.md
│ │ ├── VXPagination
│ │ │ └── index.md
│ │ ├── VXRangePicker
│ │ │ └── index.md
│ │ ├── VXSegmentForm
│ │ │ └── index.md
│ │ ├── VXSelect
│ │ │ └── index.md
│ │ ├── VXTabs
│ │ │ └── index.md
│ │ ├── VXTimePicker
│ │ │ └── index.md
│ │ ├── VXToolbar
│ │ │ └── index.md
│ │ └── VXTreeview
│ │ │ └── index.md
│ ├── index.md
│ ├── postcss.config.mjs
│ ├── public
│ │ ├── iframe.html
│ │ ├── imgs
│ │ │ └── vx-avatar-example.png
│ │ └── logo.svg
│ ├── sidebar.ts
│ └── vite.config.ts
├── index.html
├── package.json
├── patches
│ └── @vitepress-code-preview__container.patch
├── pnpm-lock.yaml
├── public
│ ├── favicon.ico
│ └── iframe.html
├── scripts
│ └── new-demo-doc.ts
├── src
│ └── lib
│ │ ├── Autocomplete.vue
│ │ ├── Breadcrumbs
│ │ └── VXBreadcrumbs.vue
│ │ ├── Chart
│ │ ├── FunnelChart.vue
│ │ ├── VXChart.vue
│ │ ├── presets.config.ts
│ │ └── useVxChartMergeOpts.ts
│ │ ├── Common
│ │ ├── VXBtn.vue
│ │ ├── VXBtnGroup.vue
│ │ ├── VXChip.vue
│ │ ├── VXDialog.vue
│ │ ├── VXLabel.vue
│ │ ├── VXPagination.vue
│ │ └── VXToolBar.vue
│ │ ├── Datepicker.vue
│ │ ├── Datetimepicker.vue
│ │ ├── Filter
│ │ ├── Constants.ts
│ │ ├── FilterData.ts
│ │ ├── Model.ts
│ │ ├── components
│ │ │ ├── AutoCompleteItem.vue
│ │ │ ├── DateItem.vue
│ │ │ ├── DatePickerItem.vue
│ │ │ ├── DateRangeItem.vue
│ │ │ ├── DateRangePickerItem.vue
│ │ │ ├── DatetimeRangeItem.vue
│ │ │ ├── DatetimeRangePickerItem.vue
│ │ │ ├── FilterButton.vue
│ │ │ ├── FilterButtonBody.vue
│ │ │ ├── ItemFilter.vue
│ │ │ ├── LinkageSelectItem.vue
│ │ │ ├── LinkageSelectItemRemote.vue
│ │ │ ├── MultipleSelectItem.vue
│ │ │ ├── NumberItem.vue
│ │ │ ├── SelectItem.vue
│ │ │ └── StringItem.vue
│ │ └── index.vue
│ │ ├── Form
│ │ ├── VXCheckbox.vue
│ │ ├── VXDatePicker
│ │ │ ├── DatePicker.vue
│ │ │ ├── DatePickerBase.vue
│ │ │ ├── RangePicker.vue
│ │ │ ├── TimePicker.vue
│ │ │ └── TimeSelect.vue
│ │ ├── VXField.vue
│ │ ├── VXSegmentForm
│ │ │ ├── ConditionSwitch.vue
│ │ │ ├── SegmentItem.vue
│ │ │ ├── SegmentItemGroup.vue
│ │ │ ├── index.vue
│ │ │ ├── type.d.ts
│ │ │ └── useUtils.ts
│ │ └── VXSelect.vue
│ │ ├── Helpers.tsx
│ │ ├── IframeEmitter.vue
│ │ ├── LinkageSelect.vue
│ │ ├── LinkageSelectRemote
│ │ ├── components
│ │ │ └── LinkSelectAutoComplete.vue
│ │ └── index.vue
│ │ ├── MessageListener.vue
│ │ ├── Overlay.vue
│ │ ├── RestoreScrollListener.vue
│ │ ├── ScrollIframe.vue
│ │ ├── SelectMany.vue
│ │ ├── SendVariables.vue
│ │ ├── Tabs
│ │ └── VXTabs.vue
│ │ ├── TextDatepicker.vue
│ │ ├── TiptapEditor
│ │ ├── Extensions
│ │ │ ├── CallbackActionButton.ts
│ │ │ ├── CallbackActionButton.vue
│ │ │ └── ImageGlue.vue
│ │ └── TiptapEditor.vue
│ │ ├── Treeview
│ │ └── VXTreeview.vue
│ │ ├── VXAvatar.vue
│ │ ├── VXModelProxy.vue
│ │ ├── __tests__
│ │ ├── Autocomplete.spec.ts
│ │ ├── Datepicker.spec.ts
│ │ ├── Datetimepicker.spec.ts
│ │ ├── Filter.spec.ts
│ │ ├── LinkSelect.spec.ts
│ │ ├── SelectMany.spec.ts
│ │ ├── TextDatepicker.spec.ts
│ │ ├── handler.ts
│ │ └── testutils.ts
│ │ ├── composables
│ │ ├── forwardRefs.ts
│ │ ├── useBindingValue.ts
│ │ ├── useColor.ts
│ │ ├── useDatePicker.ts
│ │ ├── useEventListener.ts
│ │ ├── useFilteredAttrs.ts
│ │ └── useVDatePicker.ts
│ │ ├── icons
│ │ ├── checkbox-filled-outline.vue
│ │ └── checkbox-on-filled-outline.vue
│ │ ├── main.ts
│ │ ├── plugins
│ │ ├── README.md
│ │ ├── i18n.ts
│ │ ├── index.ts
│ │ ├── theme.ts
│ │ ├── tiptap.ts
│ │ └── vuetify.ts
│ │ ├── scss
│ │ ├── _fixer.scss
│ │ ├── _mixins.scss
│ │ ├── _utils.scss
│ │ ├── _variables.scss
│ │ ├── _vuetify.scss
│ │ ├── components
│ │ │ ├── _ActivityTimeLine.scss
│ │ │ ├── _AutoComplete.scss
│ │ │ ├── _Dialog.scss
│ │ │ ├── _Navigation.scss
│ │ │ ├── _Section.scss
│ │ │ ├── _SlideGroup.scss
│ │ │ ├── _VBtn.scss
│ │ │ ├── _VCard.scss
│ │ │ ├── _VField.scss
│ │ │ ├── _VFilter.scss
│ │ │ ├── _VPagination.scss
│ │ │ ├── _VTable.scss
│ │ │ ├── _VTextarea.scss
│ │ │ ├── _VVersionSelect.scss
│ │ │ ├── _imgUploader.scss
│ │ │ ├── _vChip.scss
│ │ │ └── _vTabs.scss
│ │ ├── index.scss
│ │ ├── page
│ │ │ ├── _Detailing.scss
│ │ │ ├── _Listing.scss
│ │ │ └── _PageBuilder.scss
│ │ └── sites
│ │ │ └── k-theme.scss
│ │ └── vite-env.d.ts
├── tsconfig-build.json
├── tsconfig.json
├── tsconfig.node.json
├── update-tip-tap.sh
├── vite.config.ts
└── vitest.config.ts
├── vx-avatar.go
├── vx-breadcrumbs.go
├── vx-btn-group.go
├── vx-btn.go
├── vx-chart.go
├── vx-checkbox.go
├── vx-chip.go
├── vx-date-picker.go
├── vx-dialog.go
├── vx-field.go
├── vx-iframe-emitter.go
├── vx-label.go
├── vx-model-proxy.go
├── vx-pagination.go
├── vx-range-picker.go
├── vx-segment-form.go
├── vx-select.go
├── vx-send-variables.go
├── vx-tabs.go
├── vx-time-picker.go
├── vx-toolbar.go
└── vx-treeview.go
/.github/workflows/doc-build.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy Docs
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | build-and-deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: checkout repository
13 | uses: actions/checkout@v3
14 |
15 | - name: set Node.js environment
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: '20.x'
19 |
20 | - name: Install pnpm
21 | run: npm install -g pnpm@9.15.5
22 |
23 | - name: Install dep
24 | working-directory: ui/vuetifyx/vuetifyxjs
25 | run: pnpm install
26 |
27 | - name: build doc
28 | working-directory: ui/vuetifyx/vuetifyxjs
29 | run: pnpm run docs:build
30 |
31 | - name: push to gh-pages branch
32 | uses: peaceiris/actions-gh-pages@v3
33 | with:
34 | github_token: ${{ secrets.GITHUB_TOKEN }}
35 | publish_dir: ui/vuetifyx/vuetifyxjs/docs/.vitepress/dist
36 | publish_branch: gh-pages
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: go
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | paths-ignore:
7 | - '**/dist/**'
8 | - '**/*.md'
9 | - '**/docs/**'
10 | pull_request:
11 | branches: [master]
12 | paths-ignore:
13 | - '**/dist/**'
14 | - '**/*.md'
15 | - '**/docs/**'
16 |
17 | jobs:
18 | test:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: szenius/set-timezone@v1.0
22 | with:
23 | timezoneLinux: 'Asia/Shanghai'
24 |
25 | - uses: actions/checkout@v2
26 |
27 | - name: Set up Go
28 | uses: actions/setup-go@v2
29 | with:
30 | go-version: 1.22.3
31 |
32 | - name: Build
33 | run: go build -v ./...
34 |
35 | - name: Test
36 | run: go test -v ./...
37 |
--------------------------------------------------------------------------------
/.github/workflows/vuetifyjs-build.yml:
--------------------------------------------------------------------------------
1 | name: Build and Commit Artifacts
2 |
3 | on:
4 | pull_request:
5 | branches: [master, release-test]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0 # 确保获取完整的提交历史
16 |
17 | - name: Set up Node.js
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: '18'
21 |
22 | - name: Install pnpm
23 | run: npm install -g pnpm@9.15.5
24 |
25 | - name: Build project
26 | run: |
27 | cd ui/vuetifyx/vuetifyxjs
28 | pnpm install
29 | pnpm run build
30 |
31 | - name: Commit build artifacts
32 | run: |
33 | git config --global user.name 'github-actions[bot]'
34 | git config --global user.email 'github-actions[bot]@users.noreply.github.com'
35 |
36 | # 强制添加被 .gitignore 忽略的 dist 目录
37 | git add ui/vuetifyx/vuetifyxjs/dist -f
38 |
39 | # 检查是否有更改
40 | if ! git diff --cached --quiet; then
41 | git commit -m 'Add build artifacts'
42 | git push origin HEAD:${{ github.head_ref }}
43 | else
44 | echo "No changes detected; skipping commit."
45 | fi
46 | env:
47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 |
--------------------------------------------------------------------------------
/.github/workflows/vuetifyjs-test.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: vuetifyxjs
5 |
6 | on:
7 | push:
8 | branches: [main]
9 | paths-ignore:
10 | - '**/dist/**'
11 | - '**/*.md'
12 | - '**/docs/**'
13 | pull_request:
14 | branches: [main]
15 | paths-ignore:
16 | - '**/dist/**'
17 | - '**/*.md'
18 | - '**/docs/**'
19 |
20 | jobs:
21 | test:
22 | runs-on: ubuntu-latest
23 | strategy:
24 | matrix:
25 | node-version: [18.x]
26 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
27 |
28 | steps:
29 | - uses: szenius/set-timezone@v1.0
30 | with:
31 | timezoneLinux: 'Asia/Shanghai'
32 |
33 | - uses: actions/checkout@v2
34 | - name: Use Node.js ${{ matrix.node-version }}
35 | uses: actions/setup-node@v2
36 | with:
37 | node-version: ${{ matrix.node-version }}
38 |
39 | - name: Install pnpm
40 | run: npm install -g pnpm@9.15.5
41 | - name: Build and Test
42 | run: cd ./ui/vuetifyx/vuetifyxjs/ && pnpm install && pnpm run build && pnpm run test:unit
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.DS_Store
3 | __*
4 | !__tests__
5 | vite.config.ts.timestamp-*.mjs
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright © 2019-2023 ThePlant
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/exchange/container.go:
--------------------------------------------------------------------------------
1 | package exchange
2 |
3 | type Reader interface {
4 | Header() []string
5 | ReadRow() ([]string, error)
6 | Next() bool
7 | Total() uint
8 | }
9 |
10 | type Writer interface {
11 | WriteHeader([]string) error
12 | WriteRow([]string) error
13 | Flush() error
14 | }
15 |
--------------------------------------------------------------------------------
/exchange/meta.go:
--------------------------------------------------------------------------------
1 | package exchange
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/iancoleman/strcase"
7 | )
8 |
9 | type MetaValues interface {
10 | Get(field string) (val string)
11 | }
12 |
13 | type Meta struct {
14 | field string
15 | snakeField string
16 | columnHeader string
17 | primaryKey bool
18 |
19 | setter MetaSetter
20 | valuer MetaValuer
21 | }
22 |
23 | func NewMeta(field string) *Meta {
24 | field = strings.TrimSpace(field)
25 | return &Meta{
26 | field: field,
27 | columnHeader: field,
28 | snakeField: strcase.ToSnake(field),
29 | }
30 | }
31 |
32 | // default is field name
33 | func (m *Meta) Header(s string) *Meta {
34 | m.columnHeader = strings.TrimSpace(s)
35 | return m
36 | }
37 |
38 | func (m *Meta) PrimaryKey(b bool) *Meta {
39 | m.primaryKey = b
40 | return m
41 | }
42 |
43 | type MetaSetter func(record interface{}, value string, metaValues MetaValues) error
44 |
45 | // set values to special fields
46 | // e.g. time.Time, struct, associated records ...
47 | func (m *Meta) Setter(f MetaSetter) *Meta {
48 | m.setter = f
49 | return m
50 | }
51 |
52 | type MetaValuer func(record interface{}) (string, error)
53 |
54 | // format values when exporting
55 | func (m *Meta) Valuer(f MetaValuer) *Meta {
56 | m.valuer = f
57 | return m
58 | }
59 |
--------------------------------------------------------------------------------
/exchange/options.go:
--------------------------------------------------------------------------------
1 | package exchange
2 |
3 | type ImporterExecOption interface {
4 | iePrivate()
5 | }
6 |
7 | type ExporterExecOption interface {
8 | eePrivate()
9 | }
10 |
11 | type ExecOption interface {
12 | ImporterExecOption
13 | ExporterExecOption
14 | }
15 |
16 | func MaxParamsPerSQL(v int) ExecOption {
17 | return &maxParamsPerSQLOption{v}
18 | }
19 |
20 | type maxParamsPerSQLOption struct {
21 | v int
22 | }
23 |
24 | var (
25 | _ ImporterExecOption = (*maxParamsPerSQLOption)(nil)
26 | _ ExporterExecOption = (*maxParamsPerSQLOption)(nil)
27 | )
28 |
29 | func (o *maxParamsPerSQLOption) iePrivate() {}
30 | func (o *maxParamsPerSQLOption) eePrivate() {}
31 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/README.md:
--------------------------------------------------------------------------------
1 | # i18n-transfer
2 |
3 | ## Usage
4 |
5 | - Install the i18n-transfer.
6 |
7 | ```
8 | $ go install github.com/qor5/x/v3/i18n/i18n-transfer@latest
9 | ```
10 |
11 | - Must cd root dir of the project.
12 |
13 | ```
14 | $ cd $GOPATH/src/github.com/[your project]
15 | ```
16 |
17 | example:
18 | ```
19 | $ cd $GOPATH/src/github.com/qor5/x
20 | ```
21 |
22 |
23 | - Run i18n-transfer.
24 |
25 | ```
26 |
27 | $ i18n-transfer
28 | Use the arrow keys to navigate: ↓ ↑ → ←
29 | ? Import Or Export:
30 | ▸ Import
31 | Export
32 |
33 | ```
34 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/csv/csv_to_translations_map.go:
--------------------------------------------------------------------------------
1 | package csv
2 |
3 | import (
4 | "encoding/csv"
5 | "os"
6 | )
7 |
8 | func CsvToTranslationsMap(csvPath string) (translationsMap map[string]map[string]string, err error) {
9 | f, err := os.Open(csvPath)
10 | if err != nil {
11 | return
12 | }
13 |
14 | r := csv.NewReader(f)
15 |
16 | records, err := r.ReadAll()
17 | if err != nil {
18 | return
19 | }
20 |
21 | keyValueMap := make(map[int]map[int]string)
22 | for i, record := range records {
23 | for j, value := range record {
24 | if keyValueMap[i] == nil {
25 | keyValueMap[i] = make(map[int]string)
26 | }
27 | keyValueMap[i][j] = value
28 | }
29 | }
30 |
31 | translationsMap = make(map[string]map[string]string)
32 | for i, record := range records {
33 | for j, value := range record {
34 | if translationsMap[records[0][j]] == nil {
35 | translationsMap[records[0][j]] = make(map[string]string)
36 | }
37 | translationsMap[records[0][j]][records[i][0]] = value
38 | }
39 | }
40 | return
41 | }
42 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/parser/utils.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | go_path "path"
5 | "strings"
6 | )
7 |
8 | func getTranslationMapKey(path, keyName, projectPath string) string {
9 | temp := strings.Split(projectPath, "/")
10 | return strings.TrimPrefix(go_path.Join(path, keyName), strings.TrimSuffix(strings.Join(temp[:len(temp)-1], "/"), "/")+"/")
11 | }
12 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/test/mock/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/qor5/x/v3/i18n"
5 | "github.com/qor5/x/v3/i18n/i18n-transfer/test/mock/messages"
6 | "golang.org/x/text/language"
7 | )
8 |
9 | func main() {
10 | b := i18n.New()
11 | b.SupportLanguages(language.Chinese, language.Japanese).
12 | RegisterForModule(language.Chinese, messages.I18nModuleKey, messages.User_CN).
13 | RegisterForModule(language.Japanese, messages.I18nModuleKey, messages.User_JP)
14 | }
15 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/test/mock/messages/message.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import "github.com/qor5/x/v3/i18n/i18n-transfer/test/mock/out_messages"
4 |
5 | const I18nModuleKey = "i18nModuleKey"
6 |
7 | type UserMessage struct {
8 | name string
9 | Email
10 | out_messages.Detail
11 | }
12 |
13 | type Email struct {
14 | Email string
15 | Phone
16 | }
17 |
18 | var User_CN = &UserMessage{
19 | name: "User CN",
20 | Email: Email_CN,
21 | Detail: out_messages.Detail_CN,
22 | }
23 |
24 | var Email_CN = Email{
25 | Email: "terry@theplant.cn",
26 | Phone: Phone_CN,
27 | }
28 |
29 | var User_JP = &UserMessage{
30 | name: "User JP",
31 | Email: Email_JP,
32 | Detail: out_messages.Detail_JP,
33 | }
34 |
35 | var Email_JP = Email{
36 | Email: "terry@theplant.jp",
37 | Phone: Phone_JP,
38 | }
39 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/test/mock/messages/phone.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type Phone struct {
4 | PhoneNumber string
5 | }
6 |
7 | var Phone_CN = Phone{
8 | PhoneNumber: "+86",
9 | }
10 |
11 | var Phone_JP = Phone{
12 | PhoneNumber: "+100",
13 | }
14 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/test/mock/out_messages/message.go:
--------------------------------------------------------------------------------
1 | package out_messages
2 |
3 | type Detail struct {
4 | Age string
5 | }
6 |
7 | var Detail_CN = Detail{
8 | Age: "13",
9 | }
10 |
11 | var Detail_JP = Detail{
12 | Age: "27",
13 | }
14 |
--------------------------------------------------------------------------------
/i18n/i18n-transfer/test/mock/test_import.csv:
--------------------------------------------------------------------------------
1 | Translation Keys,Chinese,Japanese
2 | mock/messages/Email,New CNEmail,New JPEmail
3 | mock/messages/PhoneNumber,+8666,+100000
4 | mock/messages/name,New User CN,New User JP
--------------------------------------------------------------------------------
/login/assets.go:
--------------------------------------------------------------------------------
1 | package login
2 |
3 | import (
4 | "embed"
5 | )
6 |
7 | //go:embed assets
8 | var assetsFS embed.FS
9 |
10 | var assetsPathPrefix = "/auth/assets/"
11 | var (
12 | StyleCSSURL = assetsPathPrefix + "style.css"
13 | ZxcvbnJSURL = assetsPathPrefix + "zxcvbn.js"
14 | )
15 |
--------------------------------------------------------------------------------
/login/buildcss.sh:
--------------------------------------------------------------------------------
1 | npx -y tailwindcss@v3 --minify -o assets/style.css
2 |
--------------------------------------------------------------------------------
/login/helpers_test.go:
--------------------------------------------------------------------------------
1 | package login
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/google/go-cmp/cmp"
7 | )
8 |
9 | func TestMustSetQuery(t *testing.T) {
10 | for _, c := range []struct {
11 | name string
12 | u string
13 | kvs []string
14 | expect string
15 | }{
16 | {
17 | name: "no query",
18 | u: "/a/b",
19 | kvs: []string{
20 | "k1", "v1",
21 | "k2", "v2",
22 | },
23 | expect: "/a/b?k1=v1&k2=v2",
24 | },
25 | {
26 | name: "has query",
27 | u: "/a/b?a=1",
28 | kvs: []string{
29 | "k1", "v1",
30 | "k2", "v2",
31 | },
32 | expect: "/a/b?a=1&k1=v1&k2=v2",
33 | },
34 | {
35 | name: "has same query",
36 | u: "/a/b?a=1&k2=v22",
37 | kvs: []string{
38 | "k1", "v1",
39 | "k2", "v2",
40 | },
41 | expect: "/a/b?a=1&k1=v1&k2=v2",
42 | },
43 | {
44 | name: "full url",
45 | u: "https://example.com/a/b?a=1&k2=v22",
46 | kvs: []string{
47 | "k1", "v1",
48 | "k2", "v2",
49 | },
50 | expect: "https://example.com/a/b?a=1&k1=v1&k2=v2",
51 | },
52 | } {
53 | if diff := cmp.Diff(c.expect, MustSetQuery(c.u, c.kvs...)); diff != "" {
54 | t.Fatalf("%s: %s\n", c.name, diff)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/login/primary_field.go:
--------------------------------------------------------------------------------
1 | package login
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/iancoleman/strcase"
7 | "github.com/sunfmin/reflectutils"
8 | )
9 |
10 | type PrimaryFielder interface {
11 | PrimaryField() string
12 | }
13 |
14 | func primaryField(obj interface{}) string {
15 | f := "ID"
16 | if v, ok := obj.(PrimaryFielder); ok {
17 | f = v.PrimaryField()
18 | }
19 | return f
20 | }
21 |
22 | func snakePrimaryField(obj interface{}) string {
23 | return strcase.ToSnake(primaryField(obj))
24 | }
25 |
26 | func objectID(obj interface{}) string {
27 | return fmt.Sprint(reflectutils.MustGet(obj, primaryField(obj)))
28 | }
29 |
--------------------------------------------------------------------------------
/login/session_secure.go:
--------------------------------------------------------------------------------
1 | package login
2 |
3 | import (
4 | "crypto/rand"
5 | "encoding/hex"
6 | "fmt"
7 |
8 | "gorm.io/gorm"
9 | )
10 |
11 | type SessionSecurer interface {
12 | UpdateSecure(db *gorm.DB, model interface{}, id string) error
13 | GetSecure() string
14 | }
15 |
16 | type SessionSecure struct {
17 | SessionSecure string `gorm:"size:32"`
18 | }
19 |
20 | var _ SessionSecurer = (*SessionSecure)(nil)
21 |
22 | func (ss *SessionSecure) UpdateSecure(db *gorm.DB, model interface{}, id string) error {
23 | b := make([]byte, 16)
24 | rand.Read(b)
25 | secure := hex.EncodeToString(b)
26 | err := db.Model(model).
27 | Where(fmt.Sprintf("%s = ?", snakePrimaryField(model)), id).
28 | Updates(map[string]interface{}{
29 | "session_secure": secure,
30 | }).
31 | Error
32 | if err != nil {
33 | return err
34 | }
35 | ss.SessionSecure = secure
36 | return nil
37 | }
38 |
39 | func (ss *SessionSecure) GetSecure() string {
40 | return ss.SessionSecure
41 | }
42 |
--------------------------------------------------------------------------------
/login/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./**/*.go'],
3 | }
4 |
--------------------------------------------------------------------------------
/login/utils.go:
--------------------------------------------------------------------------------
1 | package login
2 |
3 | import (
4 | "net/http"
5 |
6 | "gorm.io/gorm"
7 | )
8 |
9 | func RevokeTOTP(
10 | ssup SessionSecureUserPasser,
11 | db *gorm.DB,
12 | userModel interface{},
13 | userID string,
14 | ) (err error) {
15 | if err = ssup.SetIsTOTPSetup(db, userModel, false); err != nil {
16 | return err
17 | }
18 | if err = ssup.UpdateSecure(db, userModel, userID); err != nil {
19 | return err
20 | }
21 | return nil
22 | }
23 |
24 | func GetSessionToken(b *Builder, r *http.Request) string {
25 | c, err := r.Cookie(b.authCookieName)
26 | if err != nil {
27 | return ""
28 | }
29 | return c.Value
30 | }
31 |
--------------------------------------------------------------------------------
/oss/.gitignore:
--------------------------------------------------------------------------------
1 | test_env
2 |
--------------------------------------------------------------------------------
/oss/aliyun/README.md:
--------------------------------------------------------------------------------
1 | # Aliyun
2 |
3 | [Aliyun](http://aliyun.com) backend for [QOR OSS](https://github.com/qor/oss)
4 |
5 | ## Usage
6 |
7 | ```go
8 | import "github.com/qor/oss/aliyun"
9 |
10 | func main() {
11 | storage := aliyun.New(&aliyun.Config{
12 | AccessID: "access_id",
13 | AccessKey: "access_id",
14 | Bucket: "bucket",
15 | Endpoint: "oss-cn-hangzhou.aliyuncs.com",
16 | })
17 |
18 | // Save a reader interface into storage
19 | storage.Put("/sample.txt", reader)
20 |
21 | // Get file with path
22 | storage.Get("/sample.txt")
23 |
24 | // Get object as io.ReadCloser
25 | storage.GetStream("/sample.txt")
26 |
27 | // Delete file with path
28 | storage.Delete("/sample.txt")
29 |
30 | // List all objects under path
31 | storage.List("/")
32 |
33 | // Get Public Accessible URL (useful if current file saved privately)
34 | storage.GetURL("/sample.txt")
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/oss/filesystem/filesystem_test.go:
--------------------------------------------------------------------------------
1 | package filesystem
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/qor5/x/v3/oss/tests"
7 | )
8 |
9 | func TestAll(t *testing.T) {
10 | fileSystem := New("/tmp")
11 | tests.TestAll(fileSystem, t)
12 | }
13 |
--------------------------------------------------------------------------------
/oss/oss.go:
--------------------------------------------------------------------------------
1 | package oss
2 |
3 | import (
4 | "context"
5 | "io"
6 | "os"
7 | "time"
8 | )
9 |
10 | // StorageInterface define common API to operate storage
11 | type StorageInterface interface {
12 | Get(ctx context.Context, path string) (*os.File, error)
13 | GetStream(ctx context.Context, path string) (io.ReadCloser, error)
14 | Put(ctx context.Context, path string, reader io.Reader) (*Object, error)
15 | Delete(ctx context.Context, path string) error
16 | List(ctx context.Context, path string) ([]*Object, error)
17 | GetURL(ctx context.Context, path string) (string, error)
18 | GetEndpoint(ctx context.Context) string
19 | }
20 |
21 | // Object content object
22 | type Object struct {
23 | Path string
24 | Name string
25 | LastModified *time.Time
26 | StorageInterface StorageInterface
27 | }
28 |
29 | // Get retrieve object's content
30 | func (object Object) Get(ctx context.Context) (*os.File, error) {
31 | return object.StorageInterface.Get(ctx, object.Path)
32 | }
33 |
--------------------------------------------------------------------------------
/oss/qiniu/README.md:
--------------------------------------------------------------------------------
1 | # Qiniu
2 |
3 | [Qiniu](https://www.qiniu.com) backend for [QOR OSS](https://github.com/qor/oss)
4 |
5 | ## Usage
6 |
7 | ```go
8 | import "github.com/qor/oss/qiniu"
9 |
10 | func main() {
11 | storage := qiniu.New(&qiniu.Config{
12 | AccessID: "access_id",
13 | AccessKey: "access_key",
14 | Bucket: "bucket",
15 | Region: "huadong",
16 | Endpoint: "https://up.qiniup.com",
17 | })
18 |
19 | // Save a reader interface into storage
20 | storage.Put("/sample.txt", reader)
21 |
22 | // Get file with path
23 | storage.Get("/sample.txt")
24 |
25 | // Get object as io.ReadCloser
26 | storage.GetStream("/sample.txt")
27 |
28 | // Delete file with path
29 | storage.Delete("/sample.txt")
30 |
31 | // List all objects under path
32 | storage.List("/")
33 |
34 | // Get Public Accessible URL (useful if current file saved privately)
35 | storage.GetURL("/sample.txt")
36 | }
37 | ```
38 |
39 |
--------------------------------------------------------------------------------
/oss/s3/README.md:
--------------------------------------------------------------------------------
1 | # AWS S3
2 |
3 | [AWS S3](https://aws.amazon.com/cn/s3/) backend for [QOR OSS](https://github.com/qor/oss)
4 |
5 | ## Usage
6 |
7 | ```go
8 | import "github.com/qor/oss/aliyun"
9 |
10 | func main() {
11 | storage := s3.New(s3.Config{
12 | AccessID: "access_id",
13 | AccessKey: "access_key",
14 | Region: "region",
15 | Bucket: "bucket",
16 | Endpoint: "cdn.getqor.com",
17 | ACL: awss3.BucketCannedACLPublicRead,
18 | })
19 |
20 | // Save a reader interface into storage
21 | storage.Put("/sample.txt", reader)
22 |
23 | // Get file with path
24 | storage.Get("/sample.txt")
25 |
26 | // Get object as io.ReadCloser
27 | storage.GetStream("/sample.txt")
28 |
29 | // Delete file with path
30 | storage.Delete("/sample.txt")
31 |
32 | // List all objects under path
33 | storage.List("/")
34 |
35 | // Get Public Accessible URL (useful if current file saved privately)
36 | storage.GetURL("/sample.txt")
37 | }
38 | ```
39 |
40 |
41 |
--------------------------------------------------------------------------------
/oss/tencent/tencent_test.go:
--------------------------------------------------------------------------------
1 | package tencent
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "fmt"
7 | "os"
8 | "testing"
9 |
10 | "github.com/jinzhu/configor"
11 | "github.com/qor5/x/v3/oss/tests"
12 | )
13 |
14 | var client *Client
15 |
16 | func init() {
17 | cfg := Config{}
18 | configor.New(&configor.Config{ENVPrefix: "TENCENT"}).Load(&cfg)
19 |
20 | if len(cfg.AppID) == 0 {
21 | fmt.Println("No tencent configuration")
22 | return
23 | }
24 |
25 | client = New(&cfg)
26 | }
27 |
28 | func TestClient_Get(t *testing.T) {
29 |
30 | }
31 |
32 | func TestClient_Put(t *testing.T) {
33 | if client == nil {
34 | t.Skip(`skip because of no config: `)
35 | }
36 |
37 | f, err := os.ReadFile("/home/owen/Downloads/2.png")
38 | if err != nil {
39 | t.Error(err)
40 | return
41 | }
42 |
43 | client.Put(context.Background(), "test.png", bytes.NewReader(f))
44 | }
45 |
46 | func TestClient_Put2(t *testing.T) {
47 | if client == nil {
48 | t.Skip(`skip because of no config: `)
49 | }
50 |
51 | tests.TestAll(client, t)
52 | }
53 |
54 | func TestClient_Delete(t *testing.T) {
55 | if client == nil {
56 | t.Skip(`skip because of no config: `)
57 | }
58 |
59 | fmt.Println(client.Delete(context.Background(), "test.png"))
60 | }
61 |
--------------------------------------------------------------------------------
/oss/tests/sample.txt:
--------------------------------------------------------------------------------
1 | sample
2 |
--------------------------------------------------------------------------------
/perm/matcher.go:
--------------------------------------------------------------------------------
1 | package perm
2 |
3 | import (
4 | "path/filepath"
5 |
6 | "github.com/ory/ladon"
7 | )
8 |
9 | type PathMatcher struct{}
10 |
11 | func (m *PathMatcher) Matches(p ladon.Policy, haystack []string, needle string) (bool, error) {
12 | for _, h := range haystack {
13 | m, err := filepath.Match(h, needle)
14 | if err != nil {
15 | return false, err
16 | }
17 | if m {
18 | return true, nil
19 | }
20 | }
21 | return false, nil
22 | }
23 |
--------------------------------------------------------------------------------
/sitemap/ping.go:
--------------------------------------------------------------------------------
1 | package sitemap
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | )
8 |
9 | type ToUrlInterface interface {
10 | ToUrl(context.Context) string
11 | }
12 |
13 | func PingBing(site ToUrlInterface, ctx context.Context) (err error) {
14 | _, err = http.Get(fmt.Sprintf("http://www.bing.com/webmaster/ping.aspx?siteMap=%s", site.ToUrl(ctx)))
15 | return
16 | }
17 |
18 | func PingGoogle(site ToUrlInterface, ctx context.Context) (err error) {
19 | _, err = http.Get(fmt.Sprintf("https://www.google.com/webmasters/sitemaps/ping?sitemap=%s", site.ToUrl(ctx)))
20 | return
21 | }
22 |
23 | func PingAll(site ToUrlInterface, ctx context.Context) (err error) {
24 | if err = PingGoogle(site, ctx); err != nil {
25 | return
26 | }
27 |
28 | if err = PingBing(site, ctx); err != nil {
29 | return
30 | }
31 |
32 | return
33 | }
34 |
--------------------------------------------------------------------------------
/ui/build_package_json.sh:
--------------------------------------------------------------------------------
1 | ROOT=$(pwd)
2 |
3 | PKGS="
4 | $ROOT/../../web/corejs
5 | $ROOT/tiptap/tiptapjs
6 | $ROOT/vuetifyx/vuetifyxjs
7 | $ROOT/cropper/cropperjs
8 | $ROOT/redactor/redactorjs
9 | "
10 | for i in $PKGS
11 | do
12 | echo "$i" && \
13 | cd $i && pnpm install && pnpm build
14 | done
15 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist-ssr
13 | coverage
14 | *.local
15 |
16 | /cypress/videos/
17 | /cypress/screenshots/
18 |
19 | # Editor directories and files
20 | .vscode/*
21 | !.vscode/extensions.json
22 | .idea
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 |
29 | *.tsbuildinfo
30 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/README.md:
--------------------------------------------------------------------------------
1 | # cropperjs
2 |
3 | This template should help get you started developing with Vue 3 in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
8 |
9 | ## Type Support for `.vue` Imports in TS
10 |
11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
12 |
13 | ## Customize configuration
14 |
15 | See [Vite Configuration Reference](https://vitejs.dev/config/).
16 |
17 | ## Project Setup
18 |
19 | ```sh
20 | pnpm install
21 | ```
22 |
23 | ### Compile and Hot-Reload for Development
24 |
25 | ```sh
26 | pnpm dev
27 | ```
28 |
29 | ### Type-Check, Compile and Minify for Production
30 |
31 | ```sh
32 | pnpm build
33 | ```
34 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cropperjs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vue-tsc --noEmit && vite build ",
9 | "watch-build": "nodemon --watch src --exec 'npm run build'",
10 | "preview": "vite preview",
11 | "format": "prettier --write src/"
12 | },
13 | "dependencies": {
14 | "cropperjs": "^1.6.2",
15 | "vue": "^3.5.13"
16 | },
17 | "devDependencies": {
18 | "@tsconfig/node20": "^20.1.4",
19 | "@types/node": "^20.17.24",
20 | "@vitejs/plugin-vue": "^5.2.3",
21 | "@vue/tsconfig": "^0.5.1",
22 | "nodemon": "^3.1.9",
23 | "npm-run-all2": "^6.2.6",
24 | "prettier": "^3.5.3",
25 | "typescript": "~5.4.5",
26 | "vite": "^5.4.19",
27 | "vite-plugin-vue-devtools": "^7.7.2",
28 | "vue-tsc": "^2.2.8"
29 | },
30 | "pnpm": {
31 | "overrides": {
32 | "esbuild": "0.25.0"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/cropper/cropperjs/public/favicon.ico
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/src/demo/App.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | cropper-example
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/src/demo/components/CropperExample.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | {{ value }}
13 |
14 |
15 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/src/demo/main.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * main.ts
3 | *
4 | * Bootstraps Vuetify and other plugins then mounts the App`
5 | */
6 |
7 | // Plugins
8 |
9 | // Components
10 | import App from "@/demo/App.vue";
11 |
12 | // Composables
13 | import { createApp } from "vue";
14 |
15 | const app = createApp(App);
16 |
17 | app.mount("#app");
18 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/src/demo/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.vue" {
4 | import type { DefineComponent } from "vue";
5 | const component: DefineComponent<{}, {}, any>;
6 | export default component;
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/src/lib/main.ts:
--------------------------------------------------------------------------------
1 | import { type App } from "vue";
2 | import Cropper from "@/lib/Cropper.vue";
3 |
4 | declare const window: any;
5 | window.__goplaidVueComponentRegisters =
6 | window.__goplaidVueComponentRegisters || [];
7 | window.__goplaidVueComponentRegisters.push((app: App, vueOptions: any): any => {
8 | app.component("vue-cropper", Cropper);
9 | });
10 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/src/lib/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.vue" {
4 | import type { DefineComponent } from "vue";
5 | const component: DefineComponent<{}, {}, any>;
6 | export default component;
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4 | "exclude": ["src/**/__tests__/*"],
5 | "compilerOptions": {
6 | "composite": true,
7 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
8 |
9 | "baseUrl": ".",
10 | "paths": {
11 | "@/*": ["./src/*"]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.node.json"
6 | },
7 | {
8 | "path": "./tsconfig.app.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node20/tsconfig.json",
3 | "include": [
4 | "vite.config.*",
5 | "vitest.config.*",
6 | "cypress.config.*",
7 | "nightwatch.conf.*",
8 | "playwright.config.*"
9 | ],
10 | "compilerOptions": {
11 | "composite": true,
12 | "noEmit": true,
13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
14 |
15 | "module": "ESNext",
16 | "moduleResolution": "Bundler",
17 | "types": ["node"]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/cropper/cropperjs/vite.config.ts:
--------------------------------------------------------------------------------
1 | import {fileURLToPath, URL} from 'node:url'
2 |
3 | import {defineConfig} from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 | import VueDevTools from 'vite-plugin-vue-devtools'
6 | import {resolve} from "path";
7 |
8 | // https://vitejs.dev/config/
9 | export default defineConfig({
10 | build: {
11 | // minify: false,
12 | lib: {
13 | entry: resolve(__dirname, 'src/lib/main.ts'),
14 | formats: ['umd'],
15 | name: 'cropper'
16 | },
17 | copyPublicDir: false,
18 | rollupOptions: {
19 | external: ['vue'],
20 | output: {
21 | assetFileNames: (assetInfo) => {
22 | return 'cropperjs.css'
23 | },
24 | globals: {
25 | vue: 'Vue',
26 | }
27 | }
28 | }
29 | },
30 | plugins: [
31 | vue(),
32 | VueDevTools(),
33 | ],
34 | resolve: {
35 | alias: {
36 | '@': fileURLToPath(new URL('./src', import.meta.url))
37 | }
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/ui/cropper/embed.go:
--------------------------------------------------------------------------------
1 | package cropper
2 |
3 | import (
4 | "embed"
5 |
6 | "github.com/qor5/web/v3"
7 | )
8 |
9 | //go:embed cropperjs/dist
10 | var box embed.FS
11 |
12 | func JSComponentsPack() web.ComponentsPack {
13 | v, err := box.ReadFile("cropperjs/dist/cropperjs.umd.cjs")
14 | if err != nil {
15 | panic(err)
16 | }
17 |
18 | return web.ComponentsPack(v)
19 | }
20 |
21 | func CSSComponentsPack() web.ComponentsPack {
22 | v, err := box.ReadFile("cropperjs/dist/cropperjs.css")
23 | if err != nil {
24 | panic(err)
25 | }
26 |
27 | return web.ComponentsPack(v)
28 | }
29 |
--------------------------------------------------------------------------------
/ui/fileicons/embed.go:
--------------------------------------------------------------------------------
1 | package fileicons
2 |
3 | import (
4 | "embed"
5 | "encoding/base64"
6 | "fmt"
7 | "strings"
8 |
9 | h "github.com/theplant/htmlgo"
10 | )
11 |
12 | //go:embed icons
13 | var icons embed.FS
14 |
15 | var svgs = map[string]string{}
16 |
17 | func init() {
18 | files, err := icons.ReadDir("icons")
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | for _, f := range files {
24 | if f.IsDir() {
25 | continue
26 | }
27 | // fmt.Println(f.Name())
28 | svg, err := icons.ReadFile(fmt.Sprintf("icons/%s", f.Name()))
29 | if err != nil {
30 | panic(err)
31 | }
32 | filetype := strings.Split(f.Name(), ".")[0]
33 | svgs[filetype] = base64.StdEncoding.EncodeToString(svg)
34 | }
35 |
36 | // fmt.Println(svgs)
37 | }
38 |
39 | func Icon(ext string) *h.HTMLTagBuilder {
40 | img, ok := svgs[ext]
41 | if !ok {
42 | img = svgs["file"]
43 | }
44 |
45 | return h.Img(fmt.Sprintf("data:image/svg+xml;base64,%s", img))
46 | }
47 |
--------------------------------------------------------------------------------
/ui/fileicons/icons/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
46 |
--------------------------------------------------------------------------------
/ui/redactor/embed.go:
--------------------------------------------------------------------------------
1 | package redactor
2 |
3 | import (
4 | "bytes"
5 | "embed"
6 |
7 | "github.com/qor5/web/v3"
8 | )
9 |
10 | //go:embed redactorjs
11 | var box embed.FS
12 |
13 | func JSComponentsPack() web.ComponentsPack {
14 | var js [][]byte
15 | j1, err := box.ReadFile("redactorjs/dist/redactorjs.umd.cjs")
16 | if err != nil {
17 | panic(err)
18 | }
19 | js = append(js, j1)
20 | return web.ComponentsPack(bytes.Join(js, []byte("\n\n")))
21 | }
22 |
23 | func CSSComponentsPack() web.ComponentsPack {
24 | c, err := box.ReadFile("redactorjs/dist/redactor.css")
25 | if err != nil {
26 | panic(err)
27 | }
28 | return web.ComponentsPack(c)
29 | }
30 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist-ssr
13 | coverage
14 | *.local
15 |
16 | .vscode
17 |
18 | /cypress/videos/
19 | /cypress/screenshots/
20 |
21 | # Editor directories and files
22 | .vscode/*
23 | !.vscode/extensions.json
24 | .idea
25 | *.suo
26 | *.ntvs*
27 | *.njsproj
28 | *.sln
29 | *.sw?
30 |
31 | *.tsbuildinfo
32 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/README.md:
--------------------------------------------------------------------------------
1 | # redactorjs
2 |
3 | This template should help get you started developing with Vue 3 in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
8 |
9 | ## Customize configuration
10 |
11 | See [Vite Configuration Reference](https://vitejs.dev/config/).
12 |
13 | ## Project Setup
14 |
15 | ```sh
16 | pnpm install
17 | ```
18 |
19 | ### Compile and Hot-Reload for Development
20 |
21 | ```sh
22 | pnpm dev
23 | ```
24 |
25 | ### Compile and Minify for Production
26 |
27 | ```sh
28 | pnpm build
29 | ```
30 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | },
7 | "exclude": ["node_modules", "dist"]
8 | }
9 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redactorjs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "watch-build": "nodemon --watch src --exec 'npm run build'",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "vue": "^3.5.13"
14 | },
15 | "devDependencies": {
16 | "@vitejs/plugin-vue": "^5.2.3",
17 | "nodemon": "^3.1.9",
18 | "vite": "^5.4.19",
19 | "vite-plugin-vue-devtools": "^7.7.2"
20 | },
21 | "pnpm": {
22 | "overrides": {
23 | "esbuild": "0.25.0"
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/redactor/redactorjs/public/favicon.ico
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/src/demo/App.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/src/demo/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | createApp(App).mount('#app')
5 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/src/lib/main.js:
--------------------------------------------------------------------------------
1 | import RichTextRedactor from "@/lib/RichTextRedactor.vue";
2 |
3 | window.__goplaidVueComponentRegisters = window.__goplaidVueComponentRegisters || []
4 | window.__goplaidVueComponentRegisters.push((app, vueOptions) => {
5 | app.component("redactor", RichTextRedactor)
6 | })
7 |
--------------------------------------------------------------------------------
/ui/redactor/redactorjs/vite.config.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url'
2 |
3 | import { defineConfig } from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 | import VueDevTools from 'vite-plugin-vue-devtools'
6 | import { resolve } from 'path'
7 |
8 | // https://vitejs.dev/config/
9 | export default defineConfig({
10 | build: {
11 | // minify: false,
12 | lib: {
13 | entry: resolve(__dirname, 'src/lib/main.js'),
14 | formats: ['umd'],
15 | name: 'redactor'
16 | },
17 | copyPublicDir: false,
18 | rollupOptions: {
19 | external: ['vue'],
20 | output: {
21 | assetFileNames: (assetInfo) => {
22 | return 'redactor.css'
23 | },
24 | globals: {
25 | vue: 'Vue',
26 | }
27 | }
28 | }
29 | },
30 | plugins: [
31 | vue(),
32 | VueDevTools(),
33 | ],
34 | resolve: {
35 | alias: {
36 | '@': fileURLToPath(new URL('./src', import.meta.url))
37 | }
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/ui/rm_node_modules.sh:
--------------------------------------------------------------------------------
1 | ROOT=$(pwd)
2 |
3 | PKGS="
4 | $ROOT/../../web/corejs
5 | $ROOT/tiptap/tiptapjs
6 | $ROOT/vuetify/vuetifyjs
7 | $ROOT/docs/docsjs
8 | "
9 | for i in $PKGS
10 | do
11 | echo "$i/node_modules" && \
12 | rm -rf $i/node_modules
13 | done
14 |
--------------------------------------------------------------------------------
/ui/tiptap/build.sh:
--------------------------------------------------------------------------------
1 | # @snippet_begin(TiptapBuilderSH)
2 | CUR=$(pwd)/$(dirname $0)
3 |
4 | if test "$1" = 'clean'; then
5 | echo "Removing node_modules"
6 | rm -rf $CUR/tiptapjs/node_modules/
7 | fi
8 |
9 | rm -r $CUR/tiptapjs/dist
10 | echo "Building tiptapjs"
11 | cd $CUR/tiptapjs && pnpm install && pnpm run build
12 |
13 | # @snippet_end
14 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptap.go:
--------------------------------------------------------------------------------
1 | package tiptap
2 |
3 | // @snippet_begin(TipTapEditorHTMLComponent)
4 | import (
5 | "context"
6 |
7 | h "github.com/theplant/htmlgo"
8 | )
9 |
10 | type TipTapEditorBuilder struct {
11 | tag *h.HTMLTagBuilder
12 | }
13 |
14 | func TipTapEditor() (r *TipTapEditorBuilder) {
15 | r = &TipTapEditorBuilder{
16 | tag: h.Tag("tiptap-editor"),
17 | }
18 |
19 | return
20 | }
21 |
22 | func (b *TipTapEditorBuilder) Attr(vs ...interface{}) (r *TipTapEditorBuilder) {
23 | b.tag.Attr(vs...)
24 | return b
25 | }
26 |
27 | func (b *TipTapEditorBuilder) MarshalHTML(ctx context.Context) (r []byte, err error) {
28 | return b.tag.MarshalHTML(ctx)
29 | }
30 |
31 | // @snippet_end
32 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs.go:
--------------------------------------------------------------------------------
1 | package tiptap
2 |
3 | // @snippet_begin(TipTapPackrSample)
4 | import (
5 | "embed"
6 |
7 | "github.com/qor5/web/v3"
8 | )
9 |
10 | //go:embed tiptapjs/dist
11 | var box embed.FS
12 |
13 | func JSComponentsPack() web.ComponentsPack {
14 | v, err := box.ReadFile("tiptapjs/dist/tiptapjs.umd.cjs")
15 | if err != nil {
16 | panic(err)
17 | }
18 |
19 | return web.ComponentsPack(v)
20 | }
21 |
22 | func CSSComponentsPack() web.ComponentsPack {
23 | v, err := box.ReadFile("tiptapjs/dist/tiptap.css")
24 | if err != nil {
25 | panic(err)
26 | }
27 |
28 | return web.ComponentsPack(v)
29 | }
30 |
31 | // @snippet_end
32 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist-ssr
13 | coverage
14 | *.local
15 |
16 | /cypress/videos/
17 | /cypress/screenshots/
18 |
19 | # Editor directories and files
20 | .vscode/*
21 | !.vscode/extensions.json
22 | .idea
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 |
29 | *.tsbuildinfo
30 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/README.md:
--------------------------------------------------------------------------------
1 | # tiptapjs
2 |
3 | This template should help get you started developing with Vue 3 in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
8 |
9 | ## Type Support for `.vue` Imports in TS
10 |
11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
12 |
13 | ## Customize configuration
14 |
15 | See [Vite Configuration Reference](https://vitejs.dev/config/).
16 |
17 | ## Project Setup
18 |
19 | ```sh
20 | pnpm install
21 | ```
22 |
23 | ### Compile and Hot-Reload for Development
24 |
25 | ```sh
26 | pnpm dev
27 | ```
28 |
29 | ### Type-Check, Compile and Minify for Production
30 |
31 | ```sh
32 | pnpm build
33 | ```
34 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/tiptap/tiptapjs/public/favicon.ico
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/demo/App.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | editor-example
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/demo/components/EditorExample.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | {{ value }}
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/demo/main.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * main.ts
3 | *
4 | * Bootstraps Vuetify and other plugins then mounts the App`
5 | */
6 |
7 | // Plugins
8 |
9 | // Components
10 | import App from "@/demo/App.vue";
11 |
12 | // Composables
13 | import { createApp } from "vue";
14 |
15 | const app = createApp(App);
16 | app.mount("#app");
17 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/demo/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.vue" {
4 | import type { DefineComponent } from "vue";
5 | const component: DefineComponent<{}, {}, any>;
6 | export default component;
7 | }
8 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
26 |
27 |
54 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/add_col_after.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/add_col_before.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/add_row_after.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/add_row_before.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/bold.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/checklist.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/code.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/combine_cells.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/delete_col.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/delete_row.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/github.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/hr.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/image.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/italic.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/link.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/mention.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/ol.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/paragraph.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/quote.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/redo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/remove.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/strike.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/table.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/ul.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/underline.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/icons/undo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/main.ts:
--------------------------------------------------------------------------------
1 | // @snippet_begin(TipTapRegisterVueComponent)
2 | import { type App } from "vue";
3 | import TipTapEditor from "@/lib/Editor.vue";
4 |
5 | declare const window: any;
6 | window.__goplaidVueComponentRegisters =
7 | window.__goplaidVueComponentRegisters || [];
8 | window.__goplaidVueComponentRegisters.push((app: App, vueOptions: any): any => {
9 | app.component("tiptap-editor", TipTapEditor);
10 | });
11 | // @snippet_end
12 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/sass/main.scss:
--------------------------------------------------------------------------------
1 | @import "./variables";
2 | @import "./editor";
3 | @import "./menubar";
4 | @import "./menububble";
5 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/sass/menubar.scss:
--------------------------------------------------------------------------------
1 | .menubar {
2 | margin-bottom: 1rem;
3 | transition:
4 | visibility 0.2s 0.4s,
5 | opacity 0.2s 0.4s;
6 |
7 | &.is-hidden {
8 | visibility: hidden;
9 | opacity: 0;
10 | }
11 |
12 | &.is-focused {
13 | visibility: visible;
14 | opacity: 1;
15 | transition:
16 | visibility 0.2s,
17 | opacity 0.2s;
18 | }
19 |
20 | &__button {
21 | font-weight: bold;
22 | display: inline-flex;
23 | background: transparent;
24 | border: 0;
25 | color: $color-black;
26 | padding: 0.2rem 0.5rem;
27 | margin-right: 0.2rem;
28 | border-radius: 3px;
29 | cursor: pointer;
30 |
31 | &:hover {
32 | background-color: rgba($color-black, 0.05);
33 | }
34 |
35 | &.is-active {
36 | background-color: rgba($color-black, 0.1);
37 | }
38 | }
39 |
40 | span#{&}__button {
41 | font-size: 13.3333px;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/sass/menububble.scss:
--------------------------------------------------------------------------------
1 | .menububble {
2 | position: absolute;
3 | display: flex;
4 | z-index: 20;
5 | background: $color-black;
6 | border-radius: 5px;
7 | padding: 0.3rem;
8 | margin-bottom: 0.5rem;
9 | transform: translateX(-50%);
10 | visibility: hidden;
11 | opacity: 0;
12 | transition:
13 | opacity 0.2s,
14 | visibility 0.2s;
15 |
16 | &.is-active {
17 | opacity: 1;
18 | visibility: visible;
19 | }
20 |
21 | &__button {
22 | display: inline-flex;
23 | background: transparent;
24 | border: 0;
25 | color: $color-white;
26 | padding: 0.2rem 0.5rem;
27 | margin-right: 0.2rem;
28 | border-radius: 3px;
29 | cursor: pointer;
30 |
31 | &:last-child {
32 | margin-right: 0;
33 | }
34 |
35 | &:hover {
36 | background-color: rgba($color-white, 0.1);
37 | }
38 |
39 | &.is-active {
40 | background-color: rgba($color-white, 0.2);
41 | }
42 | }
43 |
44 | &__form {
45 | display: flex;
46 | align-items: center;
47 | }
48 |
49 | &__input {
50 | font: inherit;
51 | border: none;
52 | background: transparent;
53 | color: $color-white;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/sass/variables.scss:
--------------------------------------------------------------------------------
1 | $color-black: #000000;
2 | $color-white: #ffffff;
3 | $color-grey: #dddddd;
4 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/src/lib/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.vue" {
4 | import type { DefineComponent } from "vue";
5 | const component: DefineComponent<{}, {}, any>;
6 | export default component;
7 | }
8 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4 | "exclude": ["src/**/__tests__/*"],
5 | "compilerOptions": {
6 | "composite": true,
7 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
8 |
9 | "baseUrl": ".",
10 | "paths": {
11 | "@/*": ["./src/*"]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.node.json"
6 | },
7 | {
8 | "path": "./tsconfig.app.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/ui/tiptap/tiptapjs/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node20/tsconfig.json",
3 | "include": [
4 | "vite.config.*",
5 | "vitest.config.*",
6 | "cypress.config.*",
7 | "nightwatch.conf.*",
8 | "playwright.config.*"
9 | ],
10 | "compilerOptions": {
11 | "composite": true,
12 | "noEmit": true,
13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
14 |
15 | "module": "ESNext",
16 | "moduleResolution": "Bundler",
17 | "types": ["node"]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/update_package_json.sh:
--------------------------------------------------------------------------------
1 | ROOT=$(pwd)
2 |
3 | PKGS="
4 | $ROOT/../../web/corejs
5 | $ROOT/tiptap/tiptapjs
6 | $ROOT/vuetifyx/vuetifyxjs
7 | $ROOT/cropper/cropperjs
8 | $ROOT/redactor/redactorjs
9 | "
10 | for i in $PKGS
11 | do
12 | echo "$i" && \
13 | cd $i && pnpm update && pnpm install && pnpm build
14 | done
15 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-autocomplete.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import (
4 | h "github.com/theplant/htmlgo"
5 | )
6 |
7 | func VAutocomplete(children ...h.HTMLComponent) (r *VAutocompleteBuilder) {
8 | r = &VAutocompleteBuilder{
9 | tag: h.Tag("v-autocomplete").Children(children...),
10 | }
11 | return
12 | }
13 |
14 | func (b *VAutocompleteBuilder) ErrorMessages(v ...string) (r *VAutocompleteBuilder) {
15 | SetErrorMessages(b.tag, v)
16 | return b
17 | }
18 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-btn.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import (
4 | "github.com/qor5/web/v3"
5 | h "github.com/theplant/htmlgo"
6 | )
7 |
8 | func VBtn(text string) (r *VBtnBuilder) {
9 | r = &VBtnBuilder{
10 | tag: h.Tag("v-btn").Text(text),
11 | }
12 | return
13 | }
14 |
15 | func (b *VBtnBuilder) OnClick(eventFuncId string) (r *VBtnBuilder) {
16 | b.tag.Attr("@click", web.POST().EventFunc(eventFuncId).Go())
17 | return b
18 | }
19 |
20 | func (b *VBtnBuilder) AttrIf(key, value interface{}, add bool) (r *VBtnBuilder) {
21 | b.tag.AttrIf(key, value, add)
22 | return b
23 | }
24 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-checkbox.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VCheckboxBuilder) ErrorMessages(v ...string) (r *VCheckboxBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-chip-group.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VChipGroupBuilder) Value(v interface{}) (r *VChipGroupBuilder) {
4 | b.ModelValue(v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-container.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import "fmt"
4 |
5 | type DType string
6 |
7 | const (
8 | DTypeFlex DType = "flex"
9 | DTypeInlineFlex DType = "inline-flex"
10 | DTypeBlock DType = "block"
11 | )
12 |
13 | type SizeType string
14 |
15 | const (
16 | Xs SizeType = "xs"
17 | Sm SizeType = "sm"
18 | Md SizeType = "md"
19 | Lg SizeType = "lg"
20 | Xl SizeType = "xl"
21 | )
22 |
23 | type AlignType string
24 |
25 | const (
26 | Left AlignType = "left"
27 | Center AlignType = "center"
28 | Right AlignType = "right"
29 | Justify AlignType = "justify"
30 | )
31 |
32 | func (b *VContainerBuilder) DType(v DType) (r *VContainerBuilder) {
33 | b.tag.Attr(fmt.Sprintf(":d-%s", v), fmt.Sprint(true))
34 | return b
35 | }
36 |
37 | func (b *VContainerBuilder) TextAlign(s SizeType, a AlignType) (r *VContainerBuilder) {
38 | b.tag.Attr(fmt.Sprintf(":text-%s-%s", s, a), fmt.Sprint(true))
39 | return b
40 | }
41 |
42 | func (b *VLayoutBuilder) DType(v DType) (r *VLayoutBuilder) {
43 | b.tag.Attr(fmt.Sprintf(":d-%s", v), fmt.Sprint(true))
44 | return b
45 | }
46 |
47 | func (b *VContainerBuilder) GridList(s SizeType) (r *VContainerBuilder) {
48 | b.tag.Attr(fmt.Sprintf(":grid-list-%s", s), fmt.Sprint(true))
49 | return b
50 | }
51 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-file-input.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VFileInputBuilder) ErrorMessages(v ...string) (r *VFileInputBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-icon.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import h "github.com/theplant/htmlgo"
4 |
5 | func VIcon(name string) (r *VIconBuilder) {
6 | r = &VIconBuilder{tag: h.Tag("v-icon")}
7 | r.Icon(name)
8 | return
9 | }
10 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-list-item.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VListItemBuilder) Slot(v string) (r *VListItemBuilder) {
4 | b.tag.Attr("slot", v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-option-item.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | type DefaultOptionItem struct {
4 | Text string `json:"text"`
5 | Value string `json:"value"`
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-radio-group.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VRadioGroupBuilder) ErrorMessages(v ...string) (r *VRadioGroupBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
8 | func (b *VRadioGroupBuilder) Value(v interface{}) (r *VRadioGroupBuilder) {
9 | b.ModelValue(v)
10 | return b
11 | }
12 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-select.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import (
4 | h "github.com/theplant/htmlgo"
5 | )
6 |
7 | func VSelect(children ...h.HTMLComponent) (r *VSelectBuilder) {
8 | r = &VSelectBuilder{
9 | tag: h.Tag("v-select").Children(children...),
10 | }
11 | return
12 | }
13 |
14 | func (b *VSelectBuilder) ErrorMessages(v ...string) (r *VSelectBuilder) {
15 | SetErrorMessages(b.tag, v)
16 | return b
17 | }
18 |
19 | func (b *VSelectBuilder) Value(v interface{}) (r *VSelectBuilder) {
20 | b.ModelValue(v)
21 | return b
22 | }
23 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-slider.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VSliderBuilder) ErrorMessages(v ...string) (r *VSliderBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-switch.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VSwitchBuilder) ErrorMessages(v ...string) (r *VSwitchBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-table.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import h "github.com/theplant/htmlgo"
4 |
5 | func VTable(children ...h.HTMLComponent) (r *VTableBuilder) {
6 | r = &VTableBuilder{
7 | tag: h.Tag("v-table").Children(
8 | h.Template(
9 | children...,
10 | ).Attr("#default", true),
11 | ),
12 | }
13 | return
14 | }
15 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-text-field.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VTextFieldBuilder) ErrorMessages(v ...string) (r *VTextFieldBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-textarea.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VTextareaBuilder) ErrorMessages(v ...string) (r *VTextareaBuilder) {
4 | SetErrorMessages(b.tag, v)
5 | return b
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-toolbar-title.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import h "github.com/theplant/htmlgo"
4 |
5 | func VToolbarTitle(text string) (r *VToolbarTitleBuilder) {
6 | r = &VToolbarTitleBuilder{
7 | tag: h.Tag("v-toolbar-title").Text(text),
8 | }
9 | return
10 | }
11 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-toolbar.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | func (b *VToolbarBuilder) AutoHeight(v bool) (r *VToolbarBuilder) {
4 | if v {
5 | b.tag.Attr(":height", `"auto"`)
6 | } else {
7 | b.tag.Attr(":height", ``)
8 | }
9 | return b
10 | }
11 |
--------------------------------------------------------------------------------
/ui/vuetify/fix-utils.go:
--------------------------------------------------------------------------------
1 | package vuetify
2 |
3 | import (
4 | "strings"
5 |
6 | h "github.com/theplant/htmlgo"
7 | )
8 |
9 | func stringsTrim(vs ...string) (r []string) {
10 | for _, v := range vs {
11 | if cv := strings.TrimSpace(v); len(cv) > 0 {
12 | r = append(r, cv)
13 | }
14 | }
15 | return
16 | }
17 |
18 | func SetErrorMessages(t h.MutableAttrHTMLComponent, vs []string) {
19 | cvs := stringsTrim(vs...)
20 | if len(cvs) == 0 {
21 | return
22 | }
23 | t.SetAttr(":error-messages", h.JSONString(cvs))
24 | }
25 |
--------------------------------------------------------------------------------
/ui/vuetify/gen_vuetify.sh:
--------------------------------------------------------------------------------
1 | API=/opt/homebrew/lib/node_modules/vuetify/dist/json/web-types.json
2 | npm -g install vuetify@3.6.14
3 | find *.go | grep -v "fix-" | xargs rm
4 | cat $API | vuetifyapi2go -comp=all
5 |
--------------------------------------------------------------------------------
/ui/vuetifyx/build.sh:
--------------------------------------------------------------------------------
1 | CUR=$(pwd)/$(dirname $0)
2 |
3 | if test "$1" = 'clean'; then
4 | echo "Removing node_modules"
5 | rm -rf ./vuetifyxjs/node_modules/
6 | fi
7 |
8 | rm -r $CUR/vuetifyxjs/dist
9 | echo "Building vuetifyjs"
10 | cd $CUR/vuetifyxjs && pnpm install && pnpm format && pnpm run build
11 |
--------------------------------------------------------------------------------
/ui/vuetifyx/message-listener.go:
--------------------------------------------------------------------------------
1 | package vuetifyx
2 |
3 | import (
4 | "context"
5 |
6 | h "github.com/theplant/htmlgo"
7 | )
8 |
9 | type VXMessageListenerBuilder struct {
10 | tag *h.HTMLTagBuilder
11 | listenFunc string
12 | }
13 |
14 | func VXMessageListener() (r *VXMessageListenerBuilder) {
15 | r = &VXMessageListenerBuilder{
16 | tag: h.Tag("vx-messagelistener"),
17 | }
18 | return
19 | }
20 |
21 | func (b *VXMessageListenerBuilder) ListenFunc(v string) (r *VXMessageListenerBuilder) {
22 | b.listenFunc = v
23 | return b
24 | }
25 |
26 | func (b *VXMessageListenerBuilder) MarshalHTML(ctx context.Context) (r []byte, err error) {
27 | if b.listenFunc != "" {
28 | b.tag.Attr(":listen-func", b.listenFunc)
29 | }
30 |
31 | return b.tag.MarshalHTML(ctx)
32 | }
33 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 | not ie 11
5 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 |
4 | # local env files
5 | .env.local
6 | .env.*.local
7 |
8 | # Log files
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | pnpm-debug.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
23 | docs/.vitepress/cache/**/*
24 | docs/.vitepress/dist
25 | docs/.vitepress/.temp
26 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/prettierrc",
3 | "semi": false,
4 | "tabWidth": 2,
5 | "singleQuote": true,
6 | "printWidth": 100,
7 | "trailingComma": "none"
8 | }
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.eot
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.ttf
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.woff
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/dist/assets/materialdesignicons-webfont.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | // theme/index.ts
2 | // import DefaultTheme from 'vitepress/theme';
3 | import theme from './theme-default/theme.ts'
4 | import { registerComponents } from './register-components.ts'
5 | import { h } from 'vue'
6 | import myLayout from './myLayout'
7 |
8 | export default {
9 | // ...DefaultTheme,
10 | ...theme,
11 | Layout: () => {
12 | return h(myLayout)
13 | },
14 | enhanceApp({app}) {
15 | registerComponents(app)
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/myLayout.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent, h } from 'vue'
2 | import theme from './theme-default/theme'
3 |
4 | import './theme-default/custom.scss'
5 | const { Layout } = theme
6 | export default defineComponent({
7 | async mounted() {
8 | // 黑暗模式设配
9 | // const { default: theme } = await import('../../../promiseui/theme')
10 | const toggleTheme = (isDark: boolean) => {
11 | // if (!isDark) {
12 | // theme.use('light')
13 | // } else {
14 | // theme.use('dark')
15 | // }
16 | }
17 | const observer = new MutationObserver((entries) => {
18 | entries.forEach((mutation) => {
19 | const target = mutation.target as HTMLHtmlElement
20 | toggleTheme(target.classList.contains('dark'))
21 | })
22 | })
23 | observer.observe(document.documentElement, {
24 | attributes: true,
25 | attributeFilter: ['class']
26 | })
27 | toggleTheme(document.documentElement.classList.contains('dark'))
28 | },
29 | render() {
30 | return h(Layout)
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/register-components.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 | import DemoPreview, { useComponents } from '@vitepress-code-preview/container'
3 | import '@vitepress-code-preview/container/dist/style.css'
4 | import { registerPlugins } from '@/lib/plugins'
5 |
6 | export function registerComponents(app: App) {
7 | app.use(registerPlugins)
8 | useComponents(app, DemoPreview)
9 | }
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPBackdrop.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
42 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPDocAside.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
47 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPDocAsideCarbonAds.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPDocAsideSponsors.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPHomeContent.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
56 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPHomeFeatures.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPHomeHero.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPImage.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
47 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPLink.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPMenuGroup.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
18 |
19 |
20 |
48 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPMenuLink.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
25 |
26 |
27 |
55 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavBarAppearance.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
17 |
18 |
19 |
20 |
21 |
33 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavBarMenu.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
28 |
29 |
30 |
41 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavBarMenuGroup.vue:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 |
42 |
43 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavBarMenuLink.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
30 |
31 |
32 |
33 |
34 |
54 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavBarSocialLinks.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
14 |
15 |
16 |
28 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavBarTranslations.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
18 |
19 |
{{ currentLang.label }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
48 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavScreenAppearance.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
17 |
18 | {{ theme.darkModeSwitchLabel || 'Appearance' }}
19 |
20 |
21 |
22 |
23 |
24 |
41 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavScreenMenu.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
26 |
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavScreenMenuGroupLink.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
23 |
24 |
25 |
40 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavScreenMenuGroupSection.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
20 |
21 |
22 |
35 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavScreenMenuLink.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
22 |
23 |
24 |
40 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPNavScreenSocialLinks.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPSidebarGroup.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
57 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPSocialLink.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
26 |
27 |
28 |
51 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPSocialLinks.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
28 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPSponsors.vue:
--------------------------------------------------------------------------------
1 |
36 |
37 |
38 |
48 |
49 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPSponsorsGrid.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
48 |
49 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPSwitchAppearance.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
55 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPTeamPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
59 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/VPTeamPageTitle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
64 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconAlignJustify.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconAlignLeft.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconAlignRight.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconArrowLeft.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconArrowRight.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconChevronDown.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconChevronLeft.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconChevronRight.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconChevronUp.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconHeart.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconLanguages.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconMinus.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconMinusSquare.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconMoon.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconMoreHorizontal.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconPlus.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconPlusSquare.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/components/icons/VPIconSun.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/composables/aside.js:
--------------------------------------------------------------------------------
1 | import { useMediaQuery } from '@vueuse/core';
2 | import { computed } from 'vue';
3 | import { useSidebar } from './sidebar';
4 | export function useAside() {
5 | const { hasSidebar } = useSidebar();
6 | const is960 = useMediaQuery('(min-width: 960px)');
7 | const is1280 = useMediaQuery('(min-width: 1280px)');
8 | const isAsideEnabled = computed(() => {
9 | if (!is1280.value && !is960.value) {
10 | return false;
11 | }
12 | return hasSidebar.value ? is1280.value : is960.value;
13 | });
14 | return {
15 | isAsideEnabled
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/composables/data.js:
--------------------------------------------------------------------------------
1 | import { useData as useData$ } from 'vitepress';
2 | export const useData = useData$;
3 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/composables/edit-link.js:
--------------------------------------------------------------------------------
1 | import { computed } from 'vue';
2 | import { useData } from './data';
3 | export function useEditLink() {
4 | const { theme, page } = useData();
5 | return computed(() => {
6 | const { text = 'Edit this page', pattern = '' } = theme.value.editLink || {};
7 | let url;
8 | if (typeof pattern === 'function') {
9 | url = pattern(page.value);
10 | }
11 | else {
12 | url = pattern.replace(/:path/g, page.value.filePath);
13 | }
14 | return { url, text };
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/composables/langs.js:
--------------------------------------------------------------------------------
1 | import { computed } from 'vue';
2 | import { ensureStartingSlash } from '../support/utils';
3 | import { useData } from './data';
4 | export function useLangs({ correspondingLink = false } = {}) {
5 | const { site, localeIndex, page, theme, hash } = useData();
6 | const currentLang = computed(() => ({
7 | label: site.value.locales[localeIndex.value]?.label,
8 | link: site.value.locales[localeIndex.value]?.link ||
9 | (localeIndex.value === 'root' ? '/' : `/${localeIndex.value}/`)
10 | }));
11 | const localeLinks = computed(() => Object.entries(site.value.locales).flatMap(([key, value]) => currentLang.value.label === value.label
12 | ? []
13 | : {
14 | text: value.label,
15 | link: normalizeLink(value.link || (key === 'root' ? '/' : `/${key}/`), theme.value.i18nRouting !== false && correspondingLink, page.value.relativePath.slice(currentLang.value.link.length - 1), !site.value.cleanUrls) + hash.value
16 | }));
17 | return { localeLinks, currentLang };
18 | }
19 | function normalizeLink(link, addPath, path, addExt) {
20 | return addPath
21 | ? link.replace(/\/$/, '') +
22 | ensureStartingSlash(path
23 | .replace(/(^|\/)index\.md$/, '$1')
24 | .replace(/\.md$/, addExt ? '.html' : ''))
25 | : link;
26 | }
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/composables/local-nav.js:
--------------------------------------------------------------------------------
1 | import { onContentUpdated } from 'vitepress';
2 | import { computed, shallowRef } from 'vue';
3 | import { getHeaders } from '../composables/outline';
4 | import { useData } from './data';
5 | export function useLocalNav() {
6 | const { theme, frontmatter } = useData();
7 | const headers = shallowRef([]);
8 | const hasLocalNav = computed(() => {
9 | return headers.value.length > 0;
10 | });
11 | onContentUpdated(() => {
12 | headers.value = getHeaders(frontmatter.value.outline ?? theme.value.outline);
13 | });
14 | return {
15 | headers,
16 | hasLocalNav
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/composables/nav.js:
--------------------------------------------------------------------------------
1 | import { ref, watch } from 'vue';
2 | import { useRoute } from 'vitepress';
3 | export function useNav() {
4 | const isScreenOpen = ref(false);
5 | function openScreen() {
6 | isScreenOpen.value = true;
7 | window.addEventListener('resize', closeScreenOnTabletWindow);
8 | }
9 | function closeScreen() {
10 | isScreenOpen.value = false;
11 | window.removeEventListener('resize', closeScreenOnTabletWindow);
12 | }
13 | function toggleScreen() {
14 | isScreenOpen.value ? closeScreen() : openScreen();
15 | }
16 | /**
17 | * Close screen when the user resizes the window wider than tablet size.
18 | */
19 | function closeScreenOnTabletWindow() {
20 | window.outerWidth >= 768 && closeScreen();
21 | }
22 | const route = useRoute();
23 | watch(() => route.path, closeScreen);
24 | return {
25 | isScreenOpen,
26 | openScreen,
27 | closeScreen,
28 | toggleScreen
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-cyrillic-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-cyrillic-ext.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-cyrillic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-cyrillic.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-greek-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-greek-ext.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-greek.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-greek.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-latin-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-latin-ext.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-latin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-latin.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-vietnamese.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-italic-vietnamese.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-cyrillic-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-cyrillic-ext.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-cyrillic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-cyrillic.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-greek-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-greek-ext.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-greek.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-greek.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-latin-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-latin-ext.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-latin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-latin.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-vietnamese.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/fonts/inter-roman-vietnamese.woff2
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/index.js:
--------------------------------------------------------------------------------
1 | import './styles/fonts.css';
2 | export * from '../without-fonts';
3 | export { default as default } from '../without-fonts';
4 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/styles/components/vp-code.css:
--------------------------------------------------------------------------------
1 | .dark .vp-code span {
2 | color: var(--shiki-dark, inherit);
3 | }
4 |
5 | html:not(.dark) .vp-code span {
6 | color: var(--shiki-light, inherit);
7 | }
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/styles/utils.css:
--------------------------------------------------------------------------------
1 | .visually-hidden {
2 | position: absolute;
3 | width: 1px;
4 | height: 1px;
5 | white-space: nowrap;
6 | clip: rect(0 0 0 0);
7 | clip-path: inset(50%);
8 | overflow: hidden;
9 | }
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/support/lru.js:
--------------------------------------------------------------------------------
1 | // adapted from https://stackoverflow.com/a/46432113/11613622
2 | export class LRUCache {
3 | max;
4 | cache;
5 | constructor(max = 10) {
6 | this.max = max;
7 | this.cache = new Map();
8 | }
9 | get(key) {
10 | let item = this.cache.get(key);
11 | if (item !== undefined) {
12 | // refresh key
13 | this.cache.delete(key);
14 | this.cache.set(key, item);
15 | }
16 | return item;
17 | }
18 | set(key, val) {
19 | // refresh key
20 | if (this.cache.has(key))
21 | this.cache.delete(key);
22 | // evict oldest
23 | else if (this.cache.size === this.max)
24 | this.cache.delete(this.first());
25 | this.cache.set(key, val);
26 | }
27 | first() {
28 | return this.cache.keys().next().value;
29 | }
30 | clear() {
31 | this.cache.clear();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/support/utils.js:
--------------------------------------------------------------------------------
1 | import { withBase } from 'vitepress';
2 | import { useData } from '../composables/data';
3 | import { isExternal, treatAsHtml } from '../../shared';
4 | export function throttleAndDebounce(fn, delay) {
5 | let timeoutId;
6 | let called = false;
7 | return () => {
8 | if (timeoutId)
9 | clearTimeout(timeoutId);
10 | if (!called) {
11 | fn();
12 | (called = true) && setTimeout(() => (called = false), delay);
13 | }
14 | else
15 | timeoutId = setTimeout(fn, delay);
16 | };
17 | }
18 | export function ensureStartingSlash(path) {
19 | return /^\//.test(path) ? path : `/${path}`;
20 | }
21 | export function normalizeLink(url) {
22 | const { pathname, search, hash, protocol } = new URL(url, 'http://a.com');
23 | if (isExternal(url) ||
24 | url.startsWith('#') ||
25 | !protocol.startsWith('http') ||
26 | !treatAsHtml(pathname))
27 | return url;
28 | const { site } = useData();
29 | const normalizedPath = pathname.endsWith('/') || pathname.endsWith('.html')
30 | ? url
31 | : url.replace(/(?:(^\.+)\/)?.*$/, `$1${pathname.replace(/(\.md)?$/, site.value.cleanUrls ? '' : '.html')}${search}${hash}`);
32 | return withBase(normalizedPath);
33 | }
34 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/.vitepress/theme/theme-default/theme.ts:
--------------------------------------------------------------------------------
1 | import './styles/icons.css'
2 | import './styles/fonts.css'
3 | import './styles/vars.css'
4 | import './styles/base.css'
5 | import './styles/utils.css'
6 | import './styles/components/custom-block.css'
7 | import './styles/components/vp-code.css'
8 | import './styles/components/vp-doc.css';
9 |
10 | import './styles/components/vp-sponsor.css'
11 | import Layout from './Layout.vue'
12 | import NotFound from './NotFound.vue'
13 | export { default as VPHomeHero } from './components/VPHomeHero.vue'
14 | export { default as VPHomeFeatures } from './components/VPHomeFeatures.vue'
15 | export { default as VPHomeSponsors } from './components/VPHomeSponsors.vue'
16 | export { default as VPDocAsideSponsors } from './components/VPDocAsideSponsors.vue'
17 | export { default as VPTeamPage } from './components/VPTeamPage.vue'
18 | export { default as VPTeamPageTitle } from './components/VPTeamPageTitle.vue'
19 | export { default as VPTeamPageSection } from './components/VPTeamPageSection.vue'
20 | export { default as VPTeamMembers } from './components/VPTeamMembers.vue'
21 | // make vue i18n happy
22 | if (typeof globalThis !== 'undefined') {
23 | globalThis.__VUE_PROD_DEVTOOLS__ = false;
24 | } else {
25 | window.__VUE_PROD_DEVTOOLS__ = false;
26 | }
27 |
28 | const theme = {
29 | Layout,
30 | NotFound
31 | }
32 | export default theme
33 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/Components/ScrollIframe/index.md:
--------------------------------------------------------------------------------
1 | # vx-scroll-iframe 输入框
2 |
3 | ## 基本用法
4 |
5 | :::demo
6 |
7 | ```vue
8 |
31 |
32 |
33 |
39 | add after test1
40 | add after test2
41 | removeVirtual
42 | append
43 |
44 |
45 |
46 |
47 |
48 | ```
49 | :::
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/Components/VXField/sendVariables.md:
--------------------------------------------------------------------------------
1 | # vx-send-variables 输入框
2 |
3 | ## 基本用法
4 |
5 | :::demo
6 |
7 | ```vue
8 |
14 |
15 |
16 |
17 |
18 |
19 | Test
20 |
21 |
28 |
29 |
37 |
38 | Value: {{value}}
39 |
40 |
41 |
42 | ```
43 | :::
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/Components/VXModelConverter/index.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/Components/VXToolbar/index.md:
--------------------------------------------------------------------------------
1 | # vx-toolbar
2 |
3 | 这是一个基本示例,你可以用 `markdown` 语法 和 `vue3`、`vuetify` 在此处写任何组件代码
4 |
5 | ## 基本用法
6 |
7 | ```vue
8 |
9 | ```
10 |
11 | :::demo
12 |
13 | ```vue
14 |
15 | 默认配置:
16 |
17 |
18 | ```
19 |
20 |
21 | :::
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | hero:
5 | name: VuetifyX
6 | text: 一个基于vuetify的企业级组件库
7 | # tagline: 主题可调,使用 TypeScript,交互式Demo
8 | actions:
9 | - theme: alt
10 | text: 快速开始
11 | link: /Components/VXField
12 | - theme: alt
13 | text: View on GitHub
14 | link: https://github.com/qor5/x/tree/master/ui/vuetifyx
15 | features:
16 | # - icon: 🔑
17 | # title: 使用 Typescript
18 | # details: 更好的代码提示
19 | # - icon: ⚡
20 | # title: 按需引入
21 | # details: 支持按需引入
22 | # - icon: 🎁
23 | # title: 组件精美
24 | # details: 还算好看
25 |
26 | # - icon: 🌙
27 | # title: 黑暗模式
28 | # details: 支持黑暗模式
29 | # - icon: 🎨
30 | # title: 主题化
31 | # details: 支持自定义定制主题
32 | - icon: 🛠️
33 | title: 交互式
34 | details: 组件文档采用交互式,可在线编辑实时
35 | ---
36 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | import { postcssIsolateStyles } from 'vitepress'
2 |
3 | export default {
4 | plugins: [postcssIsolateStyles({
5 | includeFiles: [/vp-doc\.css/]
6 | })]
7 | }
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/public/imgs/vx-avatar-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/docs/public/imgs/vx-avatar-example.png
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/public/logo.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/docs/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import { fileURLToPath, URL } from 'node:url'
3 | import { viteDemoPreviewPlugin } from '@vitepress-code-preview/plugin'
4 | import { UserConfig } from 'vitepress';
5 |
6 | export default defineConfig({
7 | plugins: [
8 | viteDemoPreviewPlugin()
9 | ],
10 | resolve: {
11 | alias: {
12 | '@': fileURLToPath(new URL('../src', import.meta.url))
13 | }
14 | }
15 | } as UserConfig)
16 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Welcome to Vuetify 3
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/patches/@vitepress-code-preview__container.patch:
--------------------------------------------------------------------------------
1 | diff --git a/dist/index.js b/dist/index.js
2 | index ab3dd78e9a373861d0265487fe1cd6ecc6d568ca..15d1733fc89c6e34623c5804387b8dad3d8ddf06 100644
3 | --- a/dist/index.js
4 | +++ b/dist/index.js
5 | @@ -1643,7 +1643,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
6 | };
7 | }
8 | });
9 | -const example = "_example_8jhae_2";
10 | +const example = "_example_8jhae_2 vp-raw";
11 | const style0 = {
12 | example,
13 | "example-showcase": "_example-showcase_8jhae_14",
14 | diff --git a/dist/index.umd.cjs b/dist/index.umd.cjs
15 | index f33062b5245d5a6ac6ff1db0af7ea0072631eb74..a4660855d149f7aa55f7370547cd535acc6130d6 100644
16 | --- a/dist/index.umd.cjs
17 | +++ b/dist/index.umd.cjs
18 | @@ -1646,7 +1646,7 @@
19 | };
20 | }
21 | });
22 | - const example = "_example_8jhae_2";
23 | + const example = "_example_8jhae_2 vp-raw";
24 | const style0 = {
25 | example,
26 | "example-showcase": "_example-showcase_8jhae_14",
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qor5/x/e8fdf202ebcfb62a51cd5d9a83c761dfa36902e5/ui/vuetifyx/vuetifyxjs/public/favicon.ico
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Chart/useVxChartMergeOpts.ts:
--------------------------------------------------------------------------------
1 | import { ChartOptions } from './presets.config'
2 |
3 | export function useVxChartMergeOptsCallback(props: any) {
4 | const invokeMergeOptionsCallback = (
5 | options: ChartOptions,
6 | mergeCallbackOptions: { seriesData: any[] }
7 | ) => {
8 | if (props.mergeOptionsCallback) {
9 | props.mergeOptionsCallback(options, mergeCallbackOptions)
10 | }
11 | }
12 |
13 | return {
14 | invokeMergeOptionsCallback
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Common/VXToolBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
19 |
20 |
34 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/Constants.ts:
--------------------------------------------------------------------------------
1 | export const ModifierBetween = 'between'
2 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/AutoCompleteItem.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/DateItem.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/DatePickerItem.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/DateRangePickerItem.vue:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/DatetimeRangePickerItem.vue:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/FilterButton.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/LinkageSelectItem.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/LinkageSelectItemRemote.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/MultipleSelectItem.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Filter/components/SelectItem.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Form/VXSegmentForm/type.d.ts:
--------------------------------------------------------------------------------
1 | export type ConditionType = 'intersect' | 'union'
2 |
3 | export type TagType = {
4 | tag: {
5 | builderID: string
6 | params: Record
7 | values: string[]
8 | }
9 | }
10 |
11 | export type ConditionItemType =
12 | | TagType
13 | | {
14 | [key in ConditionType]?: ConditionItemType[]
15 | }
16 |
17 | export type SavedFormType = {
18 | [key in ConditionType]?: ConditionItemType[]
19 | }
20 |
21 | export type OptionsType = {
22 | id: string
23 | name: string
24 | description: string
25 | builders: BuilderType[]
26 | }
27 |
28 | export type BuilderType = {
29 | id: string
30 | name: string
31 | description: string
32 | categoryID: string
33 | view: ViewType
34 | }
35 |
36 | export type ViewType = {
37 | fragments: FragmentType[]
38 | }
39 |
40 | export type FragmentType = {
41 | defaultValue: string
42 | key: string
43 | multiple: boolean
44 | options: OptionType[]
45 | required: boolean
46 | skipIf: null | SkipType
47 | skipUnless: null | SkipType
48 | type: 'SELECT' | 'DATE_PICKER' | 'NUMBER' | 'NUMBER_INPUT' | 'TEXT'
49 | validation: null | string
50 | }
51 |
52 | export type SkipType = {
53 | [`$${string & K}`]: {
54 | [key: string]: string[]
55 | }
56 | }
57 |
58 | export type OptionType = {
59 | label: string
60 | value: string
61 | }
62 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/Helpers.tsx:
--------------------------------------------------------------------------------
1 | import { VNode, h } from 'vue'
2 |
3 | export const Core = {
4 | props: {
5 | fieldName: String,
6 | loadPageWithArrayOp: Boolean
7 | }
8 | }
9 |
10 | export const SelectedItems = {
11 | props: {
12 | selectedItems: {
13 | type: Array,
14 | default: () => []
15 | } as any,
16 | multiple: Boolean
17 | }
18 | }
19 |
20 | interface Slots {
21 | [key: string]: VNode[] | undefined
22 | }
23 |
24 | export const slotTemplates = (slots: Slots): VNode[] => {
25 | const templates: VNode[] = []
26 |
27 | for (const name in slots) {
28 | if (!Object.getOwnPropertyDescriptor(slots, name)) {
29 | continue
30 | }
31 | templates.push(h(' {slots[name]} < /template>'))
32 | }
33 | return templates
34 | }
35 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/MessageListener.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/RestoreScrollListener.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/SendVariables.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/TiptapEditor/Extensions/CallbackActionButton.ts:
--------------------------------------------------------------------------------
1 | import type { GeneralOptions } from 'vuetify-pro-tiptap'
2 | import { Extension } from '@tiptap/core'
3 |
4 | import CallbackActionButton from './CallbackActionButton.vue'
5 |
6 | export type CallbackOptions = GeneralOptions
7 |
8 | export default Extension.create({
9 | name: 'callback',
10 | addOptions() {
11 | return {
12 | divider: false,
13 | spacer: false,
14 | button: ({ editor, extension, t }) => {
15 | return {
16 | component: CallbackActionButton,
17 | componentProps: {
18 | editor: editor,
19 | extension: extension,
20 | t: t
21 | }
22 | }
23 | }
24 | }
25 | }
26 | })
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/TiptapEditor/Extensions/ImageGlue.vue:
--------------------------------------------------------------------------------
1 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/Datepicker.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest'
2 |
3 | import Datepicker from '../Datepicker.vue'
4 | import { mountTemplate } from '@/lib/__tests__/testutils'
5 | import { nextTick, ref, watch } from 'vue'
6 | import { flushPromises } from '@vue/test-utils'
7 |
8 | it('Datepicker modelValue', async () => {
9 | const wrapper = mountTemplate(Datepicker, {
10 | modelValue: '2023-10-01'
11 | })
12 | await nextTick()
13 | await flushPromises()
14 | expect(wrapper.find('input').element.value).toContain('2023-10-01')
15 | expect(wrapper.find('input').element.value).not.toContain('2023-10-02')
16 | })
17 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/Datetimepicker.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest'
2 |
3 | import Datetimepicker from '../Datetimepicker.vue'
4 | import { mountTemplate } from '@/lib/__tests__/testutils'
5 | import { nextTick, watch } from 'vue'
6 | import { flushPromises } from '@vue/test-utils'
7 |
8 | it('Datetimepicker modelValue', async () => {
9 | const wrapper = mountTemplate(Datetimepicker, {
10 | modelValue: '2023-10-01 22:11'
11 | })
12 | await nextTick()
13 | await flushPromises()
14 | expect(wrapper.find('input').element.value).toContain('2023-10-01 22:11')
15 | expect(wrapper.find('input').element.value).not.toContain('2023-10-01 22:22')
16 | })
17 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/Filter.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest'
2 |
3 | import Filter from '../Filter/index.vue'
4 | import { mountTemplate } from '@/lib/__tests__/testutils'
5 | import { nextTick, ref, watch } from 'vue'
6 |
7 | it('Filter modelValue', async () => {
8 | const internalValue = [
9 | {
10 | key: 'TestStringItem',
11 | label: 'TestStringItem',
12 | itemType: 'StringItem',
13 | selected: true,
14 | folded: true,
15 | valueIs: 'active'
16 | }
17 | ]
18 | const value = ref('')
19 | const wrapper = mountTemplate(Filter, {
20 | internalValue: internalValue,
21 | modelValue: value
22 | })
23 | await nextTick()
24 | expect(wrapper.html()).contain('active')
25 | expect(wrapper.html()).not.contain('deactivate')
26 | })
27 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/SelectMany.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest'
2 |
3 | import SelectMany from '../SelectMany.vue'
4 | import { mountTemplate } from '@/lib/__tests__/testutils'
5 | import { nextTick, watch } from 'vue'
6 |
7 | it('SelectMany modelValue', async () => {
8 | const wrapper = mountTemplate(SelectMany, {
9 | items: [
10 | {
11 | id: '1',
12 | text: 'ScanDa Adams',
13 | image: 'https://cdn.vuetifyjs.com/images/lists/1.jpg'
14 | },
15 | {
16 | id: '2',
17 | text: 'Ali Connors',
18 | image: 'https://cdn.vuetifyjs.com/images/lists/2.jpg'
19 | },
20 | {
21 | id: '3',
22 | text: 'Ali DE',
23 | image: 'https://cdn.vuetifyjs.com/images/lists/3.jpg'
24 | },
25 | {
26 | id: '4',
27 | text: 'Bogn',
28 | image: 'https://cdn.vuetifyjs.com/images/lists/4.jpg'
29 | }
30 | ],
31 | modelValue: ['1', '2']
32 | })
33 | await nextTick()
34 | expect(wrapper.html()).toContain('ScanDa Adams')
35 | expect(wrapper.html()).toContain('Ali Connors')
36 | expect(wrapper.html()).not.toContain('Bogn')
37 | })
38 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/TextDatepicker.spec.ts:
--------------------------------------------------------------------------------
1 | import { it, expect } from 'vitest'
2 |
3 | import TextDatepicker from '../TextDatepicker.vue'
4 | import { mountTemplate } from '@/lib/__tests__/testutils'
5 | import { nextTick } from 'vue'
6 |
7 | it('Datetimepicker modelValue', async () => {
8 | const wrapper = mountTemplate(TextDatepicker, {
9 | modelValue: '2023-10-01'
10 | })
11 | await nextTick()
12 | expect(wrapper.find('input').element.value).toContain('2023-10-01')
13 | expect(wrapper.find('input').element.value).not.toContain('2023-10-02')
14 | })
15 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/handler.ts:
--------------------------------------------------------------------------------
1 | import { http, HttpResponse } from 'msw'
2 |
3 | export const autocompleteHandle = [
4 | http.get('/autocomplete', () => {
5 | return HttpResponse.json({
6 | data: [
7 | {
8 | id: 1,
9 | title: '老张头'
10 | },
11 | {
12 | id: 2,
13 | title: '老李头'
14 | },
15 | {
16 | id: 3,
17 | title: '老赵头'
18 | },
19 | {
20 | id: 4,
21 | title: '老钱头'
22 | },
23 | {
24 | id: 5,
25 | title: '老孙头'
26 | },
27 | {
28 | id: 6,
29 | title: '老周头'
30 | }
31 | ],
32 | total: 6,
33 | pages: 0,
34 | current: 0
35 | })
36 | })
37 | ]
38 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/__tests__/testutils.ts:
--------------------------------------------------------------------------------
1 | import { mount, VueWrapper } from '@vue/test-utils'
2 | import { Component } from 'vue'
3 | import { createVuetify } from 'vuetify'
4 | import * as components from 'vuetify/components'
5 | import * as directives from 'vuetify/directives'
6 |
7 | const vuetify = createVuetify({
8 | components,
9 | directives
10 | })
11 |
12 | global.ResizeObserver = require('resize-observer-polyfill')
13 |
14 | export function mountTemplate(component: Component, props: {}): VueWrapper {
15 | return mount(component, {
16 | props: {
17 | ...props
18 | },
19 | global: {
20 | plugins: [vuetify]
21 | }
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/composables/useBindingValue.ts:
--------------------------------------------------------------------------------
1 | import { ref, watch } from 'vue'
2 |
3 | export default function useBindingValue(
4 | props: Record,
5 | emit: EmitFnType,
6 | modelValueFormatter?: (value: any) => any
7 | ) {
8 | const bindingValue = ref(
9 | modelValueFormatter ? modelValueFormatter(props.modelValue) : props.modelValue
10 | )
11 | const bindingFocus = ref(false)
12 |
13 | function onUpdateModelValue(value: any) {
14 | ;(emit as any)('update:modelValue', value)
15 | bindingValue.value = value
16 | }
17 |
18 | function onUpdateFocused(value: boolean) {
19 | ;(emit as any)('update:focused', value)
20 | bindingFocus.value = value
21 | }
22 |
23 | watch(
24 | () => props.modelValue,
25 | (newVal) => {
26 | bindingValue.value = modelValueFormatter ? modelValueFormatter(newVal) : newVal
27 | }
28 | )
29 |
30 | return { bindingValue, bindingFocus, onUpdateModelValue, onUpdateFocused }
31 | }
32 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/composables/useColor.ts:
--------------------------------------------------------------------------------
1 | export function useColor(colorString: string) {
2 | const isRGBorHexColor = (colorStr: string) => /rgb|#/.test(colorStr)
3 |
4 | return {
5 | color: isRGBorHexColor(colorString) ? colorString : `rgb(var(--v-theme-${colorString}))`
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/composables/useFilteredAttrs.ts:
--------------------------------------------------------------------------------
1 | import { computed, useAttrs } from 'vue'
2 |
3 | interface FilteredAttrs {
4 | attrs: Record
5 | filteredAttrs: Record
6 | rootAttrs: Record
7 | }
8 |
9 | /**
10 | * class and id are supposed to bind to root element,
11 | * other props should bind to child components
12 | */
13 | export function useFilteredAttrs(): FilteredAttrs {
14 | const attrs = useAttrs()
15 |
16 | // filter class and id, should not binding to components props
17 | const filteredAttrs = computed(() => {
18 | const { class: _class, id: _id, style: _style, ...rest } = attrs
19 | return rest
20 | })
21 |
22 | const rootAttrs = computed(() => {
23 | const { class: _class, id: _id, style: _style } = attrs
24 | return { class: _class, id: _id, style: _style }
25 | })
26 |
27 | return {
28 | attrs,
29 | filteredAttrs,
30 | rootAttrs
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/composables/useVDatePicker.ts:
--------------------------------------------------------------------------------
1 | import { ref, computed, Ref } from 'vue'
2 | import dayjs from 'dayjs'
3 |
4 | export function useVDatePickerTimeChange(dateOfPicker: Ref) {
5 | const displayedMonth = ref()
6 | const displayedYear = ref()
7 |
8 | function setDisplayedYearAndMonth(timeStamp?: string | number | Date) {
9 | let year: number, month: number
10 |
11 | if (!timeStamp) {
12 | year = new Date().getFullYear()
13 | month = new Date().getMonth()
14 | } else {
15 | ;[year, month] = dayjs(timeStamp).format('YYYY-MM').split('-').map(Number)
16 | month = month - 1
17 | }
18 |
19 | displayedYear.value = year
20 | displayedMonth.value = month
21 | }
22 |
23 | function onYearOrMonthChange(value: number | unknown, type: 'year' | 'month' | 'modelValue') {
24 | if (type === 'modelValue') {
25 | // console.log('modelValue', value)
26 | return
27 | }
28 | dateOfPicker.value = null
29 | }
30 |
31 | const valueChangedWithoutSaved = computed(() => {
32 | return dateOfPicker.value === null
33 | })
34 |
35 | return {
36 | displayedMonth,
37 | displayedYear,
38 | setDisplayedYearAndMonth,
39 | onYearOrMonthChange,
40 | valueChangedWithoutSaved
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/icons/checkbox-filled-outline.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/icons/checkbox-on-filled-outline.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/main.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 | import { registerPlugins, registerVuetify2Window } from '@/lib/plugins'
3 |
4 | declare const window: any
5 |
6 | // export vuetifyInstance to window, thus qor5/web/core.js can use it.
7 | registerVuetify2Window()
8 |
9 | window.__goplaidVueComponentRegisters = window.__goplaidVueComponentRegisters || []
10 | window.__goplaidVueComponentRegisters.push((app: App, vueOptions: any): any => {
11 | registerPlugins(app)
12 | })
13 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/plugins/README.md:
--------------------------------------------------------------------------------
1 | # Plugins
2 |
3 | Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally.
4 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/plugins/i18n.ts:
--------------------------------------------------------------------------------
1 | import { createI18n } from 'vue-i18n'
2 | import { zhHans, en, ja } from 'vuetify/locale'
3 |
4 | const messages = {
5 | zhHans: {
6 | $vuetify: {
7 | ...zhHans,
8 | datePicker: {
9 | ...zhHans.datePicker,
10 | title: '',
11 | header: '选择日期',
12 | okTips: '选择日期才能保存',
13 | saveBtn: '保存'
14 | }
15 | }
16 | },
17 | en: {
18 | $vuetify: {
19 | ...en,
20 | datePicker: {
21 | ...en.datePicker,
22 | title: '',
23 | header: 'Select date',
24 | okTips: 'You must select a date to save',
25 | saveBtn: 'Save'
26 | }
27 | }
28 | },
29 | ja: {
30 | $vuetify: {
31 | ...ja,
32 | datePicker: {
33 | ...ja.datePicker,
34 | title: '',
35 | header: '日付を選択',
36 | okTips: '日付を選択してから保存してください',
37 | saveBtn: 'ほぞん'
38 | }
39 | }
40 | }
41 | }
42 |
43 | export default createI18n({
44 | legacy: false, // Vuetify does not support the legacy mode of vue-i18n
45 | locale: 'en',
46 | fallbackLocale: 'en',
47 | messages
48 | })
49 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/plugins/tiptap.ts:
--------------------------------------------------------------------------------
1 | import {
2 | VuetifyTiptap,
3 | VuetifyViewer,
4 | createVuetifyProTipTap
5 | // @ts-ignore
6 | } from 'vuetify-pro-tiptap'
7 | import 'vuetify-pro-tiptap/style.css'
8 |
9 | export const vuetifyProTipTap = createVuetifyProTipTap({
10 | lang: 'en',
11 | components: {
12 | VuetifyTiptap,
13 | VuetifyViewer
14 | },
15 | extensions: []
16 | })
17 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/plugins/vuetify.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * plugins/vuetify.js
3 | *
4 | * Framework documentation: https://vuetifyjs.com`
5 | */
6 |
7 | // Styles
8 | import '@mdi/font/css/materialdesignicons.css'
9 | import 'vuetify/styles'
10 | import * as components from 'vuetify/components'
11 | import * as directives from 'vuetify/directives'
12 | import '@/lib/scss/index.scss'
13 | import { createVueI18nAdapter } from 'vuetify/locale/adapters/vue-i18n'
14 | import { useI18n } from 'vue-i18n'
15 | import i18n from './i18n'
16 |
17 | // Composables
18 | import { createVuetify } from 'vuetify'
19 | import themes from './theme'
20 |
21 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
22 | export default createVuetify({
23 | locale: {
24 | adapter: createVueI18nAdapter({ i18n, useI18n })
25 | },
26 | theme: themes,
27 | components,
28 | directives
29 | })
30 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/_fixer.scss:
--------------------------------------------------------------------------------
1 | /** fix continuous characters that never break word.**/
2 | .v-container {
3 | div,
4 | span,
5 | h1,
6 | h2,
7 | h3,
8 | h4,
9 | h5,
10 | h6 {
11 | word-break: break-word;
12 | }
13 |
14 | /** fix: version chip comp is pushed out by h1 in flex wrap **/
15 | h1 + .v-chip {
16 | flex-shrink: 0;
17 | }
18 | }
19 |
20 | a {
21 | color: rgb(var(--v-theme-primary));
22 | text-decoration: none;
23 | }
24 |
25 | .v-app-bar.v-toolbar,
26 | .v-list-item-title {
27 | font-weight: 500;
28 | }
29 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:map';
2 | @use './vuetify' as vuetify;
3 |
4 | // Usage: `@use "../mixins";` and then use `@include mixins.elevation(24);`
5 | @mixin elevation($z, $important: false) {
6 | box-shadow:
7 | map.get(vuetify.$shadow-key-umbra, $z),
8 | map.get(vuetify.$shadow-key-penumbra, $z),
9 | map.get(vuetify.$shadow-key-ambient, $z) if($important, !important, null);
10 | }
11 |
12 | // This mixin is inspired from vuetify for adding hover styles via before pseudo element
13 | @mixin before-pseudo() {
14 | position: relative;
15 |
16 | &::before {
17 | position: absolute;
18 | border-radius: inherit;
19 | background: currentcolor;
20 | block-size: 100%;
21 | content: '';
22 | inline-size: 100%;
23 | inset: 0;
24 | opacity: 0;
25 | pointer-events: none;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_ActivityTimeLine.scss:
--------------------------------------------------------------------------------
1 | .activity-timeline-wrap {
2 | display: flex;
3 | flex-direction: column;
4 | margin-bottom: 32px;
5 | width: 271px;
6 | }
7 |
8 | .v-row .v-col-12 .activity-timeline-wrap {
9 | width: 100%;
10 | }
11 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_AutoComplete.scss:
--------------------------------------------------------------------------------
1 | .page-builder-autoCmp {
2 | .v-input__control {
3 | height: 36px;
4 | --v-input-control-height: 36px;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_Dialog.scss:
--------------------------------------------------------------------------------
1 | .dialog-close-btn {
2 | .v-icon {
3 | color: rgba(var(--v-theme-grey-darken-1), var(--v-high-emphasis-opacity));
4 | }
5 | }
6 |
7 | .common-dialog {
8 | .v-card-title {
9 | text-overflow: initial;
10 | white-space: inherit;
11 | }
12 | .v-btn--size-small .v-btn__content {
13 | font-size: 12px;
14 | font-weight: 400;
15 | }
16 | }
17 |
18 | .v-dialog {
19 | // listing-comp-wrap is also displayed in detailing
20 | .listing-compo-wrap {
21 | padding-left: 16px;
22 | padding-right: 16px;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_SlideGroup.scss:
--------------------------------------------------------------------------------
1 | @use '../utils' as utils;
2 | @import '../variables';
3 | // resize button style in v-slide-group (filter compo)
4 | // plz use vx-tabs-wrap instead of v-tabs
5 | .v-slide-group:not(.vx-tabs-wrap) {
6 | .v-slide-group__content {
7 | flex: initial;
8 | border-block-end-width: thin;
9 | border-block-end-style: solid;
10 | border-block-end-color: rgba(var(--v-border-color), var(--v-border-opacity)) !important;
11 | }
12 | .v-tab.v-tab.v-btn {
13 | min-width: auto;
14 | }
15 | .v-btn.v-tab--selected {
16 | color: rgba(var(--v-theme-primary), var(--v-high-emphasis-opacity));
17 | }
18 |
19 | .v-btn {
20 | --v-theme-on-surface: #{utils.hex-to-rgb-number($color-grey-700)};
21 | color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VBtn.scss:
--------------------------------------------------------------------------------
1 | .v-btn__content {
2 | letter-spacing: 0.61px;
3 | }
4 |
5 | .v-btn--variant-elevated {
6 | box-shadow:
7 | 0px 1px 3px 0px rgba(var(--v-shadow-key-umbra-color), 0.14),
8 | 0px 2px 1px 0px rgba(var(--v-shadow-key-umbra-color), 0.12),
9 | 0px 1px 1px 0px rgba(var(--v-shadow-key-umbra-color), 0.14);
10 | }
11 |
12 | .fix-btn-icon {
13 | &.v-btn--size-x-small {
14 | .v-icon {
15 | font-size: calc(var(--v-icon-size-multiplier) * 1em);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VCard.scss:
--------------------------------------------------------------------------------
1 | @use '../variables' as variables;
2 |
3 | .v-card--variant-outlined {
4 | border-color: variables.$color-grey-300;
5 | }
6 |
7 | .v-card-actions .v-btn ~ .v-btn:not(.v-btn-toggle .v-btn) {
8 | margin-inline-start: 12px;
9 | }
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VField.scss:
--------------------------------------------------------------------------------
1 | // `density="compact"`
2 | .v-input--density-compact {
3 | --v-input-control-height: 32px;
4 | --v-field-padding-start: 12px;
5 | --v-field-input-padding-top: 4px;
6 | --v-field-input-padding-bottom: 4px;
7 | font-size: 14px;
8 |
9 | .v-field {
10 | --v-input-control-height: 40px;
11 | }
12 |
13 | .v-chip--size-small {
14 | --v-chip-height: 20px;
15 | font-size: 12px;
16 | padding: 0 8px;
17 | }
18 |
19 | .v-field__append-inner i {
20 | --v-theme-on-surface: var(--v-theme-grey-darken-2);
21 | --v-medium-emphasis-opacity: 1;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VFilter.scss:
--------------------------------------------------------------------------------
1 | .filter-comp-wrap {
2 | .filter-selectGroup-wrap {
3 | display: flex;
4 | flex-grow: 1;
5 | align-items: center;
6 |
7 | .v-chip {
8 | --v-chip-height: 40px;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VPagination.scss:
--------------------------------------------------------------------------------
1 | .v-pagination {
2 | .v-pagination__item .v-btn {
3 | --v-btn-size: 16px;
4 | font-weight: 500;
5 | &.v-btn--density-compact {
6 | width: auto;
7 | .v-btn__content {
8 | padding: 0 10px;
9 | min-width: 28px;
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VTable.scss:
--------------------------------------------------------------------------------
1 | @use '../vuetify' as vuetify;
2 | @use '../variables' as variables;
3 |
4 | .v-table > .v-table__wrapper > table > tbody > tr > th,
5 | .v-table > .v-table__wrapper > table > thead > tr > th,
6 | .v-table > .v-table__wrapper > table > tfoot > tr > th {
7 | color: var(--v-theme-on-background);
8 | background-color: variables.$color-grey-50;
9 | }
10 |
11 | .v-table tbody td {
12 | .v-chip.v-chip--size-default {
13 | --v-chip-height: 28px;
14 | font-size: 12px;
15 | }
16 |
17 | .v-selection-control--dirty {
18 | color: rgb(var(--v-theme-primary));
19 | }
20 | }
21 |
22 | .v-table > .v-table__wrapper > table {
23 | border: vuetify.$table-border;
24 | border-radius: vuetify.$border-radius-root;
25 | overflow: hidden;
26 | }
27 |
28 | /** fix the long text word-break problem **/
29 | table {
30 | table-layout: auto;
31 | word-break: break-word;
32 | }
33 |
34 | /** fix: v-tab item have box-shadow which cropped by v-window's property overflow:hidden **/
35 | .v-slide-group:not(.vx-tabs-wrap) ~ .v-window {
36 | padding-left: 20px;
37 | padding-right: 20px;
38 | padding-bottom: 20px;
39 | margin-left: -20px;
40 | margin-right: -20px;
41 | margin-bottom: -20px;
42 | }
43 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VTextarea.scss:
--------------------------------------------------------------------------------
1 | @use '../vuetify' as vuetify;
2 | @use '../variables' as variables;
3 |
4 | .textarea-with-bottom-btns.v-textarea .v-input__control .v-field__field {
5 | padding-bottom: 12px;
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_VVersionSelect.scss:
--------------------------------------------------------------------------------
1 | .version-select-wrap {
2 | padding: 0 8px;
3 | border-color: rgb(var(--v-theme-grey-lighten-1));
4 | display: flex;
5 | .v-chip__content {
6 | flex: 1;
7 | max-width: calc(100% - 26px);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_imgUploader.scss:
--------------------------------------------------------------------------------
1 | .media-box-wrap {
2 | .img-upload-btn,
3 | .img-delete-btn {
4 | font-weight: 400;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_vChip.scss:
--------------------------------------------------------------------------------
1 | .v-chip {
2 | &.text-primary {
3 | color: rgb(var(--v-theme-primary)) !important;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/components/_vTabs.scss:
--------------------------------------------------------------------------------
1 | .v-main .v-tabs--density-compact {
2 | --v-tabs-height: 44px;
3 |
4 | .v-slide-group__content {
5 | color: rgba(var(--v-theme-grey-darken-2), var(--v-high-emphasis-opacity));
6 | }
7 |
8 | .v-btn {
9 | font-weight: 500;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/index.scss:
--------------------------------------------------------------------------------
1 | // base comp style refactor
2 | @import './components/VTable';
3 | @import './components/VCard';
4 | @import './components/VField';
5 | @import './components/VBtn';
6 | @import './components/VTextarea';
7 | @import 'components/SlideGroup';
8 | @import 'components/Navigation';
9 | @import 'components/vTabs';
10 | @import 'components/AutoComplete';
11 | @import 'components/Dialog';
12 | @import 'components/VPagination';
13 | @import 'components/Section';
14 | @import 'components/ActivityTimeLine';
15 | @import 'components/vChip';
16 | @import 'components/VFilter';
17 | @import 'components/VVersionSelect';
18 | @import 'components/imgUploader';
19 |
20 | // page module
21 | @import 'page/Listing';
22 | @import 'page/Detailing';
23 | @import 'page/PageBuilder';
24 |
25 | //some style that fix display problems
26 | @import './fixer';
27 |
28 | @import './utils';
29 |
30 | // it's flexible to split out from qor5 index.scss
31 | @import 'sites/k-theme.scss';
32 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/scss/page/_Listing.scss:
--------------------------------------------------------------------------------
1 | .v-app-bar.v-toolbar {
2 | .v-toolbar-title {
3 | font-size: 16px;
4 | line-height: 24px;
5 | font-weight: 500;
6 | letter-spacing: 0.15px;
7 | }
8 | }
9 |
10 | .list-table-wrap {
11 | padding-left: 8px;
12 | padding-right: 8px;
13 | padding-bottom: 8px;
14 | }
15 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/src/lib/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import type { DefineComponent } from 'vue'
5 | const component: DefineComponent<{}, {}, any>
6 | export default component
7 | }
8 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/tsconfig-build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "src/lib/*.ts",
5 | "src/lib/*.d.ts",
6 | "src/lib/*.tsx",
7 | "src/lib/*.vue"
8 | ],
9 | }
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "ts-node": {
3 | "esm": true
4 | },
5 | "compilerOptions": {
6 | "baseUrl": ".",
7 | "target": "ESNext",
8 | "useDefineForClassFields": true,
9 | "module": "ESNext",
10 | "moduleResolution": "Node",
11 | "strict": true,
12 | "jsx": "preserve",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "esModuleInterop": true,
16 | "lib": ["ESNext", "DOM"],
17 | "skipLibCheck": true,
18 | "noEmit": true,
19 | "paths": {
20 | "@/*": ["src/*"]
21 | }
22 | },
23 | "include": [
24 | "src/**/*.ts",
25 | "src/**/*.d.ts",
26 | "src/**/*.tsx",
27 | "src/**/*.vue"
28 | ],
29 | "references": [
30 | {
31 | "path": "./tsconfig.node.json"
32 | }
33 | ],
34 | "exclude": ["node_modules", "src/lib/TiptapEditor/source"]
35 | }
36 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": [
9 | "vite.config.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/update-tip-tap.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | # Download the zip file
6 | curl -L https://codeload.github.com/qor5/vuetify-pro-tiptap/zip/refs/heads/release -o vuetify-pro-tiptap.zip
7 |
8 | # Clean up the destination directory
9 | rm -rf src/lib/TiptapEditor/lib
10 |
11 | # Create temp directory and unzip
12 | mkdir -p vuetify-pro-tiptap
13 | unzip vuetify-pro-tiptap.zip -d vuetify-pro-tiptap
14 |
15 | # Create destination directory
16 | mkdir -p src/lib/TiptapEditor/lib
17 |
18 | # Move contents from the extracted directory to the destination
19 | mv vuetify-pro-tiptap/vuetify-pro-tiptap-release/* src/lib/TiptapEditor/lib/
20 |
21 | # Clean up
22 | rm -rf vuetify-pro-tiptap
23 | rm vuetify-pro-tiptap.zip
24 |
25 | echo "Done! The vuetify-pro-tiptap has been updated."
26 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vuetifyxjs/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
2 | import viteConfig from './vite.config'
3 |
4 | export default mergeConfig(
5 | viteConfig,
6 | defineConfig({
7 | test: {
8 | server: {
9 | deps: {
10 | inline: ['vuetify'],
11 | },
12 | },
13 | environment: 'jsdom',
14 | exclude: [...configDefaults.exclude, 'e2e/*'],
15 | }
16 | })
17 | )
18 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vx-avatar.go:
--------------------------------------------------------------------------------
1 | package vuetifyx
2 |
3 | import (
4 | "context"
5 |
6 | h "github.com/theplant/htmlgo"
7 | )
8 |
9 | type VXAvatarBuilder struct {
10 | tag *h.HTMLTagBuilder
11 | }
12 |
13 | func VXAvatar(children ...h.HTMLComponent) (r *VXAvatarBuilder) {
14 | r = &VXAvatarBuilder{
15 | tag: h.Tag("vx-avatar").Children(children...),
16 | }
17 | return
18 | }
19 |
20 | func (b *VXAvatarBuilder) Name(v string) (r *VXAvatarBuilder) {
21 | b.tag.Attr("name", v)
22 | return b
23 | }
24 |
25 | func (b *VXAvatarBuilder) Size(v string) (r *VXAvatarBuilder) {
26 | b.tag.Attr("size", v)
27 | return b
28 | }
29 |
30 | func (b *VXAvatarBuilder) Img(v string) (r *VXAvatarBuilder) {
31 | b.tag.Attr("img", v)
32 | return b
33 | }
34 |
35 | func (b *VXAvatarBuilder) Class(v string) (r *VXAvatarBuilder) {
36 | b.tag.Attr("class", v)
37 | return b
38 | }
39 |
40 | func (b *VXAvatarBuilder) Slot(name string, children ...h.HTMLComponent) *VXAvatarBuilder {
41 | slotTemplate := h.Tag("template").Attr("#"+name).Children(children...)
42 | b.tag.Children(slotTemplate)
43 | return b
44 | }
45 |
46 | func (b *VXAvatarBuilder) Attr(vs ...interface{}) (r *VXAvatarBuilder) {
47 | b.tag.Attr(vs...)
48 | return b
49 | }
50 |
51 | func (b *VXAvatarBuilder) MarshalHTML(ctx context.Context) (r []byte, err error) {
52 | return b.tag.MarshalHTML(ctx)
53 | }
54 |
--------------------------------------------------------------------------------
/ui/vuetifyx/vx-toolbar.go:
--------------------------------------------------------------------------------
1 | package vuetifyx
2 |
3 | import (
4 | "context"
5 |
6 | h "github.com/theplant/htmlgo"
7 | )
8 |
9 | type VXToolbarBuilder struct {
10 | tag *h.HTMLTagBuilder
11 | }
12 |
13 | func VXToolbar(children ...h.HTMLComponent) (r *VXToolbarBuilder) {
14 | r = &VXToolbarBuilder{
15 | tag: h.Tag("vx-toolbar").Children(children...),
16 | }
17 | return
18 | }
19 |
20 | func (b *VXToolbarBuilder) Text(v string) (r *VXToolbarBuilder) {
21 | b.tag.Attr("text", v)
22 | return b
23 | }
24 |
25 | func (b *VXToolbarBuilder) Placeholder(v string) (r *VXToolbarBuilder) {
26 | b.tag.Attr("type", v)
27 | return b
28 | }
29 |
30 | func (b *VXToolbarBuilder) Attr(vs ...interface{}) (r *VXToolbarBuilder) {
31 | b.tag.Attr(vs...)
32 | return b
33 | }
34 | func (b *VXToolbarBuilder) SetAttr(k string, v interface{}) {
35 | b.tag.SetAttr(k, v)
36 | }
37 |
38 | func (b *VXToolbarBuilder) MarshalHTML(ctx context.Context) (r []byte, err error) {
39 | return b.tag.MarshalHTML(ctx)
40 | }
41 |
--------------------------------------------------------------------------------