├── packages
├── vue2
│ ├── utils
│ │ ├── index.ts
│ │ └── diff.ts
│ ├── components
│ │ ├── Container
│ │ │ ├── types.d.ts
│ │ │ ├── createContainer.ts
│ │ │ └── index.ts
│ │ ├── Node
│ │ │ ├── createNode.ts
│ │ │ ├── constants.ts
│ │ │ └── index.ts
│ │ └── Root
│ │ │ ├── app.ts
│ │ │ └── leafer.ts
│ ├── .gitignore
│ ├── types
│ │ ├── resolver.d.ts
│ │ └── index.d.ts
│ ├── composables
│ │ ├── useGetParentNodeName.ts
│ │ ├── useGetPropsAndEventByAttrs
│ │ │ ├── utils.ts
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── useInsertBefore.ts
│ │ ├── useCreateEvents.ts
│ │ ├── useGetContainer.ts
│ │ ├── useEffectUpdate.ts
│ │ └── useSomeNode.ts
│ ├── resolver.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── index.ts
│ └── package.json
├── vue3
│ ├── index.ts
│ ├── composables
│ │ ├── index.ts
│ │ ├── useGetPropsAndEventByAttrs
│ │ │ ├── utils.ts
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── useEffectUpdate.ts
│ │ └── useLogger.ts
│ ├── renderer
│ │ ├── index.ts
│ │ └── renderer.ts
│ ├── tags
│ │ └── Empty.ts
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── utils
│ │ └── index.ts
│ ├── compiler.ts
│ ├── types
│ │ ├── LeaferVueComponent.ts
│ │ ├── LeaferEventType.ts
│ │ └── index.ts
│ ├── tsup.config.ts
│ ├── components
│ │ └── application
│ │ │ └── index.ts
│ └── package.json
└── uni-app
│ └── package.json
├── examples
├── vue2
│ ├── env.d.ts
│ ├── public
│ │ ├── test.jpg
│ │ └── favicon.ico
│ ├── src
│ │ ├── main.ts
│ │ ├── assets
│ │ │ ├── logo.svg
│ │ │ ├── main.css
│ │ │ └── base.css
│ │ └── App.vue
│ ├── tsconfig.config.json
│ ├── tsconfig.json
│ ├── index.html
│ ├── .gitignore
│ ├── vite.config.ts
│ ├── package.json
│ └── README.md
└── vue3
│ ├── env.d.ts
│ ├── public
│ ├── test.jpg
│ └── favicon.ico
│ ├── .vscode
│ └── extensions.json
│ ├── src
│ ├── main.ts
│ └── App.vue
│ ├── tsconfig.json
│ ├── tsconfig.app.json
│ ├── tsconfig.node.json
│ ├── index.html
│ ├── .gitignore
│ ├── vite.config.ts
│ ├── package.json
│ └── README.md
├── playground
├── src
│ ├── constants.ts
│ ├── main.css
│ ├── env.d.ts
│ ├── template
│ │ ├── main.vue
│ │ ├── _tsconfig.json
│ │ └── welcome.vue
│ ├── utils
│ │ ├── encode.ts
│ │ └── dependency.ts
│ ├── main.ts
│ ├── components
│ │ ├── Settings.vue
│ │ └── Header.vue
│ ├── assets
│ │ └── logo.svg
│ ├── components.d.ts
│ ├── App.vue
│ └── composables
│ │ └── store.ts
├── public
│ ├── logo.ico
│ └── logo.png
├── README.md
├── tsconfig.json
├── unocss.config.ts
├── index.html
├── package.json
└── vite.config.ts
├── .npmrc
├── docs
├── public
│ ├── logo.ico
│ ├── logo.png
│ └── leafer-vue.proxy.js
├── intro
│ ├── uni.md
│ ├── nuxt.md
│ └── vue2.md
├── .vitepress
│ ├── theme
│ │ ├── composables
│ │ │ └── dark.ts
│ │ ├── components
│ │ │ ├── HomePage.vue
│ │ │ ├── ApiTyping
│ │ │ │ └── index.vue
│ │ │ ├── repl
│ │ │ │ └── index.vue
│ │ │ ├── Sponsors.vue
│ │ │ └── MoreStarts.vue
│ │ ├── Layout.vue
│ │ ├── index.ts
│ │ ├── blur.css
│ │ └── var.css
│ ├── plugin
│ │ ├── index.ts
│ │ └── tooltip.ts
│ └── config.ts
├── shims.d.ts
├── tsconfig.json
├── guide
│ ├── components
│ │ ├── path
│ │ │ ├── pen.md
│ │ │ ├── path.md
│ │ │ ├── Pen.vue
│ │ │ └── Path.vue
│ │ ├── container
│ │ │ ├── group.md
│ │ │ ├── box.md
│ │ │ ├── frame.md
│ │ │ ├── Group.vue
│ │ │ ├── Box.vue
│ │ │ └── Frame.vue
│ │ ├── custom
│ │ │ ├── custom.md
│ │ │ └── index.vue
│ │ ├── shape
│ │ │ ├── polygon.md
│ │ │ ├── rect.md
│ │ │ ├── star.md
│ │ │ ├── ellipse.md
│ │ │ ├── line.md
│ │ │ ├── Star.vue
│ │ │ ├── Rect.vue
│ │ │ ├── Ellipse.vue
│ │ │ ├── Polygon.vue
│ │ │ └── Line.vue
│ │ ├── app
│ │ │ ├── leafer.md
│ │ │ ├── leaferApp.md
│ │ │ └── leaferApp.vue
│ │ ├── image
│ │ │ ├── image.md
│ │ │ ├── canvas.md
│ │ │ ├── Image.vue
│ │ │ └── Canvas.vue
│ │ └── text
│ │ │ └── text.md
│ ├── events
│ │ ├── leafer
│ │ │ ├── Leafer
│ │ │ │ ├── index.vue
│ │ │ │ └── leafer.md
│ │ │ ├── Resize
│ │ │ │ ├── resize.md
│ │ │ │ └── index.vue
│ │ │ ├── Watch
│ │ │ │ ├── watch.md
│ │ │ │ └── index.vue
│ │ │ ├── Layout
│ │ │ │ ├── index.vue
│ │ │ │ └── layout.md
│ │ │ └── Render
│ │ │ │ ├── index.vue
│ │ │ │ └── render.md
│ │ ├── action
│ │ │ ├── Pointer
│ │ │ │ ├── index.vue
│ │ │ │ └── pointer.md
│ │ │ ├── Drag
│ │ │ │ ├── index.vue
│ │ │ │ └── drag.md
│ │ │ ├── Drop
│ │ │ │ ├── index.vue
│ │ │ │ └── drop.md
│ │ │ ├── Swipe
│ │ │ │ ├── index.vue
│ │ │ │ └── swipe.md
│ │ │ ├── Key
│ │ │ │ ├── key.md
│ │ │ │ └── index.vue
│ │ │ ├── Zoom
│ │ │ │ ├── zoom.md
│ │ │ │ └── index.vue
│ │ │ ├── Rotate
│ │ │ │ ├── index.vue
│ │ │ │ └── rotate.md
│ │ │ └── Move
│ │ │ │ ├── move.md
│ │ │ │ └── index.vue
│ │ ├── element
│ │ │ ├── Image
│ │ │ │ └── image.md
│ │ │ ├── Property
│ │ │ │ ├── property.md
│ │ │ │ └── index.vue
│ │ │ └── Child
│ │ │ │ ├── index.vue
│ │ │ │ └── child.md
│ │ └── events.md
│ ├── start
│ │ ├── introduction.md
│ │ ├── index.vue
│ │ └── usage.md
│ └── other
│ │ ├── export
│ │ └── export.md
│ │ └── animate
│ │ └── animate.md
├── awesome
│ └── index.md
├── uno.config.ts
├── plugin
│ ├── editor.vue
│ └── index.md
├── components.d.ts
├── vite.config.ts
├── package.json
└── index.md
├── .github
├── images
│ └── logo.png
└── workflows
│ ├── deploy.yml
│ ├── release2.yml
│ └── release3.yml
├── pnpm-workspace.yaml
├── .gitignore
├── netlify.toml
├── eslint.config.js
├── tsconfig.json
├── .vscode
└── settings.json
├── LICENSE
├── README.md
└── package.json
/packages/vue2/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './diff'
2 |
--------------------------------------------------------------------------------
/examples/vue2/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/vue3/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/vue3/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components/application/index'
2 |
--------------------------------------------------------------------------------
/playground/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const IS_DEV = import.meta.env.DEV
2 |
--------------------------------------------------------------------------------
/playground/src/main.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --el-color-primary: #40883C;
3 | }
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | strict-peer-dependencies=false
2 | auto-install-peers=true
3 | shamefully-hoist=true
4 |
--------------------------------------------------------------------------------
/docs/public/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/docs/public/logo.ico
--------------------------------------------------------------------------------
/docs/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/docs/public/logo.png
--------------------------------------------------------------------------------
/packages/vue2/components/Container/types.d.ts:
--------------------------------------------------------------------------------
1 | export type Container = 'Frame' | 'Box' | 'Group'
2 |
--------------------------------------------------------------------------------
/.github/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/.github/images/logo.png
--------------------------------------------------------------------------------
/playground/public/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/playground/public/logo.ico
--------------------------------------------------------------------------------
/playground/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/playground/public/logo.png
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - playground
3 | - 'packages/**'
4 | - 'examples/**'
5 | - docs
6 |
--------------------------------------------------------------------------------
/examples/vue2/public/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/examples/vue2/public/test.jpg
--------------------------------------------------------------------------------
/examples/vue3/public/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/examples/vue3/public/test.jpg
--------------------------------------------------------------------------------
/playground/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/examples/vue2/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/examples/vue2/public/favicon.ico
--------------------------------------------------------------------------------
/examples/vue3/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FliPPeDround/leafer-vue/HEAD/examples/vue3/public/favicon.ico
--------------------------------------------------------------------------------
/packages/vue3/composables/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useEffectUpdate'
2 | export * from './useGetPropsAndEventByAttrs'
3 |
--------------------------------------------------------------------------------
/examples/vue3/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/vue3/renderer/index.ts:
--------------------------------------------------------------------------------
1 | import { renderer } from './renderer'
2 |
3 | export const createApp = renderer.createApp
4 |
--------------------------------------------------------------------------------
/packages/vue3/tags/Empty.ts:
--------------------------------------------------------------------------------
1 | import { Leaf } from 'leafer-ui'
2 |
3 | export class Empty extends Leaf {
4 | visible = false
5 | }
6 |
--------------------------------------------------------------------------------
/docs/intro/uni.md:
--------------------------------------------------------------------------------
1 | # uni-app
2 |
3 |
4 | 🏗 Working in Progress
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/playground/src/template/main.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/vue2/src/main.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | new Vue({
5 | render: h => h(App),
6 | }).$mount('#app')
7 |
--------------------------------------------------------------------------------
/examples/vue3/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | const app = createApp(App)
5 | app.mount('#app')
6 |
--------------------------------------------------------------------------------
/packages/vue2/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | .DS_Store
3 | .idea
4 | *.log
5 | *.tgz
6 | coverage
7 | dist
8 | lib-cov
9 | logs
10 | node_modules
11 | temp
12 |
--------------------------------------------------------------------------------
/packages/vue3/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | .DS_Store
3 | .idea
4 | *.log
5 | *.tgz
6 | coverage
7 | dist
8 | lib-cov
9 | logs
10 | node_modules
11 | temp
12 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/composables/dark.ts:
--------------------------------------------------------------------------------
1 | import { useDark } from '@vueuse/core'
2 |
3 | export const isDark = useDark({
4 | storageKey: 'vitepress-theme-appearance',
5 | })
6 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/HomePage.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/vue2/types/resolver.d.ts:
--------------------------------------------------------------------------------
1 | import type { ComponentResolver } from 'unplugin-vue-components'
2 |
3 | declare function LeaferVueResolver(): ComponentResolver
4 |
5 | export { LeaferVueResolver }
6 |
--------------------------------------------------------------------------------
/docs/intro/nuxt.md:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 | 🏗 Working in Progress
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/vue3/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "references": [
3 | {
4 | "path": "./tsconfig.node.json"
5 | },
6 | {
7 | "path": "./tsconfig.app.json"
8 | }
9 | ],
10 | "files": []
11 | }
12 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useGetParentNodeName.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentInstance } from 'vue-demi'
2 |
3 | export function useGetParentNodeName() {
4 | return getCurrentInstance()?.proxy?.$parent?.$options.name
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | .DS_Store
3 | .idea
4 | *.log
5 | *.tgz
6 | coverage
7 | dist
8 | lib-cov
9 | logs
10 | node_modules
11 | temp
12 |
13 | docs/.vitepress/cache
14 | docs/.vitepress/dist
15 |
16 | build
17 | leafer-vue.proxy.js
18 |
--------------------------------------------------------------------------------
/examples/vue2/tsconfig.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.node.json",
3 | "compilerOptions": {
4 | "composite": true,
5 | "types": ["node"]
6 | },
7 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/vue2/components/Node/createNode.ts:
--------------------------------------------------------------------------------
1 | import type { Node } from './constants'
2 | import { UI } from 'leafer-ui'
3 |
4 | export function createNode(Node: Node, config: any) {
5 | return UI.one({
6 | tag: Node,
7 | ...config,
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useGetPropsAndEventByAttrs/utils.ts:
--------------------------------------------------------------------------------
1 | export function getEventNameByAttrName(attrName: string) {
2 | return attrName
3 | .slice(2)
4 | .replace(/([A-Z])/g, (_, letter, index) => index === 0 ? letter.toLowerCase() : `.${letter.toLowerCase()}`)
5 | }
6 |
--------------------------------------------------------------------------------
/packages/vue3/composables/useGetPropsAndEventByAttrs/utils.ts:
--------------------------------------------------------------------------------
1 | export function getEventNameByAttrName(attrName: string) {
2 | return attrName
3 | .slice(2)
4 | .replace(/([A-Z])/g, (_, letter, index) => index === 0 ? letter.toLowerCase() : `.${letter.toLowerCase()}`)
5 | }
6 |
--------------------------------------------------------------------------------
/docs/.vitepress/plugin/index.ts:
--------------------------------------------------------------------------------
1 | import type { MarkdownRenderer } from 'vitepress'
2 | import { groupIconMdPlugin } from 'vitepress-plugin-group-icons'
3 | import tooltip from './tooltip'
4 |
5 | export function mdPlugin(md: MarkdownRenderer) {
6 | md.use(tooltip)
7 | md.use(groupIconMdPlugin)
8 | }
9 |
--------------------------------------------------------------------------------
/playground/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Leafer Vue Playground
6 |
7 | ## Credits
8 |
9 | - [vuejs/repl](https://github.com/vuejs/repl)
10 | - [element-plus/element-plus-playground](https://github.com/element-plus/element-plus-playground)
11 |
--------------------------------------------------------------------------------
/packages/vue2/composables/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useCreateEvents'
2 | export * from './useEffectUpdate'
3 | export * from './useGetContainer'
4 | export * from './useGetParentNodeName'
5 | export * from './useGetPropsAndEventByAttrs'
6 | export * from './useInsertBefore'
7 | export * from './useSomeNode'
8 |
--------------------------------------------------------------------------------
/packages/vue2/components/Node/constants.ts:
--------------------------------------------------------------------------------
1 | export const NodesType = [
2 | 'Rect',
3 | 'Ellipse',
4 | 'Polygon',
5 | 'Star',
6 | 'Line',
7 | 'Image',
8 | 'Canvas',
9 | 'Text',
10 | 'Path',
11 | 'Pen',
12 | ] as const
13 |
14 | // 获取GeometriesType枚举类型
15 | export type Node = typeof NodesType[number]
16 |
--------------------------------------------------------------------------------
/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "paths": {
6 | "@/*": ["src/*"]
7 | },
8 | "types": [],
9 | "skipLibCheck": true
10 | },
11 | "include": ["src"],
12 | "exclude": ["src/template/**"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/vue2/utils/diff.ts:
--------------------------------------------------------------------------------
1 | export function diff(oldValue: Record, newValue: Record) {
2 | const result: Record = {}
3 |
4 | for (const key in newValue) {
5 | if (oldValue[key] !== newValue[key])
6 | result[key] = newValue[key]
7 | }
8 |
9 | return result
10 | }
11 |
--------------------------------------------------------------------------------
/examples/vue2/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "docs/dist"
3 | command = "pnpm run deploy"
4 |
5 | [build.environment]
6 | NPM_FLAGS = "--version"
7 | NODE_VERSION = "20"
8 |
9 | [[redirects]]
10 | from = "/*"
11 | to = "index.html"
12 | status = 200
13 |
14 | [[redirects]]
15 | from = "/play/*"
16 | to = "/play/index.html"
17 | status = 200
18 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import antfu from '@antfu/eslint-config'
2 |
3 | export default await antfu({
4 | ignores: [
5 | '*.global.js',
6 | 'build',
7 | 'node_modules',
8 | '.vitepress',
9 | 'docs/.vitepress/cache/deps/*.*',
10 | '**/public/*.js',
11 | ],
12 | rules: {
13 | 'no-alert': 'off',
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/docs/shims.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { ComponentOptions } from 'vue'
3 |
4 | const Component: ComponentOptions
5 | export default Component
6 | }
7 |
8 | declare module '*.md' {
9 | import type { ComponentOptions } from 'vue'
10 |
11 | const Component: ComponentOptions
12 | export default Component
13 | }
14 |
--------------------------------------------------------------------------------
/packages/vue2/resolver.ts:
--------------------------------------------------------------------------------
1 | import type { ComponentResolver } from 'unplugin-vue-components'
2 |
3 | export function LeaferVueResolver(): ComponentResolver {
4 | return {
5 | type: 'component',
6 | resolve: (name: string) => {
7 | if (name.match(/^lf[A-Z]/))
8 | return { name, from: 'leafer-vue' }
9 | },
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/Layout.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "compilerOptions": {
4 | "composite": true,
5 | "types": [
6 | "leafer-vue",
7 | "vite/client"
8 | ]
9 | },
10 | "include": ["**/**/*.vue", "shims.d.ts", ".vitepress/**/*.ts", ".vitepress/**/*.vue"],
11 | "exclude": ["node_modules/**/*"]
12 | }
13 |
--------------------------------------------------------------------------------
/docs/guide/components/path/pen.md:
--------------------------------------------------------------------------------
1 |
4 | # Pen
5 | >
6 | > 像绘画一样,快速画出不同样式的路径组合。
7 | >
8 |
9 | ## 用法
10 |
11 |
12 |
13 | > 详情查看[Path |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Path.html)。
14 | >
15 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
16 |
--------------------------------------------------------------------------------
/docs/awesome/index.md:
--------------------------------------------------------------------------------
1 | # Awesome Leafer Vue
2 |
3 | > 🌟 欢迎分享你的 Leafer Vue 项目!
4 | >
5 | > 如果你使用 Leafer Vue 开发了有趣的项目,欢迎提交到这里与大家分享。无论是创意应用、实用工具还是学习示例,我们都期待看到你的作品!
6 | >
7 |
8 | | 项目 | 描述 | 链接 |
9 | | :--- | :--- | :--- |
10 | | [leafer-labubu](https://github.com/xiaohe0601/leafer-labubu) | 🧸 LABUBU Created with Leafer Vue | [预览](https://labubu.xiaohe.ink/) |
11 |
--------------------------------------------------------------------------------
/examples/vue2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.web.json",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "paths": {
6 | "@/*": ["./src/*"]
7 | }
8 | },
9 |
10 | "references": [
11 | {
12 | "path": "./tsconfig.config.json"
13 | }
14 | ],
15 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"]
16 | }
17 |
--------------------------------------------------------------------------------
/examples/vue3/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "compilerOptions": {
4 | "composite": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | },
9 | "types": ["leafer-vue"]
10 | },
11 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
12 | "exclude": ["src/**/__tests__/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/examples/vue3/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node18/tsconfig.json",
3 | "compilerOptions": {
4 | "composite": true,
5 | "module": "ESNext",
6 | "types": ["node"]
7 | },
8 | "include": [
9 | "vite.config.*",
10 | "vitest.config.*",
11 | "cypress.config.*",
12 | "nightwatch.conf.*",
13 | "playwright.config.*"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/playground/src/utils/encode.ts:
--------------------------------------------------------------------------------
1 | // prefer old unicode hacks for backward compatibility
2 | // https://base64.guru/developers/javascript/examples/unicode-strings
3 | export function utoa(data: string): string {
4 | return btoa(unescape(encodeURIComponent(data)))
5 | }
6 |
7 | export function atou(base64: string): string {
8 | return decodeURIComponent(escape(atob(base64)))
9 | }
10 |
--------------------------------------------------------------------------------
/docs/uno.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, presetAttributify, presetIcons, presetUno, transformerDirectives } from 'unocss'
2 |
3 | export default defineConfig({
4 | presets: [
5 | presetUno(),
6 | presetIcons(),
7 | presetAttributify(),
8 | ],
9 | transformers: [
10 | transformerDirectives({
11 | applyVariable: ['--at-apply', '--uno'],
12 | }),
13 | ],
14 | })
15 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useInsertBefore.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentInstance } from 'vue-demi'
2 |
3 | export function useInsertBefore(canvas: HTMLCanvasElement) {
4 | const selfElement = getCurrentInstance()?.proxy?.$el as HTMLElement
5 | const parentElement = selfElement.parentElement!
6 | const nextElement = selfElement.nextElementSibling
7 | parentElement.insertBefore(canvas, nextElement)
8 | }
9 |
--------------------------------------------------------------------------------
/playground/src/template/_tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "jsx": "preserve",
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "types": ["leafer-vue"],
8 | "allowImportingTsExtensions": true,
9 | "allowJs": true,
10 | "checkJs": true
11 | },
12 | "vueCompilerOptions": {
13 | "target": 3.3
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/vue3/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/vue2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/vue3/composables/useEffectUpdate.ts:
--------------------------------------------------------------------------------
1 | import type { IUI } from '@leafer-ui/interface'
2 | import { watch } from 'vue'
3 | import { useGetPropsByAttrs } from './index'
4 |
5 | export function useEffectUpdate(
6 | attrs: Record,
7 | instance: IUI,
8 | ) {
9 | watch(
10 | () => useGetPropsByAttrs(attrs),
11 | (newConfig) => {
12 | instance.set(newConfig)
13 | },
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/playground/unocss.config.ts:
--------------------------------------------------------------------------------
1 | import transformerDirective from '@unocss/transformer-directives'
2 | import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss'
3 |
4 | export default defineConfig({
5 | presets: [presetUno(), presetAttributify(), presetIcons()],
6 | transformers: [transformerDirective()],
7 | shortcuts: {
8 | 'color-primary': 'color-[var(--el-color-primary)]',
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Leafer/index.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | console.log('leafer ready !')">
8 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/playground/src/main.ts:
--------------------------------------------------------------------------------
1 | import App from '@/App.vue'
2 | import { createApp } from 'vue'
3 | import '@unocss/reset/tailwind.css'
4 | import '@vue/repl/style.css'
5 | import 'element-plus/theme-chalk/dark/css-vars.css'
6 | import 'uno.css'
7 | import './main.css'
8 |
9 | // @ts-expect-error Custom window property
10 | window.VUE_DEVTOOLS_CONFIG = {
11 | defaultSelectedAppId: 'repl',
12 | }
13 |
14 | createApp(App).mount('#app')
15 |
--------------------------------------------------------------------------------
/examples/vue2/.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
13 | dist-ssr
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 |
--------------------------------------------------------------------------------
/docs/guide/components/container/group.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Group
6 | >
7 | > 创建 Group。用于将元素进行打组,自身没有样式,可以不断嵌套。
8 | >
9 | > 实际宽高随子元素变化,不能设置,可以通过 bounds 获取实际宽高信息。
10 |
11 | ## 用法
12 |
13 |
14 |
15 | ## 属性
16 | > 详情查看[Group |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Group.html)。
17 | >
18 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
19 |
--------------------------------------------------------------------------------
/packages/vue2/components/Container/createContainer.ts:
--------------------------------------------------------------------------------
1 | import type { Container } from './types'
2 | import { Box, Frame, Group } from 'leafer-ui'
3 |
4 | const containerConstructors = {
5 | Frame,
6 | Box,
7 | Group,
8 | }
9 |
10 | export function createContainer(container: Container, config: any) {
11 | const constructor = containerConstructors[container]
12 | const instance = new constructor(config)
13 | return instance
14 | }
15 |
--------------------------------------------------------------------------------
/examples/vue3/.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
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .vscode/*
22 | !.vscode/extensions.json
23 | .idea
24 | *.suo
25 | *.ntvs*
26 | *.njsproj
27 | *.sln
28 | *.sw?
29 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useCreateEvents.ts:
--------------------------------------------------------------------------------
1 | import type { IUI } from 'leafer-ui'
2 | import { onUnmounted } from 'vue-demi'
3 |
4 | export function useCreateEvents(events: Record, instance: IUI) {
5 | Object.keys(events).forEach((key) => {
6 | const eventName = key as keyof typeof events
7 | instance.on(eventName, events[key])
8 |
9 | onUnmounted(() => {
10 | instance.off(eventName, events[key])
11 | })
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/packages/vue2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2018",
4 | "lib": ["esnext", "DOM"],
5 | "baseUrl": ".",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "paths": {
9 | "@/*": ["./*"]
10 | },
11 | "resolveJsonModule": true,
12 | "strict": true,
13 | "strictNullChecks": true,
14 | "esModuleInterop": true,
15 | "skipDefaultLibCheck": true,
16 | "skipLibCheck": true
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/vue3/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2018",
4 | "lib": ["esnext", "DOM"],
5 | "baseUrl": ".",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "paths": {
9 | "@/*": ["./*"]
10 | },
11 | "resolveJsonModule": true,
12 | "strict": true,
13 | "strictNullChecks": true,
14 | "esModuleInterop": true,
15 | "skipDefaultLibCheck": true,
16 | "skipLibCheck": true
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs/guide/components/custom/custom.md:
--------------------------------------------------------------------------------
1 | # Custom
2 | > 自定义元素,详情请查看[Custom |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/custom/base/register.html)
3 |
4 | ## 用法
5 |
8 |
9 |
10 |
11 | > [!TIP]
12 | > - 自定义tag名不能与内置的tag名冲突,需要全局唯一
13 | > - 自定义类名不能与``中声明的组件名一致
14 |
15 | ### 属性
16 | | 属性名 | 类型 | 默认值 | 说明 |
17 | | :--- | :--- | :--- | :--- |
18 | | is | `Constructor` | - | 自定义元素类 |
19 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useGetContainer.ts:
--------------------------------------------------------------------------------
1 | import type { Leafer } from 'leafer-ui'
2 | import { getCurrentInstance } from 'vue-demi'
3 |
4 | export function useGetContainer(_lf?: string) {
5 | try {
6 | const parent = getCurrentInstance()?.proxy?.$parent
7 | if (parent && 'container' in parent)
8 | return parent.container as Leafer
9 |
10 | else
11 | throw new Error('Container not found')
12 | }
13 | catch {
14 | throw new Error('Container not found')
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/playground/src/components/Settings.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/vue3/utils/index.ts:
--------------------------------------------------------------------------------
1 | export function isOn(key: string): boolean {
2 | return key.charCodeAt(0) === 111 /* o */
3 | && key.charCodeAt(1) === 110 /* n */
4 | && (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97) /* uppercase letter */
5 | }
6 |
7 | export function getEventNameByAttrName(attrName: string) {
8 | return attrName
9 | .replace(/^(on:?)?|Once$/g, '')
10 | .replace(/([A-Z])/g, (_match, letter, index) =>
11 | index === 0 ? letter.toLowerCase() : `.${letter.toLowerCase()}`)
12 | }
13 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Resize/resize.md:
--------------------------------------------------------------------------------
1 | # ResizeEvent
2 | 重置尺寸事件。
3 |
4 | > 详情请查看[ResizeEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Resize.html)
5 |
6 | ## 示例
7 |
10 |
11 |
12 |
13 | ## 事件名称
14 |
15 | [IResizeEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IResizeEvent.html
16 |
17 | | 事件名 | 说明 | 参数 |
18 | | --- | --- | --- |
19 | | `resize` | 重置尺寸大小 | [`IResizeEvent`][IResizeEvent-url] |
20 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Pointer/index.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/docs/plugin/editor.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/packages/vue3/compiler.ts:
--------------------------------------------------------------------------------
1 | const elementNames = [
2 | 'Leafer',
3 | 'Frame',
4 | 'Box',
5 | 'Group',
6 |
7 | 'Rect',
8 | 'Ellipse',
9 | 'Polygon',
10 | 'Line',
11 | 'Star',
12 |
13 | 'Path',
14 | 'Pen',
15 |
16 | 'Image',
17 | 'Canvas',
18 |
19 | 'Text',
20 | 'Custom',
21 | ]
22 |
23 | export function isCustomElement(name: string, customElements: string[] = []) {
24 | return [...customElements, ...elementNames].includes(name)
25 | }
26 |
27 | export const compilerOptions = {
28 | isCustomElement,
29 | }
30 |
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Leafer Vue Playground
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Drag/index.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 | fill = '#a8b1ff'"
17 | @drag-end="() => fill = '#66A659'"
18 | />
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useEffectUpdate.ts:
--------------------------------------------------------------------------------
1 | import type { IUI } from '@leafer-ui/interface'
2 | import { diff } from '@/utils'
3 | import { watch } from 'vue-demi'
4 | import { useGetPropsByAttrs } from '.'
5 |
6 | export function useEffectUpdate(
7 | attrs: Record,
8 | instance: IUI,
9 | ) {
10 | watch(
11 | () => useGetPropsByAttrs(attrs),
12 | (newConfig, oldConfig) => {
13 | const diffConfig = diff(oldConfig, newConfig)
14 | if (diffConfig) {
15 | instance.set(diffConfig)
16 | }
17 | },
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useSomeNode.ts:
--------------------------------------------------------------------------------
1 | import type { VNode } from 'vue-demi'
2 |
3 | interface VNodeType {
4 | name: string
5 | props: []
6 | setup: () => void
7 | }
8 | export function useSomeNode(slots: any, nodeName: string, name: string) {
9 | const internal = slots && slots.default && slots.default()
10 |
11 | if (internal && internal?.length > 0) {
12 | const someNode = internal.some((node: VNode) => (node.type as VNodeType).name === nodeName)
13 | if (!someNode)
14 | throw new Error(`${name} must have a ${nodeName} child`)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/polygon.md:
--------------------------------------------------------------------------------
1 |
4 | # Polygon
5 | >
6 | > 绘制三角形、菱形、五边形、多边形。
7 | >
8 |
9 | ## 用法
10 |
11 |
12 |
13 | ## 属性
14 |
15 | | 名称 | 类型 | 默认值 | 说明 |
16 | | --- | --- | --- | --- |
17 | | width | ^[number] | —— | 宽度 |
18 | | height | ^[number] | —— | 高度 |
19 | | sides | ^[number] | ^[3] | 多边形的边数,取值范围 ≥ 3 |
20 |
21 | > 详情查看[Polygon |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Polygon.html)。
22 | >
23 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
24 |
--------------------------------------------------------------------------------
/examples/vue2/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url'
2 |
3 | import legacy from '@vitejs/plugin-legacy'
4 | import vue2 from '@vitejs/plugin-vue2'
5 | import { defineConfig } from 'vite'
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | vue2(),
11 | legacy({
12 | targets: ['ie >= 11'],
13 | additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
14 | }),
15 | ],
16 | resolve: {
17 | alias: {
18 | '@': fileURLToPath(new URL('./src', import.meta.url)),
19 | },
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/rect.md:
--------------------------------------------------------------------------------
1 |
4 | # Rect
5 | >
6 | > 绘制矩形、圆角矩形。
7 | >
8 |
9 | ## 用法
10 |
11 |
12 | ## 属性
13 |
14 | | 名称 | 类型 | 默认值 | 说明 |
15 | | --- | --- | --- | --- |
16 | | width | ^[number] | —— | 宽度 |
17 | | height | ^[number] | —— | 高度 |
18 | | fill | ^[string] | —— | 填充色 |
19 | | cornerRadius | ^[number] | —— | 圆角大小,可以分别设置 4 个圆角 |
20 |
21 | > 详情查看[Rect |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Rect.html)。
22 | >
23 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
24 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Watch/watch.md:
--------------------------------------------------------------------------------
1 | # WatchEvent
2 | 布局事件。
3 |
4 | > 详情请查看[WatchEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Watch.html)
5 |
6 | ## 示例
7 |
8 |
11 |
12 |
13 |
14 | ## 事件名称
15 |
16 | [IWatchEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IWatchEvent.html
17 |
18 | | 事件名 | 说明 | 参数 |
19 | | --- | --- | --- |
20 | | `watchRequest` | 请求布局 | [`IWatchEvent`][IWatchEvent-url] |
21 | | `watchData` | 开始本轮布局 | [`IWatchEvent`][IWatchEvent-url] |
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "jsx": "preserve",
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "useDefineForClassFields": true,
7 | "module": "ESNext",
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "node",
11 | "resolveJsonModule": true,
12 |
13 | /* Linting */
14 | "strict": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "noUnusedLocals": true,
17 | "noUnusedParameters": true,
18 | "noEmit": true,
19 | "isolatedModules": true,
20 | "skipLibCheck": true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/guide/components/container/box.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Box
6 | >
7 | > 创建 Box。支持 Group 的功能和 Rect 的外观样式, 类似于 HTML5 中的 DIV,可以不断嵌套。
8 | >
9 |
10 | ## 用法
11 |
12 |
13 |
14 | ## 属性
15 |
16 | | 名称 | 类型 | 默认值 | 说明 |
17 | | --- | --- | --- | --- |
18 | | width | ^[number] | —— | 宽度 |
19 | | height | ^[number] | —— | 高度 |
20 | | overflow | ^['show']\| ^['hide'] | ^['hide'] | 超出部分是否裁剪 |
21 | > 详情查看[Box |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Box.html)。
22 | >
23 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
24 |
--------------------------------------------------------------------------------
/docs/guide/start/introduction.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 | Leafer Vue
7 |
8 |
9 |
10 | 创建一个可交互`leafer-vue`应用,可以拖拽、点击改变颜色的星星。
11 |
12 |
13 | ## 安装
14 |
15 | ::: code-group
16 |
17 | ```bash [pnpm]
18 | pnpm i leafer-ui leafer-vue
19 | ```
20 |
21 | ```bash [yarn]
22 | yarn add leafer-ui leafer-vue
23 | ```
24 |
25 | ```bash [npm]
26 | npm i leafer-ui leafer-vue
27 | ```
28 |
29 | ```bash [bun]
30 | bun add leafer-ui leafer-vue
31 | ```
32 |
33 | :::
34 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/star.md:
--------------------------------------------------------------------------------
1 |
4 | # Star
5 | >
6 | > 绘制车标、星光、五角星、多角星形。
7 | >
8 | ## 用法
9 |
10 |
11 |
12 | ## 属性
13 |
14 | | 名称 | 类型 | 默认值 | 说明 |
15 | | --- | --- | --- | --- |
16 | | width | ^[number] | —— | 宽度 |
17 | | height | ^[number] | —— | 高度 |
18 | | points | ^[number] | ^[5] | 星形的顶点数,取值范围为 >=3 |
19 | | innerRadius | ^[number] | 0.382 | 内半径比例,取值范围为 0.0 ~ 1.0 |
20 |
21 | > 详情查看[Star |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Star.html)。
22 | >
23 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
24 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useGetPropsAndEventByAttrs/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 | import { useGetPropsAndEventByAttrs } from '.'
3 |
4 | describe('test leafer_vue', () => {
5 | it('test leafer-vue.useGetPropsAndEventByAttrs', () => {
6 | const attrs = {
7 | onPointerMove: () => {},
8 | test: 'test',
9 | }
10 | const { config, events } = useGetPropsAndEventByAttrs(attrs)
11 | expect(config).toEqual({ test: 'test' })
12 | expect(events).toMatchInlineSnapshot(`
13 | {
14 | "pointer.move": [Function],
15 | }
16 | `)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/packages/vue3/composables/useGetPropsAndEventByAttrs/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 | import { useGetPropsAndEventByAttrs } from '.'
3 |
4 | describe('test leafer_vue', () => {
5 | it('test leafer-vue.useGetPropsAndEventByAttrs', () => {
6 | const attrs = {
7 | onPointerMove: () => {},
8 | test: 'test',
9 | }
10 | const { config, events } = useGetPropsAndEventByAttrs(attrs)
11 | expect(config).toEqual({ test: 'test' })
12 | expect(events).toMatchInlineSnapshot(`
13 | {
14 | "pointer.move": [Function],
15 | }
16 | `)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/docs/guide/components/path/path.md:
--------------------------------------------------------------------------------
1 |
4 | # Path
5 | >
6 | > 创建路径。可以画出任意形状的图形,了解[绘图命令](https://www.leaferjs.com/ui/reference/interface/ui/PathData.html)。
7 | >
8 |
9 | ## 用法
10 |
11 |
12 |
13 | ## 属性
14 |
15 | | 名称 | 类型 | 默认值 | 说明 |
16 | | --- | --- | --- | --- |
17 | | path | ^[string] | —— | 路径数据,支持 SVG 与 Cavnas 绘图命令 |
18 | | windingRule | "nonzero" \| "evenodd" | nonzero | 路径缠绕规则 |
19 |
20 | > 详情查看[Path |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Path.html)。
21 | >
22 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
23 |
--------------------------------------------------------------------------------
/examples/vue3/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url'
2 |
3 | import vue from '@vitejs/plugin-vue'
4 | import { isCustomElement } from 'leafer-vue/compiler'
5 | import { defineConfig } from 'vite'
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | vue({
11 | template: {
12 | compilerOptions: {
13 | isCustomElement: tag => isCustomElement(tag, ['CustomRect']),
14 | },
15 | },
16 | }),
17 | ],
18 | resolve: {
19 | alias: {
20 | '@': fileURLToPath(new URL('./src', import.meta.url)),
21 | },
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/docs/guide/components/app/leafer.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Leafer
6 | >
7 | > 用于创建Leafer组件。
8 |
9 | ## 基础用法
10 |
11 |
12 | ## 属性
13 |
14 | | 名称 | 类型 | 默认值 | 说明 |
15 | | --- | --- | --- | --- |
16 | | width | ^[number] | —— | 宽度 |
17 | | height | ^[number] | —— | 高度 |
18 | | pixelRatio | ^[number] | 1 | 像素比 |
19 | | fill | ^[string] | —— | 填充色 |
20 | | hittable |^[boolean] | true | 是否响应交互事件 |
21 | > 更多属性请查看[Leafer |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Leafer.html)
22 | >
23 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
24 |
--------------------------------------------------------------------------------
/docs/guide/components/container/frame.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Frame
6 | >
7 | > 创建画板。默认会裁剪掉超出宽高的内容,类似于 HTML5 中的一个页面。
8 | >
9 |
10 | ## 用法
11 |
12 |
13 |
14 | ## 属性
15 |
16 | | 名称 | 类型 | 默认值 | 说明 |
17 | | --- | --- | --- | --- |
18 | | width | ^[number] | —— | 宽度 |
19 | | height | ^[number] | —— | 高度 |
20 | | fill | ^[string] | ^[#FFF] | 填充色 |
21 | | overflow | ^['show']\| ^['hide'] | ^['hide'] | 超出部分是否裁剪 |
22 |
23 | > 详情查看[Frame |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Frame.html)。
24 | >
25 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
26 |
--------------------------------------------------------------------------------
/docs/guide/components/image/image.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Image
6 | >
7 | > 图片对象,另外所有图形都支持通过[图案填充](https://www.leaferjs.com/ui/reference/property/paint/image.html)来显示图片。
8 | >
9 |
10 | ## 用法
11 |
12 |
13 |
14 | ## 属性
15 |
16 | | 名称 | 类型 | 默认值 | 说明 |
17 | | --- | --- | --- | --- |
18 | | width | ^[number] | —— | 宽度,默认使用图片原始宽度 |
19 | | height | ^[number] | —— | 高度, 默认使用图片原始高度 |
20 | | url | ^[string] | —— | 图片地址 |
21 |
22 | > 详情查看[Image |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Image.html)。
23 | >
24 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
25 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Drop/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/examples/vue2/src/assets/main.css:
--------------------------------------------------------------------------------
1 | @import "./base.css";
2 |
3 | #app {
4 | max-width: 1280px;
5 | margin: 0 auto;
6 | padding: 2rem;
7 |
8 | font-weight: normal;
9 | }
10 |
11 | a,
12 | .green {
13 | text-decoration: none;
14 | color: hsla(160, 100%, 37%, 1);
15 | transition: 0.4s;
16 | }
17 |
18 | @media (hover: hover) {
19 | a:hover {
20 | background-color: hsla(160, 100%, 37%, 0.2);
21 | }
22 | }
23 |
24 | @media (min-width: 1024px) {
25 | body {
26 | display: flex;
27 | place-items: center;
28 | }
29 |
30 | #app {
31 | display: grid;
32 | grid-template-columns: 1fr 1fr;
33 | padding: 0 2rem;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Swipe/index.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 | point.x -= 20"
17 | @swipe-right="() => point.x += 20"
18 | @swipe-up="() => point.y -= 20"
19 | @swipe-down="() => point.y += 20"
20 | />
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Drop/drop.md:
--------------------------------------------------------------------------------
1 | # DropEvent
2 | 拖放事件。
3 | > 详情请查看[DropEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Drop.html)
4 |
5 | ## 示例
6 |
7 | > 试着将
小绿方块拖放到
紫色的方块上,触发
drop 事件。
8 |
9 |
12 |
13 |
14 |
15 | ## 事件名称
16 |
17 | [IDropEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IDropEvent.html
18 |
19 | | 事件名 | 说明 | 参数 |
20 | | --- | --- | --- |
21 | | `drop` | 放置对象事件 | [`IDropEvent`][IDropEvent-url] |
22 |
--------------------------------------------------------------------------------
/docs/guide/components/custom/index.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Key/key.md:
--------------------------------------------------------------------------------
1 | # KeyEvent
2 |
3 | 键盘事件。
4 | 只能在 Leafer 实例上监听键盘事件。
5 | > 详情请查看[KeyEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Key.html)
6 |
7 | ## 示例
8 |
9 | > 按下方向键⌨️移动方块
10 |
11 |
14 |
15 |
16 |
17 | ## 事件名称
18 |
19 | [IKeyEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IKeyEvent.html
20 |
21 | | 事件名 | 说明 | 参数 |
22 | | --- | --- | --- |
23 | | `keyDown` | 按下按键事件, 长按会不断触发 | [`IKeyEvent`][IKeyEvent-url] |
24 | | `keyHold` | 按住按键事件, 长按只会触发一次 | [`IKeyEvent`][IKeyEvent-url] |
25 | | `keyUp` | 抬起按键事件 | [`IKeyEvent`][IKeyEvent-url] |
26 |
--------------------------------------------------------------------------------
/docs/guide/events/element/Image/image.md:
--------------------------------------------------------------------------------
1 | # ImageEvent
2 | 图片事件。
3 | > 详情请查看[ImageEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Image.html)
4 |
5 | ## 示例
6 |
7 |
10 |
11 |
12 |
13 | ## 事件名称
14 |
15 | [ILeaferImage-url]: https://www.leaferjs.com/ui/api/interfaces/ILeaferImage.html
16 |
17 | | 事件名 | 说明 | 参数 |
18 | | --- | --- | --- |
19 | | `imageLoad` | 图片开始加载 | [`ILeaferImage`][ILeaferImage-url] |
20 | | `imageLoaded` | 图片加载完成 | [`ILeaferImage`][ILeaferImage-url] |
21 | | `imageError` | 图片加载失败 | [`ILeaferImage`][ILeaferImage-url] |
22 |
--------------------------------------------------------------------------------
/docs/guide/components/image/canvas.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Canvas
6 | >
7 | > 画布对象,可以自由绘制、操作像素,或将其他图形直接绘制到 Canvas 上。
8 | >
9 |
10 | ## 用法
11 |
12 |
13 |
14 | ## 属性
15 |
16 | | 名称 | 类型 | 默认值 | 说明 |
17 | | --- | --- | --- | --- |
18 | | width | ^[number] | —— | 宽度 |
19 | | height | ^[number] | —— | 高度 |
20 | | pixelRatio | ^[number] | ^[1] | 像素比 |
21 | | smooth | ^[boolean] | ^[true] | 是否平滑绘制图像 |
22 | | contextSettings | ^[object] | —— | 画布上下文设置 |
23 |
24 | > 详情查看[Canvas |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Canvas.html)。
25 | >
26 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
27 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Layout/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
15 |
16 |
17 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Render/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
15 |
16 |
17 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Zoom/zoom.md:
--------------------------------------------------------------------------------
1 | # ZoomEvent
2 | 缩放事件,一般用于视窗交互中缩放视图。
3 | 1. 移动端: 双指捏合
4 | 2. 触摸板: 双指滑动
5 | 3. 鼠标: Ctrl / Command + 滚轮
6 | > 详情请查看[ZoomEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Zoom.html)
7 |
8 | ## 示例
9 |
10 |
13 |
14 |
15 |
16 | ## 事件名称
17 |
18 | [IZoomEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IZoomEvent.html
19 |
20 | | 事件名 | 说明 | 参数 |
21 | | --- | --- | --- |
22 | | `zoomStart` | 开始缩放事件 | [`IZoomEvent`][IZoomEvent-url] |
23 | | `zoom` | 缩放事件 | [`IZoomEvent`][IZoomEvent-url] |
24 | | `zoomEnd` | 结束缩放事件 | [`IZoomEvent`][IZoomEvent-url] |
25 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Rotate/index.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Rotate/rotate.md:
--------------------------------------------------------------------------------
1 | # RotateEvent
2 | 缩放事件,一般用于视窗交互中缩放视图。
3 | 1. 移动端: 双指旋转
4 | 2. 触摸板: 双指旋转(仅 Safari 支持)
5 | > 详情请查看[RotateEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Rotate.html)
6 |
7 | ## 示例
8 |
9 |
12 |
13 |
14 |
15 | ## 事件名称
16 |
17 | [IRotateEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IRotateEvent.html
18 |
19 | | 事件名 | 说明 | 参数 |
20 | | --- | --- | --- |
21 | | `rotateStart` | 开始旋转事件 | [`IRotateEvent`][IRotateEvent-url] |
22 | | `rotate` | 旋转事件 | [`IRotateEvent`][IRotateEvent-url] |
23 | | `rotateEnd` | 结束旋转事件 | [`IRotateEvent`][IRotateEvent-url] |
24 |
--------------------------------------------------------------------------------
/docs/guide/events/element/Property/property.md:
--------------------------------------------------------------------------------
1 | # PropertyEvent
2 | 元素属性事件,leafer.ready 事件之后才会派发此事件。
3 |
4 | > 详情请查看[PropertyEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Property.html)
5 |
6 | ## 示例
7 |
8 | > 点击方块修改宽度,触发`PropertyEvent`
9 |
10 |
13 |
14 |
15 |
16 | ## 事件名称
17 |
18 | [PropertyEvent-url]: https://www.leaferjs.com/ui/api/interfaces/PropertyEvent.html
19 |
20 | | 事件名 | 说明 | 参数 |
21 | | --- | --- | --- |
22 | | `propertyChange` | 同时派发给元素自身、Leafer 实例 | [`PropertyEvent`][PropertyEvent-url] |
23 | | `propertyLeafer_change` | 只派发给 Leafer 实例自身 | [`PropertyEvent`][PropertyEvent-url] |
24 |
--------------------------------------------------------------------------------
/docs/guide/components/app/leaferApp.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # LeaferApp
6 | >
7 | > 用于创建Leafer应用的根组件,所有的Leafer元素都必须为``的子组件。
8 | > LeaferApp的子组件必须为``用以初始化,可以有多个。
9 |
10 | ## 基础用法
11 |
12 |
13 | ## 属性
14 |
15 | | 属性名 | 类型 | 默认值 | 说明 |
16 | | --- | --- | --- | --- |
17 | | width | ^[number] | `800` | canvas画布的宽度 |
18 | | height | ^[number] | `600` | canvas画布的高度 |
19 |
20 | ## Exposes
21 |
22 | | 属性名 | 类型 | 说明 |
23 | | --- | --- | --- |
24 | | app | [`IApp`](https://www.leaferjs.com/ui/api/classes/App.html) | app组件实例 |
25 |
26 | > 更多属性请查看[App |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/App.html)
27 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/ellipse.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Ellipse
6 | >
7 | > 绘制圆、圆环、圆环扇形、扇形、弧线、椭圆。
8 | >
9 |
10 | ## 用法
11 |
12 |
13 |
14 | ## 属性
15 |
16 | | 名称 | 类型 | 默认值 | 说明 |
17 | | --- | --- | --- | --- |
18 | | width | ^[number] | —— | 宽度 |
19 | | height | ^[number] | —— | 高度 |
20 | | startAngle | ^[number] | —— | 弧形的起始`角度`, 取值范围为 -180 ~ 180 |
21 | | endAngle | ^[number] | —— | 弧形的结束`角度`, 取值范围为 -180 ~ 180 |
22 | | innerRadius | ^[number] | —— | 内半径比例, 取值范围为 0.0 ~ 1.0 |
23 |
24 | > 详情查看[Ellipse |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Ellipse.html)。
25 | >
26 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
27 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Swipe/swipe.md:
--------------------------------------------------------------------------------
1 | # SwipeEvent
2 | 滑动事件。
3 | > 详情请查看[SwipeEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Swipe.html)
4 |
5 | ## 示例
6 |
7 | > 使用鼠标按住进行滑动
8 |
9 |
12 |
13 |
14 |
15 | ## 事件名称
16 |
17 | [ISwipeEvent-url]: https://www.leaferjs.com/ui/api/interfaces/ISwipe.html
18 |
19 | | 事件名 | 说明 | 参数 |
20 | | --- | --- | --- |
21 | | `swipeLeft` | 向左滑动事件 | [`ISwipeEvent`][ISwipeEvent-url] |
22 | | `swipeRight` | 向右滑动事件 | [`ISwipeEvent`][ISwipeEvent-url] |
23 | | `swipeUp` | 向上滑动事件 | [`ISwipeEvent`][ISwipeEvent-url] |
24 | | `swipeDown` | 向下滑动事件 | [`ISwipeEvent`][ISwipeEvent-url] |
25 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Watch/index.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {{ changeData }}
15 |
16 |
19 |
20 |
21 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // Generated by unplugin-vue-components
5 | // Read more: https://github.com/vuejs/core/pull/3399
6 | export {}
7 |
8 | declare module 'vue' {
9 | export interface GlobalComponents {
10 | ApiTyping: typeof import('./.vitepress/theme/components/ApiTyping/index.vue')['default']
11 | HomePage: typeof import('./.vitepress/theme/components/HomePage.vue')['default']
12 | MoreStarts: typeof import('./.vitepress/theme/components/MoreStarts.vue')['default']
13 | Repl: typeof import('./.vitepress/theme/components/repl/index.vue')['default']
14 | Sponsors: typeof import('./.vitepress/theme/components/Sponsors.vue')['default']
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/guide/components/container/Group.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "iife",
4 | "kolorist",
5 | "pathe"
6 | ],
7 | "prettier.enable": false,
8 | "editor.formatOnSave": false,
9 |
10 | "editor.codeActionsOnSave": {
11 | "source.fixAll.eslint": "explicit",
12 | "source.organizeImports": "never"
13 | },
14 |
15 | "eslint.validate": [
16 | "javascript",
17 | "javascriptreact",
18 | "typescript",
19 | "typescriptreact",
20 | "vue",
21 | "html",
22 | "markdown",
23 | "json",
24 | "jsonc",
25 | "yaml",
26 | "toml",
27 | "xml",
28 | "gql",
29 | "graphql",
30 | "astro",
31 | "svelte",
32 | "css",
33 | "less",
34 | "scss",
35 | "pcss",
36 | "postcss"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Resize/index.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
19 |
Old Width: {{ oldValue }} ;
20 |
New Width: {{ width }} ;
21 |
22 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Move/move.md:
--------------------------------------------------------------------------------
1 | # MoveEvent
2 | 移动事件,一般用于视窗交互中平移视图。
3 | 1. 移动端: 双指滑动
4 | 2. 触摸板: 双指滑动
5 | 3. 鼠标: 滚轮(纵向平移),Shift + 滚轮(横向平移),鼠标中键 + 拖拽 (自由移动)
6 | 4. 拖拽: 拖拽元素到达视图边界时,会自动平移视图,以实现向外拖拽
7 | > 详情请查看[MoveEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Move.html)
8 |
9 | ## 示例
10 |
11 |
14 |
15 |
16 |
17 | ## 事件名称
18 |
19 | [IMoveEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IMoveEvent.html
20 |
21 | | 事件名 | 说明 | 参数 |
22 | | --- | --- | --- |
23 | | `moveStart` | 开始移动事件 | [`IMoveEvent`][IMoveEvent-url] |
24 | | `move` | 移动事件 | [`IMoveEvent`][IMoveEvent-url] |
25 | | `moveEnd` | 结束移动事件 | [`IMoveEvent`][IMoveEvent-url] |
26 |
--------------------------------------------------------------------------------
/examples/vue2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue2-example",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "run-p type-check build-only",
8 | "preview": "vite preview",
9 | "build-only": "vite build",
10 | "type-check": "vue-tsc --noEmit"
11 | },
12 | "dependencies": {
13 | "leafer-vue": "workspace:^",
14 | "vue": "^2.7.16"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^16.18.108",
18 | "@vitejs/plugin-legacy": "^2.3.1",
19 | "@vitejs/plugin-vue2": "^1.1.2",
20 | "@vue/tsconfig": "^0.1.3",
21 | "npm-run-all": "^4.1.5",
22 | "terser": "^5.32.0",
23 | "typescript": "~4.7.4",
24 | "vite": "^3.2.10",
25 | "vue-tsc": "^0.38.9"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Zoom/index.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/playground/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Move/index.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/packages/vue2/composables/useGetPropsAndEventByAttrs/index.ts:
--------------------------------------------------------------------------------
1 | import { getEventNameByAttrName } from './utils'
2 |
3 | export function useGetPropsAndEventByAttrs(attrs: Record) {
4 | const config: Record = {}
5 | const events: Record = {}
6 | Object.keys(attrs).forEach((key) => {
7 | if (key.startsWith('on'))
8 | events[getEventNameByAttrName(key)] = attrs[key]
9 |
10 | else config[key] = attrs[key]
11 | })
12 | return { config, events }
13 | }
14 |
15 | export function useGetPropsByAttrs(attrs: Record) {
16 | const config: Record = {}
17 | Object.keys(attrs).forEach((key) => {
18 | if (!key.startsWith('on'))
19 | config[key] = attrs[key]
20 | })
21 | return config
22 | }
23 |
--------------------------------------------------------------------------------
/packages/vue3/composables/useGetPropsAndEventByAttrs/index.ts:
--------------------------------------------------------------------------------
1 | import { getEventNameByAttrName } from './utils'
2 |
3 | export function useGetPropsAndEventByAttrs(attrs: Record) {
4 | const config: Record = {}
5 | const events: Record = {}
6 | Object.keys(attrs).forEach((key) => {
7 | if (key.startsWith('on'))
8 | events[getEventNameByAttrName(key)] = attrs[key]
9 |
10 | else config[key] = attrs[key]
11 | })
12 | return { config, events }
13 | }
14 |
15 | export function useGetPropsByAttrs(attrs: Record) {
16 | const config: Record = {}
17 | Object.keys(attrs).forEach((key) => {
18 | if (!key.startsWith('on'))
19 | config[key] = attrs[key]
20 | })
21 | return config
22 | }
23 |
--------------------------------------------------------------------------------
/docs/vite.config.ts:
--------------------------------------------------------------------------------
1 | import UnoCSS from 'unocss/vite'
2 | import Components from 'unplugin-vue-components/vite'
3 | import { defineConfig } from 'vite'
4 | import { groupIconVitePlugin } from 'vitepress-plugin-group-icons'
5 |
6 | export default defineConfig({
7 | optimizeDeps: {
8 | exclude: [
9 | 'vitepress',
10 | ],
11 | },
12 | server: {
13 | hmr: {
14 | overlay: false,
15 | },
16 | },
17 |
18 | ssr: {
19 | noExternal: ['naive-ui', 'vueuc'],
20 | },
21 | plugins: [
22 | UnoCSS(),
23 | Components({
24 | dirs: [
25 | '.vitepress/theme/components',
26 | ],
27 | include: [
28 | /\.vue$/,
29 | /\.vue\?vue/,
30 | /\.md$/,
31 | ],
32 | }),
33 | groupIconVitePlugin(),
34 | ],
35 | })
36 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/line.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Line
6 | >
7 | > 绘制横线、斜线、竖线、折线、平滑曲线。
8 | >
9 |
10 | ## 用法
11 |
12 |
13 |
14 | ## 属性
15 |
16 | | 名称 | 类型 | 默认值 | 说明 |
17 | | --- | --- | --- | --- |
18 | | x | ^[number] | —— | x 轴起点 |
19 | | y | ^[number] | —— | y 轴起点 |
20 | | width | ^[number] | —— | 线的长度 |
21 | | strokeWidth | ^[number] | 1 | 线的宽度 |
22 | | stroke | ^[string] | —— | 线的颜色 |
23 | | rotation | ^[number] | —— | 旋转`角度`, 取值范围: -180 ~ 180 |
24 | | toPoint | {x:`^[number]`, y:`^[number]`} | —— | 画到某一点 setter(相对于元素起点的坐标), 自动换算出 width 与 rotation |
25 |
26 | > 详情查看[Line |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Line.html)。
27 | >
28 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
29 |
--------------------------------------------------------------------------------
/packages/vue2/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { bold, gray } from 'kolorist'
2 | import { defineConfig } from 'tsup'
3 | import pkg from './package.json'
4 |
5 | export default defineConfig ((options) => {
6 | const buildBanner = `\n ☘️ ☘️ ☘️ ${bold('leafer-vu@2')} ${gray(`v${pkg.version}`)} \n`
7 |
8 | return {
9 | entry: ['index.ts', 'resolver.ts'],
10 | format: options.watch ? 'esm' : ['cjs', 'esm', 'iife'],
11 | target: 'node14',
12 | tsconfig: './tsconfig.json',
13 | clean: true,
14 | external: [
15 | 'vue',
16 | 'leafer-ui',
17 | ],
18 | minify: !options.watch,
19 | publicDir: 'types',
20 | onSuccess: async () => {
21 | // eslint-disable-next-line no-console
22 | console.log(buildBanner)
23 | return Promise.resolve()
24 | },
25 | }
26 | })
27 |
--------------------------------------------------------------------------------
/docs/guide/events/element/Child/index.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
21 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/playground/src/template/welcome.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/guide/components/app/leaferApp.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/packages/vue3/types/LeaferVueComponent.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AllowedComponentProps,
3 | ComponentCustomProps,
4 | ComponentOptionsMixin,
5 | ComputedOptions,
6 | DefineComponent,
7 | EmitsOptions,
8 | ExtractPropTypes,
9 | MethodOptions,
10 | VNodeProps,
11 | } from 'vue'
12 | import type { LeaferBaseEvent } from './LeaferEventType'
13 |
14 | type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps
15 |
16 | type _AnyRecord = Record
17 |
18 | export type LeaferVueComponent<
19 | P extends _AnyRecord = _AnyRecord,
20 | > = DefineComponent<
21 | object,
22 | object,
23 | object,
24 | ComputedOptions,
25 | MethodOptions,
26 | ComponentOptionsMixin,
27 | ComponentOptionsMixin,
28 | EmitsOptions,
29 | string,
30 | PublicProps,
31 | Readonly>
32 | >
33 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { Theme } from 'vitepress'
2 | import DefaultTheme from 'vitepress/theme'
3 | import { h } from 'vue'
4 | import ApiTyping from './components/ApiTyping/index.vue'
5 | import Layout from './Layout.vue'
6 | import 'virtual:group-icons.css'
7 | import 'uno.css'
8 | import './var.css'
9 | import './blur.css'
10 |
11 | export default {
12 | extends: DefaultTheme,
13 | Layout: () => {
14 | return h(Layout)
15 | },
16 | enhanceApp: async ({ app }) => {
17 | app.component('ApiTyping', ApiTyping)
18 | if (!import.meta.env.SSR) {
19 | const Repl = (await import('./components/repl/index.vue')).default
20 | app.component('Repl', Repl)
21 | const MoreStarts = (await import('./components/MoreStarts.vue')).default
22 | app.component('MoreStart', MoreStarts)
23 | }
24 | },
25 | } satisfies Theme
26 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Drag/drag.md:
--------------------------------------------------------------------------------
1 | # DragEvent
2 | 拖动事件。
3 | > 详情请查看[DragEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Drag.html)
4 |
5 | ## 示例
6 |
9 |
10 |
11 |
12 | ## 事件名称
13 |
14 | [IDragEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IDragEvent.html
15 |
16 | | 事件名 | 说明 | 参数 |
17 | | --- | --- | --- |
18 | | `dragStart` | 开始拖动事件 | [`IDragEvent`][IDragEvent-url] |
19 | | `drag` | 拖动事件中 | [`IDragEvent`][IDragEvent-url] |
20 | | `dragEnd` | 结束拖动事件 | [`IDragEvent`][IDragEvent-url] |
21 | | `dragOver` | 拖动元素 over | [`IDragEvent`][IDragEvent-url] |
22 | | `dragOut` | 拖动元素 out | [`IDragEvent`][IDragEvent-url] |
23 | | `dragEnter` | 拖动元素进入 | [`IDragEvent`][IDragEvent-url] |
24 | | `dragLeave` | 拖动元素离开 | [`IDragEvent`][IDragEvent-url] |
25 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "type": "module",
4 | "version": "0.0.3",
5 | "private": true,
6 | "description": "",
7 | "author": "FliPPeDround ",
8 | "license": "MIT",
9 | "scripts": {
10 | "docs:dev": "vitepress dev",
11 | "docs:build": "vitepress build",
12 | "docs:preview": "vitepress preview"
13 | },
14 | "dependencies": {
15 | "@vue/repl": "4.3.1",
16 | "@vueuse/core": "^10.11.1",
17 | "leafer-vue": "workspace:^",
18 | "vue": "^3.5.4"
19 | },
20 | "devDependencies": {
21 | "@iconify-json/file-icons": "^1.2.0",
22 | "@iconify-json/vscode-icons": "^1.2.1",
23 | "@vue/tsconfig": "^0.5.1",
24 | "leafer-ui": "^1.0.2",
25 | "naive-ui": "^2.39.0",
26 | "vite": "^5.4.8",
27 | "vitepress": "^1.3.4",
28 | "vitepress-plugin-group-icons": "^1.2.4"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/docs/guide/components/container/Box.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
14 |
20 |
25 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/playground/src/components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-nocheck
3 | // Generated by unplugin-vue-components
4 | // Read more: https://github.com/vuejs/core/pull/3399
5 | export {}
6 |
7 | /* prettier-ignore */
8 | declare module 'vue' {
9 | export interface GlobalComponents {
10 | ElForm: typeof import('element-plus/es')['ElForm']
11 | ElFormItem: typeof import('element-plus/es')['ElFormItem']
12 | ElOption: typeof import('element-plus/es')['ElOption']
13 | ElPopover: typeof import('element-plus/es')['ElPopover']
14 | ElSelect: typeof import('element-plus/es')['ElSelect']
15 | Header: typeof import('./components/Header.vue')['default']
16 | Settings: typeof import('./components/Settings.vue')['default']
17 | }
18 | export interface ComponentCustomProperties {
19 | vLoading: typeof import('element-plus/es')['ElLoadingDirective']
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/intro/vue2.md:
--------------------------------------------------------------------------------
1 | # Vue2
2 |
3 | 因为vue2不支持自定义渲染器,所以采用封装组件的方式,实现leafer-ui组件化
4 | > 性能不及vue3版本
5 |
6 | ## 快速安装
7 |
8 | ::: code-group
9 |
10 | ```bash [pnpm]
11 | pnpm i leafer-vue@2
12 | ```
13 |
14 | ```bash [yarn]
15 | yarn add leafer-vue@2
16 | ```
17 |
18 | ```bash [npm]
19 | npm i leafer-vue@2
20 | ```
21 |
22 | ```bash [bun]
23 | bun add leafer-vue@2
24 | ```
25 |
26 | :::
27 | ::: code-group
28 |
29 | ```vue [App.vue]
30 |
33 |
34 |
35 |
36 |
37 |
42 |
43 |
44 |
45 | ```
46 | :::
47 |
--------------------------------------------------------------------------------
/docs/guide/events/element/Property/index.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
Old Width: {{ oldValue }} ;
19 |
New Width: {{ newValue }} ;
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/vue2/index.ts:
--------------------------------------------------------------------------------
1 | import { lfContainer } from './components/Container'
2 | import { lfNode } from './components/Node'
3 |
4 | // Root
5 | export * from './components/Root/app'
6 | export * from './components/Root/leafer'
7 |
8 | // Containers
9 | export const lfFrame = lfContainer('Frame')
10 | export const lfBox = lfContainer('Box')
11 | export const lfGroup = lfContainer('Group')
12 |
13 | // Geometries
14 | export const lfRect = lfNode('Rect')
15 | export const lfEllipse = lfNode('Ellipse')
16 | export const lfLine = lfNode('Line')
17 | export const lfPolygon = lfNode('Polygon')
18 | export const lfStar = lfNode('Star')
19 |
20 | // Images
21 | export const lfImage = lfNode('Image')
22 | export const lfCanvas = lfNode('Canvas')
23 |
24 | // Text
25 | export const lfText = lfNode('Text')
26 |
27 | // Path
28 | export const lfPath = lfNode('Path')
29 | export const lfPen = lfNode('Pen')
30 |
--------------------------------------------------------------------------------
/docs/guide/events/element/Child/child.md:
--------------------------------------------------------------------------------
1 | # ChildEvent
2 | Child 事件。
3 |
4 | `leafer.ready` 事件之后才会派发此事件,想在 ready 前 [执行相关事件](https://www.leaferjs.com/ui/reference/property/layer.html#waitparent)?
5 |
6 | 事件派发的顺序为:子元素、父元素、Leafer 实例,[渲染生命周期](https://www.leaferjs.com/ui/reference/life/render.html) 中会监听此事件。
7 | > 详情请查看[ChildEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Child.html)
8 |
9 | ## 示例
10 |
11 |
14 |
15 |
16 |
17 | ## 事件名称
18 |
19 | ## 事件名称
20 |
21 | [IChildEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IChildEvent.html
22 |
23 | | 事件名 | 说明 | 参数 |
24 | | --- | --- | --- |
25 | | `childAdd` | 添加元素 | [`IChildEvent`][IChildEvent-url] |
26 | | `childRemove` | 删除元素 | [`IChildEvent`][IChildEvent-url] |
27 | | `childDestroy` | 销毁元素 (仅派发给元素自身) | [`IChildEvent`][IChildEvent-url] |
28 |
--------------------------------------------------------------------------------
/examples/vue3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-example",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "run-p type-check build-only",
8 | "preview": "vite preview",
9 | "build-only": "vite build",
10 | "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false"
11 | },
12 | "dependencies": {
13 | "@leafer-in/arrow": "^1.0.4",
14 | "@leafer-in/editor": "^1.0.4",
15 | "leafer-flex-plugin": "^0.0.2",
16 | "leafer-ui": "^1.0.4",
17 | "leafer-vue": "workspace:^",
18 | "vue": "^3.5.4"
19 | },
20 | "devDependencies": {
21 | "@tsconfig/node18": "^2.0.1",
22 | "@types/node": "^18.19.50",
23 | "@vitejs/plugin-vue": "^4.6.2",
24 | "@vue/tsconfig": "^0.4.0",
25 | "npm-run-all": "^4.1.5",
26 | "typescript": "~5.0.4",
27 | "vite": "^4.5.3",
28 | "vue-tsc": "^1.8.27"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/vue2/components/Root/app.ts:
--------------------------------------------------------------------------------
1 | import { useEffectUpdate, useGetPropsByAttrs, useInsertBefore, useSomeNode } from '@/composables'
2 | import { App } from 'leafer-ui'
3 | import { defineComponent, onMounted } from 'vue-demi'
4 |
5 | export const lfApp = defineComponent({
6 | name: 'LfApp',
7 | inheritAttrs: false,
8 | setup(_, { slots, expose, attrs }) {
9 | useSomeNode(slots, 'LfLeafer', 'LfApp')
10 | const config = useGetPropsByAttrs(attrs)
11 | const canvas = document.createElement('canvas')
12 | document.body.appendChild(canvas)
13 | const container = new App({
14 | ...config,
15 | view: canvas,
16 | start: false,
17 | })
18 |
19 | onMounted(() => {
20 | useInsertBefore(canvas)
21 | container.start()
22 | })
23 |
24 | useEffectUpdate(attrs, container)
25 |
26 | expose(container)
27 |
28 | return () => slots.default?.()
29 | },
30 | })
31 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Conditional Deploy Based on Commit Message
2 |
3 | on: push
4 |
5 | jobs:
6 | check_commit:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout code
10 | uses: actions/checkout@v2
11 |
12 | - name: Get commit message
13 | id: commit-message
14 | run: echo "::set-output name=message::$(git log -1 --pretty=%B)"
15 |
16 | - name: Check if commit starts with 'docs'
17 | id: check-starts
18 | run: |
19 | if [[ "${{ steps.commit-message.outputs.message }}" == docs* ]]; then
20 | echo "::set-output name=trigger::true"
21 | else
22 | echo "::set-output name=trigger::false"
23 | fi
24 |
25 | - name: Deploy to Netlify
26 | if: steps.check-starts.outputs.trigger == 'true'
27 | run: curl -X POST -d {} ${{ secrets.NETLIFY_BUILD_HOOK_URL }}
28 |
--------------------------------------------------------------------------------
/.github/workflows/release2.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | permissions:
4 | contents: write
5 |
6 | on:
7 | push:
8 | tags:
9 | - 'v2*'
10 |
11 | jobs:
12 | release:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Install pnpm
20 | uses: pnpm/action-setup@v2
21 |
22 | - name: Set node
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: 18.x
26 | cache: pnpm
27 | registry-url: https://registry.npmjs.org
28 |
29 | - run: npx changelogithub
30 | env:
31 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
32 |
33 | - run: corepack enable
34 | - run: pnpm install
35 | - run: cd packages/vue2 && pnpm publish --access public --no-git-checks
36 | env:
37 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/release3.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | permissions:
4 | contents: write
5 |
6 | on:
7 | push:
8 | tags:
9 | - 'v3*'
10 |
11 | jobs:
12 | release:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Install pnpm
20 | uses: pnpm/action-setup@v2
21 |
22 | - name: Set node
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: 18.x
26 | cache: pnpm
27 | registry-url: https://registry.npmjs.org
28 |
29 | - run: npx changelogithub
30 | env:
31 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
32 |
33 | - run: corepack enable
34 | - run: pnpm install
35 | - run: cd packages/vue3 && pnpm publish --access public --no-git-checks
36 | env:
37 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
38 |
--------------------------------------------------------------------------------
/docs/guide/components/container/Frame.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
12 |
20 |
21 |
28 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/guide/other/export/export.md:
--------------------------------------------------------------------------------
1 | # 导出
2 | 会等待视图内所有网络资源都加载完再进行导出图片。
3 | > 详情查看[export |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/property/export.html)。
4 |
5 | ::: code-group
6 |
7 | ```vue [App.vue]
8 |
20 |
21 |
22 |
25 |
26 |
27 |
34 |
35 |
36 |
37 | ```
38 | :::
39 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Layout/layout.md:
--------------------------------------------------------------------------------
1 | # LayoutEvent
2 | 布局事件。
3 |
4 | > 详情请查看[LayoutEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Layout.html)
5 |
6 | ## 示例
7 |
8 | > 点击按钮添加新元素,重新触发布局事件。
9 |
10 |
13 |
14 |
15 |
16 | ## 事件名称
17 |
18 | [ILayoutEvent-url]: https://www.leaferjs.com/ui/api/interfaces/ILayoutEvent.html
19 |
20 | | 事件名 | 说明 | 参数 |
21 | | --- | --- | --- |
22 | | `layoutRequest` | 请求布局 | [`ILayoutEvent`][ILayoutEvent-url] |
23 | | `layoutStart` | 开始本轮布局 | [`ILayoutEvent`][ILayoutEvent-url] |
24 | | `layoutBefore` | 单次布局前 | [`ILayoutEvent`][ILayoutEvent-url] |
25 | | `layout` | 单词布局,可进行多次 | [`ILayoutEvent`][ILayoutEvent-url] |
26 | | `layoutAfter` | 单词布局后 | [`ILayoutEvent`][ILayoutEvent-url] |
27 | | `layoutAgain` | 准备再次布局 | [`ILayoutEvent`][ILayoutEvent-url] |
28 | | `layoutEnd` | 结束本轮布局 | [`ILayoutEvent`][ILayoutEvent-url] |
29 |
--------------------------------------------------------------------------------
/examples/vue3/src/App.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
21 |
28 |
29 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Key/index.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/docs/guide/components/image/Image.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
31 | loading = false"
34 | />
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/docs/guide/components/image/Canvas.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/Star.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs/.vitepress/plugin/tooltip.ts:
--------------------------------------------------------------------------------
1 | import type { MarkdownRenderer } from 'vitepress'
2 |
3 | export default (md: MarkdownRenderer): void => {
4 | md.renderer.rules.tooltip = (tokens, idx) => {
5 | const token = tokens[idx]
6 |
7 | return ``
8 | }
9 |
10 | md.inline.ruler.before('emphasis', 'tooltip', (state, silent) => {
11 | const tooltipRegExp = /^\^\[([^\]]*)\](`[^`]*`)?/
12 | const str = state.src.slice(state.pos, state.posMax)
13 |
14 | if (!tooltipRegExp.test(str))
15 | return false
16 | if (silent)
17 | return true
18 |
19 | const result = str.match(tooltipRegExp)
20 |
21 | if (!result)
22 | return false
23 |
24 | const token = state.push('tooltip', 'tooltip', 0)
25 | token.content = result[1].replace(/\\\|/g, '|')
26 | token.info = (result[2] || '').replace(/^`(.*)`$/, '$1')
27 | token.level = state.level
28 | state.pos += result[0].length
29 |
30 | return true
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/docs/guide/start/index.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
25 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Leafer/leafer.md:
--------------------------------------------------------------------------------
1 | # LeaferEvent
2 | Leafer 事件。
3 |
4 | > 详情请查看[LeaferEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Leafer.html)
5 |
6 | ## 示例
7 |
10 |
11 |
12 |
13 | ## 事件名称
14 |
15 | [ILeaferEvent-url]: https://www.leaferjs.com/ui/api/interfaces/ILeaferEvent.html
16 |
17 | | 事件名 | 说明 | 参数 |
18 | | --- | --- | --- |
19 | | `leaferStart` | 启动应用 | [`ILeaferEvent`][ILeaferEvent-url] |
20 | | `leaferBefore_ready` | 应用准备就绪前 | [`ILeaferEvent`][ILeaferEvent-url] |
21 | | `leaferReady` | 应用准备就绪(首次布局完成) | [`ILeaferEvent`][ILeaferEvent-url] |
22 | | `leaferAfter_ready` | 应用准备就绪后 | [`ILeaferEvent`][ILeaferEvent-url] |
23 | | `leaferView_ready` | 视图准备就绪(首次渲染完成) | [`ILeaferEvent`][ILeaferEvent-url] |
24 | | `leaferStop` | 应用停止 | [`ILeaferEvent`][ILeaferEvent-url] |
25 | | `leaferRestart` | 应用重启 | [`ILeaferEvent`][ILeaferEvent-url] |
26 | | `leaferEnd` | 结束应用(即将销毁) | [`ILeaferEvent`][ILeaferEvent-url] |
27 |
--------------------------------------------------------------------------------
/packages/vue2/components/Node/index.ts:
--------------------------------------------------------------------------------
1 | import type { Node } from './constants'
2 | import { useCreateEvents, useEffectUpdate, useGetContainer, useGetPropsAndEventByAttrs } from '@/composables'
3 | import { defineComponent, getCurrentInstance, onUnmounted } from 'vue-demi'
4 | import { createNode } from './createNode'
5 |
6 | export function lfNode(NodeName: Node) {
7 | return defineComponent({
8 | name: `Lf${NodeName}`,
9 | inheritAttrs: false,
10 | setup(_, { attrs, expose }) {
11 | const allAttrs = getCurrentInstance()?.proxy?.$attrs || {}
12 | const { events, config } = useGetPropsAndEventByAttrs(allAttrs)
13 | const instance = createNode(NodeName, config)
14 | const container = useGetContainer()
15 | container.add(instance)
16 | expose(instance)
17 |
18 | useEffectUpdate(attrs, instance)
19 | useCreateEvents(events, instance)
20 |
21 | onUnmounted(() => {
22 | container.remove(instance)
23 | })
24 |
25 | return () => null
26 | },
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/docs/guide/events/leafer/Render/render.md:
--------------------------------------------------------------------------------
1 | # RenderEvent
2 | 渲染事件。
3 |
4 | > 详情请查看[RenderEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/basic/Render.html)
5 |
6 | ## 示例
7 |
8 | > 点击按钮添加新元素,重新触发渲染事件。
9 |
10 |
13 |
14 |
15 |
16 | ## 事件名称
17 |
18 | [IRenderEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IRenderEvent.html
19 |
20 | | 事件名 | 说明 | 参数 |
21 | | --- | --- | --- |
22 | | `renderRequest` | 请求渲染 | [`IRenderEvent`][IRenderEvent-url] |
23 | | `renderStart` | 开始本轮渲染 | [`IRenderEvent`][IRenderEvent-url] |
24 | | `renderBefore` | 单次渲染前 | [`IRenderEvent`][IRenderEvent-url] |
25 | | `render` | 单词渲染,可进行多次 | [`IRenderEvent`][IRenderEvent-url] |
26 | | `renderAfter` | 单词渲染后 | [`IRenderEvent`][IRenderEvent-url] |
27 | | `renderAgain` | 准备再次渲染 | [`IRenderEvent`][IRenderEvent-url] |
28 | | `renderEnd` | 结束本轮渲染 | [`IRenderEvent`][IRenderEvent-url] |
29 | | `renderNext` | 本轮渲染已完成,预备下一次渲染,可以用于下一帧前的 动画 计算 | [`IRenderEvent`][IRenderEvent-url] |
30 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/Rect.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/packages/vue3/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import { bold, gray } from 'kolorist'
3 | import { defineConfig } from 'tsup'
4 | import pkg from './package.json'
5 |
6 | export default defineConfig ((options) => {
7 | const buildBanner = `\n ☘️ ☘️ ☘️ ${bold('leafer-vue@3')} ${gray(`v${pkg.version}`)} \n`
8 | const isWatch = options.watch
9 | return {
10 | entry: ['index.ts', 'compiler.ts'],
11 | format: ['cjs', 'esm'],
12 | target: 'node18',
13 | tsconfig: './tsconfig.json',
14 | clean: true,
15 | minify: !isWatch,
16 | dts: {
17 | entry: ['compiler.ts', 'types/index.ts'],
18 | },
19 | onSuccess: async () => {
20 | const leaferVueEsm = fs.readFileSync('./dist/index.js', 'utf-8')
21 | fs.writeFileSync('./../../docs/public/leafer-vue.proxy.js', leaferVueEsm)
22 | if (isWatch) {
23 | fs.writeFileSync('./../../playground/public/leafer-vue.proxy.js', leaferVueEsm)
24 | }
25 | // eslint-disable-next-line no-console
26 | console.log(buildBanner)
27 | return Promise.resolve()
28 | },
29 | }
30 | })
31 |
--------------------------------------------------------------------------------
/docs/guide/components/path/Pen.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
31 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/docs/plugin/index.md:
--------------------------------------------------------------------------------
1 | # 插件中心
2 |
3 | leafer-vue完全兼容leafer插件,你可以通过插件来扩展leafer-vue的功能。
4 | > 详情请查看🛒[leafer插件中心](https://www.leaferjs.com/ui/plugin/)。
5 |
6 | ## 示例
7 |
8 | ### 图形编辑器
9 |
10 | #### 安装
11 |
12 | ::: code-group
13 |
14 | ```bash [pnpm]
15 | pnpm i @leafer-in/editor
16 | ```
17 |
18 | ```bash [yarn]
19 | yarn add @leafer-in/editor
20 | ```
21 |
22 | ```bash [npm]
23 | npm i @leafer-in/editor
24 | ```
25 |
26 | ```bash [bun]
27 | bun add @leafer-in/editor
28 | ```
29 | :::
30 |
31 | #### 体验
32 |
35 |
46 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | hero:
5 | name: "leafer-vue"
6 | text: "vue渲染器"
7 | tagline: "使用vue开发leafer应用程序"
8 | image: /logo.png
9 | actions:
10 | - theme: brand
11 | text: 快速开始
12 | link: /guide/start/introduction
13 | - theme: alt
14 | text: 在 GitHub 上查看
15 | link: https://github.com/FliPPeDround/leafer-vue
16 | - theme: alt
17 | text: 演练场
18 | link: https://leafer-vue.netlify.app/play/
19 |
20 | features:
21 | - title: Vue渲染器
22 | icon:
23 | details: 使用vue渲染器创建leafer应用程序,高性能
24 | - title: 类型安全
25 | icon: 🗃️
26 | details: 由TypeScript编写,提供强大的类型支持
27 | - title: 在线演练场
28 | icon: 🎪
29 | details: 提供在线演练场,支持运行leafer-vue应用程序
30 | link: https://leafer-vue.netlify.app/play/
31 | - title: 无需打包
32 | icon: ☁️
33 | details: 提供CDN引用,无需打包
34 | ---
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 FliPPeDround
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/vue2/components/Container/index.ts:
--------------------------------------------------------------------------------
1 | import type { Container } from './types'
2 | import { useCreateEvents, useEffectUpdate, useGetContainer, useGetParentNodeName, useGetPropsAndEventByAttrs } from '@/composables'
3 | import { defineComponent, getCurrentInstance, h, onUnmounted } from 'vue-demi'
4 | import { createContainer } from './createContainer'
5 |
6 | export function lfContainer(containerName: Container) {
7 | return defineComponent({
8 | name: `Lf${containerName}`,
9 | inheritAttrs: false,
10 | setup(_, { slots, expose, attrs }) {
11 | useGetParentNodeName()
12 | const allAttrs = getCurrentInstance()?.proxy?.$attrs || {}
13 | const { events, config } = useGetPropsAndEventByAttrs(allAttrs)
14 | const instance = createContainer(containerName, config)
15 | const container = useGetContainer()
16 | container.add(instance)
17 | expose({ container: instance })
18 |
19 | useEffectUpdate(attrs, instance)
20 | useCreateEvents(events, instance)
21 |
22 | onUnmounted(() => {
23 | container.remove(instance)
24 | })
25 |
26 | return () => h('template', slots.default?.())
27 | },
28 | })
29 | }
30 |
--------------------------------------------------------------------------------
/packages/vue3/composables/useLogger.ts:
--------------------------------------------------------------------------------
1 | interface colorInfo {
2 | color?: string
3 | content?: any
4 | backgroundColor?: string
5 | }
6 |
7 | interface LogDataType {
8 | content: string
9 | css: string[]
10 | }
11 | export function useLogger() {
12 | function log(colorInfo: colorInfo[]) {
13 | const logData: LogDataType = {
14 | content: '',
15 | css: [],
16 | }
17 | colorInfo.unshift({
18 | content: ' [Leafer-vue warn:] ',
19 | color: '#FFF',
20 | })
21 | colorInfo.forEach((item, index) => {
22 | logData.content += `%c${item?.content ?? ' '}`
23 | logData.css.push(
24 | `${item?.color ? `color:${item.color}` : ''}`
25 | + `${item?.backgroundColor ? `;background:${item.backgroundColor}` : ''}`
26 | + ';padding: 0px'
27 | + `${index === 0 ? ';border-top-left-radius: 25px; border-bottom-left-radius: 8px' : ''}`
28 | + `${index === colorInfo.length - 1 ? ';border-top-right-radius: 8px; border-bottom-right-radius: 8px' : ''}`,
29 | )
30 | })
31 |
32 | console.warn(
33 | `${logData.content}`,
34 | ...logData.css,
35 | )
36 | }
37 |
38 | return {
39 | log,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/Ellipse.vue:
--------------------------------------------------------------------------------
1 |
42 |
43 |
44 |
45 |
46 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/docs/guide/components/text/text.md:
--------------------------------------------------------------------------------
1 |
4 | # Text
5 | >
6 | > 绘制文本。与 HTML5 文本显示效果基本一致。
7 | >
8 |
9 | ## 用法
10 |
11 |
12 |
13 | ## 属性
14 |
15 | | 名称 | 类型 | 默认值 | 说明 |
16 | | --- | --- | --- | --- |
17 | | width | ^[number] | —— | 文本框宽度,不设置时为自动宽度。 |
18 | | height | ^[number] | —— | 文本框高度,不设置时为自动高度。 |
19 | | text | ^[string] | —— | 文本内容 |
20 | | fontFamily | ^[string] | —— | 字体 |
21 | | fontSize | ^[number] | —— | 文字大小 |
22 | | fontWeight | ^[number] | —— | 文字粗细 |
23 | | italic | ^[boolean] | false | 是否斜体 |
24 | | textCase | ^[string] | —— | 文字大小写 |
25 | | textDecoration | ^[string] | —— | 文字下划线或删除线 |
26 | | letterSpacing | ^[number] | —— | 字间距 |
27 | | lineHeight | ^[number] | —— | 行高 |
28 | | textOverflow | ^[string] | —— | 文字溢出处理 |
29 | | paraIndent | ^[number] | —— | 段落首行缩进,单位为 px |
30 | | paraSpacing | ^[number] | —— | 段落间距,单位为 px |
31 | | textAlign | ^[string] | —— | 文字对齐方式 |
32 | | verticalAlign | ^[string] | —— | 文字垂直对齐方式 |
33 | | padding | `number[]` | —— | 文本内边距,可分别设置 4 个值 |
34 |
35 | > 更多属性请查看[Text |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/display/Text.html)
36 | >
37 | > 事件请查看[事件处理 |🌿 Leafer Vue](/guide/events/events)
38 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/ApiTyping/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 | {{ type.slice(1) }}
14 |
15 |
16 | {{ type }}
17 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
32 |
33 |
34 | {{ details }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/Polygon.vue:
--------------------------------------------------------------------------------
1 |
45 |
46 |
47 |
48 |
49 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/packages/uni-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@leafer-vue/uni-app",
3 | "type": "module",
4 | "version": "0.0.3",
5 | "description": "",
6 | "author": "FliPPeDround ",
7 | "license": "MIT",
8 | "homepage": "https://github.com/FliPPeDround/leafer-vue#readme",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/FliPPeDround/leafer-vue.git"
12 | },
13 | "bugs": "https://github.com/FliPPeDround/leafer-vue/issues",
14 | "keywords": [],
15 | "sideEffects": false,
16 | "exports": {
17 | ".": {
18 | "types": "./dist/index.d.ts",
19 | "import": "./dist/index.js",
20 | "require": "./dist/index.cjs"
21 | },
22 | "./*": "./*"
23 | },
24 | "main": "./dist/index.js",
25 | "module": "./dist/index.js",
26 | "unpkg": "dist/index.global.js",
27 | "types": "./dist/index.d.ts",
28 | "files": [
29 | "dist"
30 | ],
31 | "scripts": {
32 | "build": "tsup",
33 | "dev": "tsup --watch",
34 | "lint": "eslint .",
35 | "lint:fix": "eslint --fix .",
36 | "prepublishOnly": "nr build",
37 | "release": "bumpp && npm publish",
38 | "test": "vitest",
39 | "typecheck": "tsc --noEmit"
40 | },
41 | "peerDependencies": {
42 | "leafer-ui": "1.0.0-rc.6"
43 | },
44 | "devDependencies": {
45 | "vue": "^3.5.4"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/docs/guide/components/shape/Line.vue:
--------------------------------------------------------------------------------
1 |
46 |
47 |
48 |
49 |
50 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leafer-vue-playground",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "private": true,
6 | "packageManager": "pnpm@9.9.0",
7 | "description": "Leafer Vue Playground",
8 | "license": "MIT",
9 | "keywords": [
10 | "Leafer",
11 | "leafer-ui",
12 | "playground",
13 | "repl"
14 | ],
15 | "scripts": {
16 | "dev": "vite",
17 | "build": "vite build",
18 | "preview": "vite preview",
19 | "typecheck": "vue-tsc --noEmit"
20 | },
21 | "dependencies": {
22 | "@unocss/reset": "^0.62.3",
23 | "@vue/repl": "4.4.1",
24 | "@vueuse/core": "^11.0.3",
25 | "element-plus": "^2.8.2",
26 | "semver": "^7.6.3",
27 | "vue": "^3.5.3"
28 | },
29 | "devDependencies": {
30 | "@iconify-json/ri": "^1.2.0",
31 | "@types/node": "^22.5.4",
32 | "@types/semver": "^7.5.8",
33 | "@unocss/transformer-directives": "^0.62.3",
34 | "@vitejs/plugin-vue": "^5.1.3",
35 | "@vue/tsconfig": "^0.5.1",
36 | "bumpp": "^9.5.2",
37 | "eslint": "^9.10.0",
38 | "sass": "^1.78.0",
39 | "typescript": "^5.5.4",
40 | "unocss": "^0.62.3",
41 | "unplugin-auto-import": "^0.18.2",
42 | "unplugin-vue-components": "^0.27.4",
43 | "vite": "^5.4.3",
44 | "vite-plugin-inspect": "^0.8.7",
45 | "vite-plugin-mkcert": "^1.17.6",
46 | "vue-tsc": "^2.1.6"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/guide/other/animate/animate.md:
--------------------------------------------------------------------------------
1 | # 动画
2 | 动画功能,支持延时、摇摆循环、seek、动画事件,可制作过渡动画、关键帧动画、路径动画。
3 | > 详情查看[animate |🌿 Leafer UI](https://www.leaferjs.com/ui/guide/plugin/animate.html)
4 |
5 | ::: info
6 | 需安装 动画插件 才能使用。
7 | :::
8 |
9 | ::: code-group
10 |
11 | ```bash [pnpm]
12 | pnpm i @leafer-in/animate
13 | ```
14 |
15 | ```bash [yarn]
16 | yarn add @leafer-in/animate
17 | ```
18 |
19 | ```bash [npm]
20 | npm i @leafer-in/animate
21 | ```
22 |
23 | ```bash [bun]
24 | bun add @leafer-in/animate
25 | ```
26 |
27 | :::
28 |
29 |
30 |
31 | ::: code-group
32 |
33 | ```vue [App.vue]
34 |
55 |
56 |
57 |
58 |
59 |
64 |
65 |
66 |
67 | ```
68 | :::
69 |
--------------------------------------------------------------------------------
/examples/vue2/src/App.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
25 |
36 |
50 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/packages/vue3/components/application/index.ts:
--------------------------------------------------------------------------------
1 | import type { ElementWithProps } from '@/renderer/renderer'
2 | import { useEffectUpdate, useGetPropsByAttrs } from '@/composables'
3 | import { App } from 'leafer-ui'
4 | import { defineComponent, h, markRaw, onMounted, onUnmounted, renderSlot, shallowRef } from 'vue'
5 | import { createApp } from './../../renderer'
6 |
7 | export const LeaferApp = defineComponent({
8 | inheritAttrs: false,
9 | setup(_props, { slots, expose, attrs }) {
10 | const canvas = shallowRef()
11 | const container = shallowRef()
12 | const config = useGetPropsByAttrs(attrs)
13 |
14 | function mount() {
15 | const context = new App({
16 | ...config,
17 | view: canvas.value,
18 | width: config.width || 800,
19 | height: config.height || 600,
20 | })
21 |
22 | container.value = markRaw(context)
23 | const app = createApp({
24 | render: () => renderSlot(slots, 'default'),
25 | })
26 | app.mount(container.value as unknown as ElementWithProps)
27 | }
28 |
29 | function unMount() {
30 | container.value!.destroy()
31 | }
32 |
33 | onMounted(() => {
34 | mount()
35 | useEffectUpdate(attrs, container.value as unknown as ElementWithProps)
36 | })
37 |
38 | onUnmounted(unMount)
39 | expose({ app: container })
40 |
41 | return () => h('canvas', { ref: canvas })
42 | },
43 | })
44 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/blur.css:
--------------------------------------------------------------------------------
1 | :root {
2 | @media (min-width: 960px) {
3 | /* 首页下滑后导航两侧 */
4 | .VPNavBar:not(.has-sidebar):not(.home.top) {
5 | background-color: rgba(255, 255, 255, 0);
6 | backdrop-filter: blur(10px);
7 | }
8 | }
9 |
10 | @media (min-width: 960px) {
11 | /* 文档页导航中间 */
12 | .VPNavBar:not(.home.top) .content-body {
13 | background-color: rgba(255, 255, 255, 0);
14 | backdrop-filter: blur(10px);
15 | }
16 |
17 | /* 首页下滑后导航中间 */
18 | .VPNavBar:not(.has-sidebar):not(.home.top) .content-body {
19 | background-color: rgba(255, 255, 255, 0);
20 | backdrop-filter: blur(10px);
21 | }
22 | }
23 |
24 | @media (min-width: 960px) {
25 | /* 文档页分割线 */
26 | .VPNavBar:not(.home.top) .divider-line {
27 | background-color: rgba(255, 255, 255, 0);
28 | backdrop-filter: blur(10px);
29 | }
30 |
31 | /* 首页分割线 */
32 | .VPNavBar:not(.has-sidebar):not(.home.top) .divider {
33 | background-color: rgba(255, 255, 255, 0);
34 | backdrop-filter: blur(10px);
35 | }
36 | }
37 |
38 | /* 搜索框 VPNavBarSearchButton.vue */
39 | .DocSearch-Button {
40 | background-color: rgba(255, 255, 255, 0);
41 | backdrop-filter: blur(10px);
42 | }
43 |
44 | /* 移动端大纲栏 */
45 | .VPLocalNav {
46 | background-color: rgba(255, 255, 255, 0);
47 | backdrop-filter: blur(10px);
48 | border-bottom: 5px solid var(--vp-c-gutter);
49 | border-bottom: 0px;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/vue2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leafer-vue",
3 | "type": "module",
4 | "version": "2.0.3",
5 | "description": "",
6 | "author": "FliPPeDround ",
7 | "license": "MIT",
8 | "homepage": "https://github.com/FliPPeDround/leafer-vue#readme",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/FliPPeDround/leafer-vue.git"
12 | },
13 | "bugs": "https://github.com/FliPPeDround/leafer-vue/issues",
14 | "keywords": [],
15 | "sideEffects": false,
16 | "exports": {
17 | ".": {
18 | "types": "./dist/index.d.ts",
19 | "import": "./dist/index.js",
20 | "require": "./dist/index.cjs"
21 | },
22 | "./*": "./*"
23 | },
24 | "main": "./dist/index.js",
25 | "module": "./dist/index.js",
26 | "unpkg": "dist/index.global.js",
27 | "types": "./dist/index.d.ts",
28 | "files": [
29 | "dist"
30 | ],
31 | "scripts": {
32 | "build": "tsup",
33 | "dev": "tsup --watch",
34 | "lint": "eslint .",
35 | "lint:fix": "eslint --fix .",
36 | "prepublishOnly": "nr build",
37 | "release": "bumpp",
38 | "test": "vitest",
39 | "typecheck": "tsc --noEmit"
40 | },
41 | "peerDependencies": {
42 | "@vue/composition-api": "^1.7.1",
43 | "leafer-ui": "1.0.0-rc.6"
44 | },
45 | "peerDependenciesMeta": {
46 | "@vue/composition-api": {
47 | "optional": true
48 | }
49 | },
50 | "dependencies": {
51 | "vue-demi": "^0.14.10"
52 | },
53 | "devDependencies": {
54 | "vue": "^2.7.16"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/docs/guide/events/action/Pointer/pointer.md:
--------------------------------------------------------------------------------
1 | # PointerEvent
2 | 鼠标、手写笔、触摸屏点击事件。
3 | > 详情请查看[PointerEvent |🌿 Leafer UI](https://www.leaferjs.com/ui/reference/event/ui/Pointer.html)
4 |
5 | ## 示例
6 |
9 |
10 |
11 |
12 | ## 事件名称
13 |
14 | [IPointerEvent-url]: https://www.leaferjs.com/ui/api/interfaces/IPointerEvent.html
15 |
16 | ### 点击事件
17 | | 事件名 | 说明 | 参数 |
18 | | --- | --- | --- |
19 | | `tap` | 点击事件 | [`IPointerEvent`][IPointerEvent-url] |
20 | | `double_tap` | 双击事件 | [`IPointerEvent`][IPointerEvent-url] |
21 | | `long_tap` | 长按事件 | [`IPointerEvent`][IPointerEvent-url] |
22 | | `click` | 点击事件(建议使用`tap`) | [`IPointerEvent`][IPointerEvent-url] |
23 | | `double_click` | 双击事件(建议使用`double_tap`) | [`IPointerEvent`][IPointerEvent-url] |
24 |
25 | ### 鼠标事件
26 | | 事件名 | 说明 | 参数 |
27 | | --- | --- | --- |
28 | | `pointerDown` | 按下事件 | [`IPointerEvent`][IPointerEvent-url] |
29 | | `pointerMove` | 光标移动事件 | [`IPointerEvent`][IPointerEvent-url] |
30 | | `pointerUp` | 抬起事件 | [`IPointerEvent`][IPointerEvent-url] |
31 | | `pointerOver` | 鼠标移入事件 | [`IPointerEvent`][IPointerEvent-url] |
32 | | `pointerOut` | 鼠标移出事件 | [`IPointerEvent`][IPointerEvent-url] |
33 | | `pointerEnter` | 鼠标移入事件 | [`IPointerEvent`][IPointerEvent-url] |
34 | | `pointerLeave` | 鼠标移出事件 | [`IPointerEvent`][IPointerEvent-url] |
35 |
36 | ### 右键菜单
37 | | 事件名 | 说明 | 参数 |
38 | | --- | --- | --- |
39 | | `pointerMenu` | 右键菜单事件,同 HTML 的 contextmenu 事件,按下时触发 | [`IPointerEvent`][IPointerEvent-url] |
40 | | `pointerMenu_tap` | 右键 tap 事件, 抬起后触发 | [`IPointerEvent`][IPointerEvent-url] |
41 |
--------------------------------------------------------------------------------
/packages/vue2/components/Root/leafer.ts:
--------------------------------------------------------------------------------
1 | import {
2 | useCreateEvents,
3 | useEffectUpdate,
4 | useGetContainer,
5 | useGetParentNodeName,
6 | useGetPropsAndEventByAttrs,
7 | useInsertBefore,
8 | } from '@/composables'
9 | import { Leafer } from 'leafer-ui'
10 | import { defineComponent, getCurrentInstance, onMounted, onUnmounted } from 'vue-demi'
11 |
12 | export const lfLeafer = defineComponent({
13 | name: 'LfLeafer',
14 | inheritAttrs: false,
15 | setup(_, { slots, expose, attrs }) {
16 | const parentNodeName = useGetParentNodeName()
17 | const allAttrs = getCurrentInstance()?.proxy?.$attrs || {}
18 | const { events, config } = useGetPropsAndEventByAttrs(allAttrs)
19 | let container: Leafer
20 | if (parentNodeName !== 'LfApp') {
21 | const canvas = document.createElement('canvas')
22 | document.body.appendChild(canvas)
23 | container = new Leafer({
24 | ...config,
25 | view: canvas,
26 | start: false,
27 | })
28 |
29 | onMounted(() => {
30 | useInsertBefore(canvas)
31 | container.start()
32 | })
33 |
34 | onUnmounted(() => {
35 | container.destroy()
36 | })
37 | }
38 | else {
39 | container = new Leafer(config)
40 | const appContainer = useGetContainer()
41 | appContainer.add(container)
42 |
43 | onUnmounted(() => {
44 | appContainer.remove(container)
45 | })
46 | }
47 | useEffectUpdate(attrs, container)
48 | useCreateEvents(events, container)
49 |
50 | expose({ container })
51 |
52 | return () => slots.default?.()
53 | },
54 | })
55 |
--------------------------------------------------------------------------------
/packages/vue3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leafer-vue",
3 | "type": "module",
4 | "version": "3.1.1",
5 | "description": "",
6 | "author": "FliPPeDround ",
7 | "license": "MIT",
8 | "homepage": "https://github.com/FliPPeDround/leafer-vue#readme",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/FliPPeDround/leafer-vue.git"
12 | },
13 | "bugs": "https://github.com/FliPPeDround/leafer-vue/issues",
14 | "keywords": [],
15 | "sideEffects": false,
16 | "exports": {
17 | ".": {
18 | "types": "./dist/types/index.d.ts",
19 | "import": "./dist/index.js",
20 | "require": "./dist/index.cjs"
21 | },
22 | "./compiler": {
23 | "types": "./dist/compiler.d.ts",
24 | "import": "./dist/compiler.js",
25 | "require": "./dist/compiler.cjs"
26 | },
27 | "./*": "./*"
28 | },
29 | "main": "dist/index.cjs",
30 | "module": "dist/index.js",
31 | "unpkg": "dist/index.js",
32 | "types": "dist/types/index.d.ts",
33 | "typesVersions": {
34 | "*": {
35 | "*": [
36 | "./dist/*",
37 | "./*"
38 | ]
39 | }
40 | },
41 | "files": [
42 | "dist"
43 | ],
44 | "scripts": {
45 | "build": "tsup",
46 | "dev": "tsup --watch",
47 | "lint": "eslint .",
48 | "lint:fix": "eslint --fix .",
49 | "prepublishOnly": "nr build",
50 | "release": "bumpp",
51 | "test": "vitest",
52 | "typecheck": "tsc --noEmit"
53 | },
54 | "peerDependencies": {
55 | "leafer-ui": ">= 1.0.2"
56 | },
57 | "dependencies": {
58 | "@vue/runtime-core": "^3.5.4",
59 | "vue": "^3.5.4"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/examples/vue2/README.md:
--------------------------------------------------------------------------------
1 | # .
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) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
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 [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
12 |
13 | If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
14 |
15 | 1. Disable the built-in TypeScript Extension
16 | 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
17 | 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
18 | 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
19 |
20 | ## Customize configuration
21 |
22 | See [Vite Configuration Reference](https://vitejs.dev/config/).
23 |
24 | ## Project Setup
25 |
26 | ```sh
27 | pnpm install
28 | ```
29 |
30 | ### Compile and Hot-Reload for Development
31 |
32 | ```sh
33 | pnpm dev
34 | ```
35 |
36 | ### Type-Check, Compile and Minify for Production
37 |
38 | ```sh
39 | pnpm build
40 | ```
41 |
--------------------------------------------------------------------------------
/examples/vue3/README.md:
--------------------------------------------------------------------------------
1 | # vue-test
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) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
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 [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
12 |
13 | If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
14 |
15 | 1. Disable the built-in TypeScript Extension
16 | 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
17 | 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
18 | 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
19 |
20 | ## Customize configuration
21 |
22 | See [Vite Configuration Reference](https://vitejs.dev/config/).
23 |
24 | ## Project Setup
25 |
26 | ```sh
27 | pnpm install
28 | ```
29 |
30 | ### Compile and Hot-Reload for Development
31 |
32 | ```sh
33 | pnpm dev
34 | ```
35 |
36 | ### Type-Check, Compile and Minify for Production
37 |
38 | ```sh
39 | pnpm build
40 | ```
41 |
--------------------------------------------------------------------------------
/playground/vite.config.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs'
2 | import path from 'node:path'
3 | import vue from '@vitejs/plugin-vue'
4 | import Unocss from 'unocss/vite'
5 | import AutoImport from 'unplugin-auto-import/vite'
6 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
7 | import Components from 'unplugin-vue-components/vite'
8 | import { defineConfig } from 'vite'
9 | import Inspect from 'vite-plugin-inspect'
10 | import pkg from './package.json'
11 |
12 | const pathSrc = path.resolve(__dirname, 'src')
13 |
14 | export default defineConfig({
15 | base: '/play/',
16 | resolve: {
17 | alias: {
18 | '@': pathSrc,
19 | },
20 | },
21 | define: {
22 | 'import.meta.env.APP_VERSION': JSON.stringify(pkg.version),
23 | 'import.meta.env.REPL_VERSION': JSON.stringify('4.4.2'),
24 | },
25 | server: {
26 | host: true,
27 | },
28 | plugins: [
29 | vue({
30 | script: {
31 | defineModel: true,
32 | propsDestructure: true,
33 | fs: {
34 | fileExists: fs.existsSync,
35 | readFile: file => fs.readFileSync(file, 'utf-8'),
36 | },
37 | },
38 | }),
39 | AutoImport({
40 | dirs: [path.resolve(pathSrc, 'composables')],
41 | imports: ['vue', '@vueuse/core'],
42 | resolvers: [ElementPlusResolver()],
43 | dts: path.resolve(pathSrc, 'auto-imports.d.ts'),
44 | }),
45 | Components({
46 | dirs: [path.resolve(pathSrc, 'components')],
47 | resolvers: [ElementPlusResolver()],
48 | dts: path.resolve(pathSrc, 'components.d.ts'),
49 | }),
50 | Unocss(),
51 | Inspect(),
52 | ],
53 | optimizeDeps: {
54 | exclude: ['@vue/repl'],
55 | },
56 | build: {
57 | outDir: '../docs/dist/play',
58 | emptyOutDir: true,
59 | rollupOptions: {
60 | external: ['typescript'],
61 | input: [
62 | './index.html',
63 | ],
64 | },
65 | },
66 | })
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Leafer Vue
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 | ## Install
22 |
23 | ```bash
24 | npm install leafer-ui leafer-vue
25 | ```
26 | ## Try it Online
27 | You can try leafer-vue in your browser, in the
28 | [](https://leafer-vue.netlify.app/play/)
29 |
30 | ## License
31 |
32 | [MIT](https://github.com/FliPPeDround/leafer-vue/blob/master/LICENSE)
33 |
34 | ## Credits
35 |
36 | - [leaferjs/ui](https://github.com/leaferjs/ui)
37 | - [hairyf/vue3-pixi](https://github.com/hairyf/vue3-pixi)
38 | - [vuejs/repl](https://github.com/vuejs/repl)
39 | - [element-plus/element-plus-playground](https://github.com/element-plus/element-plus-playground)
40 | - Icon Design by [@马清路德汀](https://weibo.com/u/5225481233)
41 |
42 | ## 🙇🏻♂️[Sponsors](https://afdian.com/a/flippedround)
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/guide/events/events.md:
--------------------------------------------------------------------------------
1 | # 事件处理
2 |
3 | ## 监听事件
4 | 我们可以使用 `v-on` 指令 (简写为 `@`) 来监听 Leafer 事件,并在事件触发时执行对应的 JavaScript。用法:`v-on:tap="handler"` 或 `@tap="handler"`。
5 | ```vue
6 | console.log('Tap')" />
7 | ```
8 | > 详情请查看 [Vue事件处理](https://cn.vuejs.org/guide/essentials/event-handling.html)
9 |
10 | ## 事件修饰符
11 |
12 | - `.once`:只触发一次
13 |
14 | ```vue
15 |
16 | console.log('Tap')" />
17 | ```
18 |
19 | > 详情请查看 [once |🌿 Leafer UI ](https://www.leaferjs.com/ui/reference/property/on.html#%E5%8F%AA%E7%9B%91%E5%90%AC%E4%B8%80%E6%AC%A1%E4%BA%8B%E4%BB%B6)
20 |
21 | ## 移除事件
22 |
23 | 移除事件监听需要获取组件实例使用`off`方法移除事件监听。
24 |
25 | ::: code-group
26 |
27 | ```vue [App.vue]
28 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | ```
46 | :::
47 |
48 | > 详情请查看 [off |🌿 Leafer UI ](https://www.leaferjs.com/ui/reference/property/off.html)
49 |
50 | ## 派发事件
51 |
52 | 我们可以使用`emit`手动派发事件。
53 |
54 | ::: code-group
55 |
56 | ```vue [App.vue]
57 |
67 |
68 |
69 |
70 | console.log('emit Tap')" />
71 |
72 |
73 | ```
74 | > 详情请查看 [emit |🌿 Leafer UI ](https://www.leaferjs.com/ui/reference/property/emit.html)
75 |
76 | 更复杂的[模拟交互](https://www.leaferjs.com/ui/reference/event/simulation.html)
77 |
--------------------------------------------------------------------------------
/docs/guide/components/path/Path.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 |
26 |
32 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/repl/index.vue:
--------------------------------------------------------------------------------
1 |
48 |
49 |
50 |
51 |
64 |
65 |
66 |
67 |
81 |
--------------------------------------------------------------------------------
/docs/guide/start/usage.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # 快速安装
6 |
7 | ::: code-group
8 |
9 | ```bash [pnpm]
10 | pnpm i leafer-ui leafer-vue
11 | ```
12 |
13 | ```bash [yarn]
14 | yarn add leafer-ui leafer-vue
15 | ```
16 |
17 | ```bash [npm]
18 | npm i leafer-ui leafer-vue
19 | ```
20 |
21 | ```bash [bun]
22 | bun add leafer-ui leafer-vue
23 | ```
24 |
25 | :::
26 |
27 | ## 初始化vue插件
28 |
29 | 添加Vue插件配置,支持自定义元素。
30 | ::: code-group
31 |
32 | ```ts [vite.config.ts]
33 | import vue from '@vitejs/plugin-vue'
34 | import { isCustomElement } from 'leafer-vue/compiler'
35 | import { defineConfig } from 'vite'
36 |
37 | export default defineConfig({
38 | plugins: [
39 | vue({
40 | template: {
41 | compilerOptions: {
42 | isCustomElement,
43 | },
44 | },
45 | }),
46 | ],
47 | })
48 | ```
49 |
50 | ```ts [vue.config.ts]
51 | const { isCustomElement } = require('leafer-vue/compiler')
52 |
53 | module.exports = {
54 | chainWebpack: (config) => {
55 | config.module
56 | .rule('vue')
57 | .use('vue-loader')
58 | .tap(options => ({
59 | ...options,
60 | compilerOptions: {
61 | isCustomElement,
62 | }
63 | }))
64 | }
65 | }
66 | ```
67 | :::
68 |
69 |
70 | 为什么需要配置插件?
71 |
72 | > 默认情况下,Vue 会将任何非原生的 HTML 标签优先当作 Vue 组件处理,而将“渲染一个自定义元素”作为后备选项。这会在开发时导致 Vue 抛出一个“解析组件失败”的警告。要让 Vue 知晓特定元素应该被视为自定义元素并跳过组件解析,我们可以指定 [isCustomElement](https://cn.vuejs.org/api/application#app-config-compileroptions) 选项。
73 | >
74 | > 详情请参考 [跳过组件解析 | Vue官方文档](https://cn.vuejs.org/guide/extras/web-components.html#skipping-component-resolution)
75 |
76 |
77 | ## 基础使用
78 | ::: code-group
79 |
80 | ```vue [App.vue]
81 |
84 |
85 |
86 |
87 |
88 |
93 |
94 |
95 |
96 | ```
97 | :::
98 |
99 | ## 在线尝试
100 |
101 |
102 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@leafer-vue/monorepo",
3 | "type": "module",
4 | "private": true,
5 | "packageManager": "pnpm@8.10.2",
6 | "description": "",
7 | "author": "FliPPeDround ",
8 | "license": "MIT",
9 | "homepage": "https://github.com/FliPPeDround/leafer-vue#readme",
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/FliPPeDround/leafer-vue.git"
13 | },
14 | "bugs": "https://github.com/FliPPeDround/leafer-vue/issues",
15 | "keywords": [],
16 | "sideEffects": false,
17 | "scripts": {
18 | "build": "nr build:3",
19 | "dev": "nr dev:3",
20 | "build:3": "nr -C packages/vue3 build",
21 | "dev:3": "nr -C packages/vue3 dev",
22 | "build:2": "nr -C packages/vue2 build",
23 | "dev:2": "nr -C packages/vue2 dev",
24 | "play": "nr -C playground dev",
25 | "play:build": "nr -C playground build",
26 | "example": "nr example:3",
27 | "example:3": "nr -C examples/vue3 dev",
28 | "example:2": "nr -C examples/vue2 dev",
29 | "docs:dev": "nr -C docs docs:dev",
30 | "docs:build": "nr -C docs docs:build",
31 | "docs:preview": "nr -C docs docs:preview",
32 | "deploy": "npm run build && npm run docs:build && npm run play:build",
33 | "lint": "eslint .",
34 | "lint:fix": "eslint --fix .",
35 | "release": "nr release:3",
36 | "release:2": "nr -C packages/vue2 release",
37 | "release:3": "nr -C packages/vue3 release",
38 | "test": "vitest"
39 | },
40 | "devDependencies": {
41 | "@antfu/eslint-config": "^3.6.2",
42 | "@antfu/ni": "^0.21.12",
43 | "@antfu/utils": "^0.7.10",
44 | "@types/fs-extra": "^11.0.4",
45 | "@types/lodash-es": "^4.17.12",
46 | "@types/node": "^18.19.50",
47 | "bumpp": "^9.5.2",
48 | "eslint": "^9.10.0",
49 | "fs-extra": "^11.2.0",
50 | "kolorist": "^1.8.0",
51 | "lint-staged": "^13.3.0",
52 | "pathe": "^1.1.2",
53 | "pnpm": "^8.15.9",
54 | "rimraf": "^5.0.10",
55 | "simple-git-hooks": "^2.11.1",
56 | "tsup": "^8.2.4",
57 | "typescript": "^5.6.2",
58 | "unocss": "^0.62.3",
59 | "unplugin-vue-components": "^0.25.2",
60 | "vitest": "^0.31.4"
61 | },
62 | "simple-git-hooks": {
63 | "pre-commit": "pnpm lint-staged"
64 | },
65 | "lint-staged": {
66 | "*": "eslint --fix"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/examples/vue2/src/assets/base.css:
--------------------------------------------------------------------------------
1 | /* color palette from */
2 | :root {
3 | --vt-c-white: #ffffff;
4 | --vt-c-white-soft: #f8f8f8;
5 | --vt-c-white-mute: #f2f2f2;
6 |
7 | --vt-c-black: #181818;
8 | --vt-c-black-soft: #222222;
9 | --vt-c-black-mute: #282828;
10 |
11 | --vt-c-indigo: #2c3e50;
12 |
13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15 | --vt-c-divider-dadarkrk-1: rgba(84, 84, 84, 0.65);
16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17 |
18 | --vt-c-text-light-1: var(--vt-c-indigo);
19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20 | --vt-c-text-dark-1: var(--vt-c-white);
21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22 | }
23 |
24 | /* semantic color variables for this project */
25 | :root {
26 | --color-background: var(--vt-c-white);
27 | --color-background-soft: var(--vt-c-white-soft);
28 | --color-background-mute: var(--vt-c-white-mute);
29 |
30 | --color-border: var(--vt-c-divider-light-2);
31 | --color-border-hover: var(--vt-c-divider-light-1);
32 |
33 | --color-heading: var(--vt-c-text-light-1);
34 | --color-text: var(--vt-c-text-light-1);
35 |
36 | --section-gap: 160px;
37 | }
38 |
39 | @media (prefers-color-scheme: dark) {
40 | :root {
41 | --color-background: var(--vt-c-black);
42 | --color-background-soft: var(--vt-c-black-soft);
43 | --color-background-mute: var(--vt-c-black-mute);
44 |
45 | --color-border: var(--vt-c-divider-dark-2);
46 | --color-border-hover: var(--vt-c-divider-dark-1);
47 |
48 | --color-heading: var(--vt-c-text-dark-1);
49 | --color-text: var(--vt-c-text-dark-2);
50 | }
51 | }
52 |
53 | *,
54 | *::before,
55 | *::after {
56 | box-sizing: border-box;
57 | margin: 0;
58 | position: relative;
59 | font-weight: normal;
60 | }
61 |
62 | body {
63 | min-height: 100vh;
64 | color: var(--color-text);
65 | background: var(--color-background);
66 | transition: color 0.5s, background-color 0.5s;
67 | line-height: 1.6;
68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
70 | font-size: 15px;
71 | text-rendering: optimizeLegibility;
72 | -webkit-font-smoothing: antialiased;
73 | -moz-osx-font-smoothing: grayscale;
74 | }
75 |
--------------------------------------------------------------------------------
/packages/vue3/renderer/renderer.ts:
--------------------------------------------------------------------------------
1 | import type { IUI } from '@leafer-ui/interface'
2 | import { useLogger } from '@/composables/useLogger'
3 | import { getEventNameByAttrName, isOn } from '@/utils'
4 | import { UI } from 'leafer-ui'
5 | import { camelize, createRenderer, markRaw } from 'vue'
6 | import { Empty } from '../tags/Empty'
7 |
8 | const { log } = useLogger()
9 |
10 | export interface ElementWithProps extends IUI {
11 | [key: string]: any
12 | }
13 |
14 | export const renderer = createRenderer({
15 | createElement(tag, _?, Constructor?) {
16 | let element: IUI
17 | if (tag === 'Custom') {
18 | if (!Constructor) {
19 | throw new Error('Custom tag must have is attribute')
20 | }
21 | else {
22 | element = new (Constructor as unknown as new () => IUI)()
23 | }
24 | }
25 | else {
26 | element = UI.one({ tag })
27 | }
28 | markRaw(element)
29 | return element
30 | },
31 | patchProp(el, key, _prevValue, nextValue) {
32 | key = camelize(key)
33 | if (isOn(key)) {
34 | const eventName = getEventNameByAttrName(key)
35 | if (key.endsWith('Once'))
36 | el.once(eventName, nextValue)
37 |
38 | else
39 | el.on(eventName, nextValue)
40 | }
41 | else {
42 | el[key] = nextValue === '' ? true : nextValue
43 | }
44 | },
45 | insert(el, parent) {
46 | if (parent?.tag === 'App' && el?.tag !== 'Leafer') {
47 | return
48 | }
49 | if (el && parent)
50 | parent.add(el)
51 | },
52 | remove(el) {
53 | el?.destroy()
54 | },
55 | createText(text) {
56 | const trimmedText = text.trim()
57 | if (trimmedText) {
58 | log([
59 | {
60 | content: 'Direct text writing is not supported, please use ',
61 | },
62 | {
63 | color: '#6eacf8',
64 | backgroundColor: '#222222',
65 | content: ``,
66 | },
67 | {
68 | content: ' instead',
69 | },
70 | ])
71 | }
72 | return new Empty() as unknown as IUI
73 | },
74 | createComment() {
75 | return new Empty() as unknown as IUI
76 | },
77 | setText() {},
78 | setElementText() {},
79 | parentNode(node) {
80 | return node?.parent as IUI
81 | },
82 | nextSibling(node) {
83 | if (!node || !node.parent)
84 | return null
85 |
86 | const children = node.parent.children
87 | const index = children.findIndex(_node => _node.innerId === node.innerId)
88 | return index + 1 < children.length ? children[index + 1] : null
89 | },
90 | })
91 |
--------------------------------------------------------------------------------
/playground/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
70 |
71 |
72 |
73 |
74 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
118 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/Sponsors.vue:
--------------------------------------------------------------------------------
1 |
69 |
70 |
71 |
75 |
76 |
84 |
85 |
86 |
87 |
126 |
--------------------------------------------------------------------------------
/playground/src/utils/dependency.ts:
--------------------------------------------------------------------------------
1 | import type { Versions } from '@/composables/store'
2 | import type { ImportMap } from '@vue/repl'
3 | import type { MaybeRef } from '@vueuse/core'
4 | import type { Ref } from 'vue'
5 | import { IS_DEV } from '@/constants'
6 | import { gte } from 'semver'
7 |
8 | export interface Dependency {
9 | pkg?: string
10 | version?: string
11 | devPath?: string
12 | path: string
13 | }
14 |
15 | export type Cdn = 'unpkg' | 'jsdelivr' | 'jsdelivr-fastly'
16 | export const cdn = useLocalStorage('setting-cdn', 'unpkg')
17 |
18 | export function genCdnLink(pkg: string, version: string | undefined, path: string) {
19 | version = version ? `@${version}` : ''
20 | switch (cdn.value) {
21 | case 'unpkg':
22 | return `https://unpkg.com/${pkg}${version}${path}`
23 | case 'jsdelivr':
24 | return `https://cdn.jsdelivr.net/npm/${pkg}${version}${path}`
25 | case 'jsdelivr-fastly':
26 | return `https://fastly.jsdelivr.net/npm/${pkg}${version}${path}`
27 | }
28 | }
29 |
30 | export function genCompilerSfcLink(version: string) {
31 | return genCdnLink(
32 | '@vue/compiler-sfc',
33 | version,
34 | '/dist/compiler-sfc.esm-browser.js',
35 | )
36 | }
37 |
38 | export function genImportMap(version: Partial = {}): ImportMap {
39 | const deps: Record = {
40 | 'vue': {
41 | version: version.vue,
42 | path: '/dist/vue.esm-browser.prod.js',
43 | },
44 | 'leafer-ui': {
45 | version: version.leaferUI,
46 | path: '/dist/web.module.min.js',
47 | },
48 | 'leafer-vue': {
49 | version: version.leaferVue,
50 | path: '/dist/index.js',
51 | devPath: `${location.origin}/leafer-vue.proxy.js`,
52 | },
53 | }
54 |
55 | return {
56 | imports: Object.fromEntries(
57 | Object.entries(deps).map(([key, dep]) => [
58 | key,
59 | IS_DEV && dep.devPath
60 | ? dep.devPath
61 | : genCdnLink(dep.pkg ?? key, dep.version, dep.path),
62 | ]),
63 | ),
64 | }
65 | }
66 |
67 | export function getVersions(pkg: MaybeRef) {
68 | const url = computed(
69 | () => `https://data.jsdelivr.com/v1/package/npm/${unref(pkg)}`,
70 | )
71 | return useFetch(url, {
72 | initialData: [],
73 | afterFetch: (ctx) => {
74 | ctx.data = ctx.data.versions
75 | return ctx
76 | },
77 | refetch: true,
78 | }).json().data as Ref
79 | }
80 |
81 | export function getSupportedVueVersions() {
82 | const versions = getVersions('vue')
83 | return computed(() =>
84 | versions.value.filter(version => gte(version, '3.2.0')),
85 | )
86 | }
87 |
88 | export function getSupportedTSVersions() {
89 | const versions = getVersions('typescript')
90 | return computed(() =>
91 | versions.value.filter(
92 | version => !version.includes('dev') && !version.includes('insiders'),
93 | ),
94 | )
95 | }
96 |
97 | export function getSupportedLfUIVersions() {
98 | const versions = getVersions('leafer-ui')
99 | return computed(() => {
100 | return versions.value.filter(version => gte(version, '1.0.0'))
101 | })
102 | }
103 |
104 | export function getSupportedLfVUEVersions() {
105 | const versions = getVersions('leafer-vue')
106 | return computed(() => {
107 | return versions.value.filter(version => gte(version, '3.1.0'))
108 | })
109 | }
110 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/MoreStarts.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
41 |
42 |
43 |
44 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/packages/vue3/types/LeaferEventType.ts:
--------------------------------------------------------------------------------
1 | import type {
2 |
3 | IDragEvent,
4 | IDropEvent,
5 | IImageEvent,
6 | IMoveEvent,
7 | IPointerEvent,
8 | IRotateEvent,
9 | ISwipeEvent,
10 | IZoomEvent,
11 | } from '@leafer-ui/interface'
12 | import type { ChildEvent, LayoutEvent, LeaferEvent, PropertyEvent, RenderEvent, ResizeEvent, WatchEvent } from 'leafer-ui'
13 |
14 | export interface LeaferBaseEvent {
15 | /**
16 | * @description IPointerEvent
17 | * @param e
18 | */
19 | onPointerDown: (e: IPointerEvent) => void
20 | onPointerMove: (e: IPointerEvent) => void
21 | onPointerUp: (e: IPointerEvent) => void
22 | onPointerOver: (e: IPointerEvent) => void
23 | onPointerOut: (e: IPointerEvent) => void
24 | onPointerEnter: (e: IPointerEvent) => void
25 | onPointerLeave: (e: IPointerEvent) => void
26 | onTap: (e: IPointerEvent) => void
27 | onDouble_tap: (e: IPointerEvent) => void
28 | onLong_press: (e: IPointerEvent) => void
29 | onLong_tap: (e: IPointerEvent) => void
30 | /**
31 | *
32 | * @deprecated use Tap instead of click
33 | */
34 | onClick: (e: IPointerEvent) => void
35 | /**
36 | *
37 | * @deprecated use onDouble_tap instead
38 | */
39 | onDouble_click: (e: IPointerEvent) => void
40 |
41 | /**
42 | * @description IDragEvent
43 | * @param e
44 | */
45 | onDrag: (e: IDragEvent) => void
46 | onDragStart: (e: IDragEvent) => void
47 | onDragEnd: (e: IDragEvent) => void
48 | onDragOver: (e: IDragEvent) => void
49 | onDragOut: (e: IDragEvent) => void
50 | onDragEnter: (e: IDragEvent) => void
51 | onDragLeave: (e: IDragEvent) => void
52 |
53 | /**
54 | * @description DropEvent
55 | * @param e
56 | */
57 | onDrop: (e: IDropEvent) => void
58 |
59 | /**
60 | * @description ISwipeEvent
61 | * @param e
62 | */
63 | onSwipeLeft: (e: ISwipeEvent) => void
64 | onSwipeRight: (e: ISwipeEvent) => void
65 | onSwipeUp: (e: ISwipeEvent) => void
66 | onSwipeDown: (e: ISwipeEvent) => void
67 |
68 | /**
69 | * @description IMoveEvent
70 | * @param e
71 | */
72 | onMoveStart: (e: IMoveEvent) => void
73 | onMove: (e: IMoveEvent) => void
74 | onMoveEnd: (e: IMoveEvent) => void
75 |
76 | /**
77 | * @description IZoomEvent
78 | * @param e
79 | */
80 | onZoomStart: (e: IZoomEvent) => void
81 | onZoom: (e: IZoomEvent) => void
82 | onZoomEnd: (e: IZoomEvent) => void
83 |
84 | /**
85 | * @description IRotateEvent
86 | * @param e
87 | */
88 | onRotateStart: (e: IRotateEvent) => void
89 | onRotate: (e: IRotateEvent) => void
90 | onRotateEnd: (e: IRotateEvent) => void
91 |
92 | /**
93 | * @description IImageEvent
94 | * @param e
95 | */
96 | onImageload: (e: IImageEvent) => void
97 | onImageLoaded: (e: IImageEvent) => void
98 | onImageError: (e: IImageEvent) => void
99 | }
100 |
101 | export interface _LeaferEvent {
102 | onLeaferStart: (e: LeaferEvent) => void
103 | onLeaferBefore_ready: (e: LeaferEvent) => void
104 | onLeaferReady: (e: LeaferEvent) => void
105 | onLeaferAfter_ready: (e: LeaferEvent) => void
106 | onLeaferView_ready: (e: LeaferEvent) => void
107 | onLeaferStop: (e: LeaferEvent) => void
108 | onLeaferRestart: (e: LeaferEvent) => void
109 | onLeaferEnd: (e: LeaferEvent) => void
110 |
111 | onResize: (e: ResizeEvent) => void
112 |
113 | onRenderRequest: (e: RenderEvent) => void
114 | onRenderStart: (e: RenderEvent) => void
115 | onRenderBefore: (e: RenderEvent) => void
116 | onRender: (e: RenderEvent) => void
117 | onRenderAgain: (e: RenderEvent) => void
118 | onRenderEnd: (e: RenderEvent) => void
119 |
120 | onLayoutRequest: (e: LayoutEvent) => void
121 | onLayoutStart: (e: LayoutEvent) => void
122 | onLayoutBefore: (e: LayoutEvent) => void
123 | onLayout: (e: LayoutEvent) => void
124 | onLayoutAfter: (e: LayoutEvent) => void
125 | onLayoutAgain: (e: LayoutEvent) => void
126 | onLayoutEnd: (e: LayoutEvent) => void
127 |
128 | onWatchRequest: (e: WatchEvent) => void
129 | onWatchData: (e: WatchEvent) => void
130 |
131 | onChildAdd: (e: ChildEvent) => void
132 | onChildRemove: (e: ChildEvent) => void
133 |
134 | onPropertyChange: (e: PropertyEvent) => void
135 | }
136 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/var.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Customize default theme styling by overriding CSS variables:
3 | * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
4 | */
5 |
6 | /**
7 | * Colors
8 | * -------------------------------------------------------------------------- */
9 |
10 | :root {
11 | --vp-c-brand-1: #40883C;
12 | --vp-c-brand-light: #66A659;
13 | --vp-c-brand-lighter: #8CB97A;
14 | --vp-c-brand-dark: #296E25;
15 | --vp-c-brand-darker: #135317;
16 | --vp-c-brand-next: #357B30;
17 | --vp-c-bg-soft: #488f460f;
18 | --vp-c-gutter: var(--vp-c-divider);
19 | --vp-code-block-bg: rgb(125 125 125 / 0.04);
20 | --vp-code-tab-divider: var(--vp-c-divider);
21 | --vp-code-copy-code-bg: rgb(125 125 125 / 0.1);
22 | --vp-code-copy-code-hover-bg: rgb(125 125 125 / 0.2);
23 | --vp-c-disabled-bg: rgb(125 125 125 / 0.2);
24 | --vp-code-tab-text-color: var(--vp-c-text-2);
25 | --vp-code-tab-active-text-color: var(--vp-c-text-1);
26 | --vp-code-tab-hover-text-color: var(--vp-c-text-1);
27 | --vp-code-copy-code-active-text: var(--vp-c-text-2);
28 | --vp-c-text-dark-3: rgb(56 56 56 / 0.8);
29 | --vp-c-brand-lightest: var(--vp-c-brand-1);
30 |
31 | --vp-c-highlight-bg: var(--vp-c-brand-light);
32 | --vp-c-highlight-text: var(--vp-c-bg);
33 | }
34 |
35 | .dark {
36 | --vp-code-block-bg: rgb(0 0 0 / 0.2);
37 | --vp-c-text-code: #c0cec0;
38 | }
39 |
40 |
41 | /**
42 | * Component: Button
43 | * -------------------------------------------------------------------------- */
44 |
45 | :root {
46 | --vp-button-brand-border: var(--vp-c-brand-light);
47 | --vp-button-brand-text: var(--vp-c-white);
48 | --vp-button-brand-bg: var(--vp-c-brand-1);
49 | --vp-button-brand-hover-border: var(--vp-c-brand-light);
50 | --vp-button-brand-hover-text: var(--vp-c-white);
51 | --vp-button-brand-hover-bg: var(--vp-c-brand-light);
52 | --vp-button-brand-active-border: var(--vp-c-brand-light);
53 | --vp-button-brand-active-text: var(--vp-c-white);
54 | --vp-button-brand-active-bg: var(--vp-button-brand-bg);
55 | }
56 |
57 | /**
58 | * Component: Home
59 | * -------------------------------------------------------------------------- */
60 |
61 | :root {
62 | --vp-home-hero-name-color: transparent;
63 | --vp-home-hero-name-background: -webkit-linear-gradient(
64 | 120deg,
65 | var(--vp-c-brand-1) 30%,
66 | var(--vp-c-brand-next)
67 | );
68 | --vp-home-hero-image-background-image: linear-gradient(
69 | -45deg,
70 | var(--vp-c-brand-1) 50%,
71 | var(--vp-c-brand-next) 50%
72 | );
73 | --vp-home-hero-image-filter: blur(80px);
74 | .image-src {
75 | max-width: 250px;
76 | }
77 | }
78 |
79 | @media (min-width: 640px) {
80 | :root {
81 | --vp-home-hero-image-filter: blur(120px);
82 | }
83 | }
84 |
85 | @media (min-width: 960px) {
86 | :root {
87 | --vp-home-hero-image-filter: blur(120px);
88 | }
89 | }
90 |
91 | /* Safari has a very bad performance on gradient and filter */
92 | .browser-safari, .browser-firefox {
93 | --vp-home-hero-image-background-image: transparent;
94 | --vp-home-hero-image-filter: '';
95 | }
96 |
97 | /**
98 | * Component: Custom Block
99 | * -------------------------------------------------------------------------- */
100 |
101 | :root {
102 | --vp-custom-block-tip-border: var(--vp-c-brand-1);
103 | --vp-custom-block-tip-text: var(--vp-c-brand-darker);
104 | --vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
105 | }
106 |
107 | .dark {
108 | --vp-custom-block-tip-border: var(--vp-c-brand-1);
109 | --vp-custom-block-tip-text: var(--vp-c-brand-lightest);
110 | --vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
111 | }
112 |
113 | /**
114 | * Component: Algolia
115 | * -------------------------------------------------------------------------- */
116 |
117 | .DocSearch {
118 | --docsearch-primary-color: var(--vp-c-brand-1) !important;
119 | }
120 |
121 | .image-container {
122 | position: relative;
123 | margin: 0 auto;
124 | }
125 |
126 | .image-bg {
127 | position: absolute;
128 | top: 50%;
129 | left: 50%;
130 | border-radius: 50%;
131 | width: 192px;
132 | height: 192px;
133 | background-image: var(--vp-home-hero-image-background-image);
134 | filter: var(--vp-home-hero-image-filter);
135 | transform: translate(-50%, -50%);
136 | }
137 |
138 | .VPHomeSponsors {
139 | margin-bottom: 0 !important;
140 | }
141 |
--------------------------------------------------------------------------------
/playground/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
65 |
66 |
67 |
134 |
135 |
136 |
162 |
--------------------------------------------------------------------------------
/packages/vue2/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { DefineComponent } from '@vue/runtime-core'
2 | import type {
3 | ChildEvent,
4 |
5 | DragEvent,
6 | DropEvent,
7 | IBoxInputData,
8 | ICanvasInputData,
9 | IEllipseInputData,
10 | IFrameInputData,
11 | IImageInputData,
12 | ILeaferConfig,
13 | ILineInputData,
14 | ImageEvent,
15 |
16 | IPolygonInputData,
17 | IRectInputData,
18 | IStarInputData,
19 | ITextInputData,
20 | LayoutEvent,
21 | LeaferEvent,
22 | MoveEvent,
23 | PointerEvent,
24 | PropertyEvent,
25 | RenderEvent,
26 | ResizeEvent,
27 | RotateEvent,
28 | SwipeEvent,
29 | WatchEvent,
30 | ZoomEvent,
31 | } from 'leafer-ui'
32 |
33 | type LeaferComponent = DefineComponent
34 | type RequiredField = T & { [P in K]-?: T[P] }
35 |
36 | type LeaferBaseEvent = Partial<{
37 | /**
38 | * @description PointerEvent
39 | * @param e
40 | */
41 | onPointerDown: (e: PointerEvent) => void
42 | onPointerMove: (e: PointerEvent) => void
43 | onPointerUp: (e: PointerEvent) => void
44 | onPointerOver: (e: PointerEvent) => void
45 | onPointerOut: (e: PointerEvent) => void
46 | onPointerEnter: (e: PointerEvent) => void
47 | onPointerLeave: (e: PointerEvent) => void
48 | onTap: (e: PointerEvent) => void
49 | onDouble_tap: (e: PointerEvent) => void
50 | onLong_press: (e: PointerEvent) => void
51 | onLong_tap: (e: PointerEvent) => void
52 | /**
53 | *
54 | * @deprecated use Tap instead of click
55 | */
56 | onClick: (e: PointerEvent) => void
57 | /**
58 | *
59 | * @deprecated use onDouble_tap instead
60 | */
61 | onDouble_click: (e: PointerEvent) => void
62 |
63 | /**
64 | * @description DragEvent
65 | * @param e
66 | */
67 | onDrag: (e: DragEvent) => void
68 | onDragStart: (e: DragEvent) => void
69 | onDragEnd: (e: DragEvent) => void
70 | onDragOver: (e: DragEvent) => void
71 | onDragOut: (e: DragEvent) => void
72 | onDragEnter: (e: DragEvent) => void
73 | onDragLeave: (e: DragEvent) => void
74 |
75 | /**
76 | * @description DropEvent
77 | * @param e
78 | */
79 | onDrop: (e: DropEvent) => void
80 |
81 | /**
82 | * @description SwipeEvent
83 | * @param e
84 | */
85 | onSwipeLeft: (e: SwipeEvent) => void
86 | onSwipeRight: (e: SwipeEvent) => void
87 | onSwipeUp: (e: SwipeEvent) => void
88 | onSwipeDown: (e: SwipeEvent) => void
89 |
90 | /**
91 | * @description MoveEvent
92 | * @param e
93 | */
94 | onMoveStart: (e: MoveEvent) => void
95 | onMove: (e: MoveEvent) => void
96 | onMoveEnd: (e: MoveEvent) => void
97 |
98 | /**
99 | * @description ZoomEvent
100 | * @param e
101 | */
102 | onZoomStart: (e: ZoomEvent) => void
103 | onZoom: (e: ZoomEvent) => void
104 | onZoomEnd: (e: ZoomEvent) => void
105 |
106 | /**
107 | * @description RotateEvent
108 | * @param e
109 | */
110 | onRotateStart: (e: RotateEvent) => void
111 | onRotate: (e: RotateEvent) => void
112 | onRotateEnd: (e: RotateEvent) => void
113 |
114 | /**
115 | * @description ImageEvent
116 | * @param e
117 | */
118 | onImageLoaded: (e: ImageEvent) => void
119 | onImageError: (e: ImageEvent) => void
120 | }>
121 |
122 | type _LeaferEvent = Partial<{
123 | onLeaferStart: (e: LeaferEvent) => void
124 | onLeaferBefore_ready: (e: LeaferEvent) => void
125 | onLeaferReady: (e: LeaferEvent) => void
126 | onLeaferAfter_ready: (e: LeaferEvent) => void
127 | onLeaferView_ready: (e: LeaferEvent) => void
128 | onLeaferStop: (e: LeaferEvent) => void
129 | onLeaferRestart: (e: LeaferEvent) => void
130 | onLeaferEnd: (e: LeaferEvent) => void
131 |
132 | onResize: (e: ResizeEvent) => void
133 |
134 | onRenderRequest: (e: RenderEvent) => void
135 | onRenderStart: (e: RenderEvent) => void
136 | onRenderBefore: (e: RenderEvent) => void
137 | onRender: (e: RenderEvent) => void
138 | onRenderAgain: (e: RenderEvent) => void
139 | onRenderEnd: (e: RenderEvent) => void
140 |
141 | onLayoutRequest: (e: LayoutEvent) => void
142 | onLayoutStart: (e: LayoutEvent) => void
143 | onLayoutBefore: (e: LayoutEvent) => void
144 | onLayout: (e: LayoutEvent) => void
145 | onLayoutAfter: (e: LayoutEvent) => void
146 | onLayoutAgain: (e: LayoutEvent) => void
147 | onLayoutEnd: (e: LayoutEvent) => void
148 |
149 | onWatchRequest: (e: WatchEvent) => void
150 | onWatchData: (e: WatchEvent) => void
151 |
152 | onChildAdd: (e: ChildEvent) => void
153 | onChildRemove: (e: ChildEvent) => void
154 |
155 | onPropertyChange: (e: PropertyEvent) => void
156 | }>
157 |
158 | type LfEvent = _LeaferEvent & LeaferBaseEvent
159 |
160 | declare const lfApp: DefineComponent
161 | declare const lfLeafer: LeaferComponent
162 |
163 | declare const lfFrame: LeaferComponent
164 | declare const lfBox: LeaferComponent
165 |
166 | declare const lfRect: LeaferComponent
167 | declare const lfEllipse: LeaferComponent
168 | declare const lfPolygon: LeaferComponent
169 | declare const lfLine: LeaferComponent
170 | declare const lfStar: LeaferComponent
171 |
172 | declare const lfImage: DefineComponent
173 | declare const lfCanvas: DefineComponent
174 |
175 | type _TextInputData = RequiredField
176 | declare const lfText: LeaferComponent<_TextInputData, LeaferBaseEvent>
177 |
--------------------------------------------------------------------------------
/playground/src/composables/store.ts:
--------------------------------------------------------------------------------
1 | import { IS_DEV } from '@/constants'
2 | import {
3 | genCompilerSfcLink,
4 | genImportMap,
5 | } from '@/utils/dependency'
6 | import { atou, utoa } from '@/utils/encode'
7 | import {
8 | compileFile,
9 | File,
10 | type ImportMap,
11 | mergeImportMap,
12 | type StoreState,
13 | useStore as useReplStore,
14 | } from '@vue/repl'
15 | import { objectOmit } from '@vueuse/core'
16 | import tsconfigCode from '../template/_tsconfig.json?raw'
17 | import mainCode from '../template/main.vue?raw'
18 | import welcomeCode from '../template/welcome.vue?raw'
19 |
20 | export interface Initial {
21 | serializedState?: string
22 | initialized?: () => void
23 | }
24 | export type VersionKey = 'vue' | 'leaferUI' | 'leaferVue' | 'typescript'
25 | export type Versions = Record
26 | export interface UserOptions {
27 | styleSource?: string
28 | showHidden?: boolean
29 | }
30 | export type SerializeState = Record & {
31 | _o?: UserOptions
32 | }
33 |
34 | const MAIN_FILE = 'src/PlaygroundMain.vue'
35 | const APP_FILE = 'src/App.vue'
36 | const LEGACY_IMPORT_MAP = 'src/import_map.json'
37 | export const IMPORT_MAP = 'import-map.json'
38 | export const TSCONFIG = 'tsconfig.json'
39 | export function useStore(initial: Initial) {
40 | const saved: SerializeState | undefined = initial.serializedState
41 | ? deserialize(initial.serializedState)
42 | : undefined
43 | const versions = reactive({
44 | vue: 'latest',
45 | leaferUI: 'latest',
46 | leaferVue: 'latest',
47 | typescript: 'latest',
48 | })
49 | const userOptions: UserOptions = {}
50 |
51 | const builtinImportMap = computed(() => {
52 | const importMap = genImportMap(versions)
53 | return importMap
54 | })
55 |
56 | const storeState: Partial = toRefs(
57 | reactive({
58 | files: initFiles(),
59 | mainFile: MAIN_FILE,
60 | activeFilename: APP_FILE,
61 | vueVersion: computed(() => versions.vue),
62 | typescriptVersion: versions.typescript,
63 | builtinImportMap,
64 | template: {
65 | welcomeSFC: mainCode,
66 | },
67 | sfcOptions: {
68 | script: {
69 | propsDestructure: true,
70 | },
71 | },
72 | }),
73 | )
74 | const store = useReplStore(storeState)
75 | store.files[MAIN_FILE].hidden = !IS_DEV
76 | setVueVersion(versions.vue).then(() => {
77 | initial.initialized?.()
78 | })
79 |
80 | watch(
81 | builtinImportMap,
82 | (newBuiltinImportMap) => {
83 | const importMap = JSON.parse(store.files[IMPORT_MAP].code)
84 | store.files[IMPORT_MAP].code = JSON.stringify(
85 | mergeImportMap(importMap, newBuiltinImportMap),
86 | undefined,
87 | 2,
88 | )
89 | },
90 | { deep: true },
91 | )
92 |
93 | function init() {
94 | watchEffect(() => {
95 | compileFile(store, store.activeFile).then(errs => (store.errors = errs))
96 | })
97 | for (const [filename, file] of Object.entries(store.files)) {
98 | if (filename === store.activeFilename)
99 | continue
100 | compileFile(store, file).then(errs => store.errors.push(...errs))
101 | }
102 |
103 | watch(
104 | () => [
105 | store.files[TSCONFIG]?.code,
106 | store.typescriptVersion,
107 | store.locale,
108 | store.dependencyVersion,
109 | store.vueVersion,
110 | ],
111 | useDebounceFn(() => store.reloadLanguageTools?.(), 300),
112 | { deep: true },
113 | )
114 | }
115 |
116 | function serialize() {
117 | const state: SerializeState = { ...store.getFiles() }
118 | state._o = userOptions
119 | return utoa(JSON.stringify(state))
120 | }
121 | function deserialize(text: string): SerializeState {
122 | const state = JSON.parse(atou(text))
123 | return state
124 | }
125 |
126 | function initFiles() {
127 | const files: Record = Object.create(null)
128 | if (saved) {
129 | for (let [filename, file] of Object.entries(objectOmit(saved, ['_o']))) {
130 | if (
131 | ![IMPORT_MAP, TSCONFIG].includes(filename)
132 | && !filename.startsWith('src/')
133 | ) {
134 | filename = `src/${filename}`
135 | }
136 | if (filename === LEGACY_IMPORT_MAP) {
137 | filename = IMPORT_MAP
138 | }
139 | files[filename] = new File(filename, file as string)
140 | }
141 | }
142 | else {
143 | files[APP_FILE] = new File(APP_FILE, welcomeCode)
144 | }
145 | if (!files[TSCONFIG]) {
146 | files[TSCONFIG] = new File(TSCONFIG, tsconfigCode)
147 | }
148 | return files
149 | }
150 |
151 | async function setVueVersion(version: string) {
152 | store.compiler = await import(
153 | /* @vite-ignore */ genCompilerSfcLink(version)
154 | )
155 | versions.vue = version
156 | }
157 | async function setVersion(key: VersionKey, version: string) {
158 | switch (key) {
159 | case 'vue':
160 | await setVueVersion(version)
161 | break
162 | case 'leaferUI':
163 | versions.leaferUI = version
164 | break
165 | case 'leaferVue':
166 | versions.leaferVue = version
167 | break
168 | case 'typescript':
169 | store.typescriptVersion = version
170 | break
171 | }
172 | }
173 |
174 | const utils = {
175 | versions,
176 | init,
177 | serialize,
178 | setVersion,
179 | }
180 | Object.assign(store, utils)
181 | return store as typeof store & typeof utils
182 | }
183 |
184 | export type Store = ReturnType
185 |
--------------------------------------------------------------------------------
/docs/public/leafer-vue.proxy.js:
--------------------------------------------------------------------------------
1 | // composables/useEffectUpdate.ts
2 | import { watch } from "vue";
3 | function useEffectUpdate(attrs, instance) {
4 | watch(
5 | () => useGetPropsByAttrs(attrs),
6 | (newConfig) => {
7 | instance.set(newConfig);
8 | }
9 | );
10 | }
11 |
12 | // composables/useGetPropsAndEventByAttrs/index.ts
13 | function useGetPropsByAttrs(attrs) {
14 | const config = {};
15 | Object.keys(attrs).forEach((key) => {
16 | if (!key.startsWith("on"))
17 | config[key] = attrs[key];
18 | });
19 | return config;
20 | }
21 |
22 | // components/application/index.ts
23 | import { App } from "leafer-ui";
24 | import { defineComponent, h, markRaw as markRaw2, onMounted, onUnmounted, renderSlot, shallowRef } from "vue";
25 |
26 | // composables/useLogger.ts
27 | function useLogger() {
28 | function log2(colorInfo) {
29 | const logData = {
30 | content: "",
31 | css: []
32 | };
33 | colorInfo.unshift({
34 | content: " [Leafer-vue warn:] ",
35 | color: "#FFF"
36 | });
37 | colorInfo.forEach((item, index) => {
38 | logData.content += `%c${item?.content ?? " "}`;
39 | logData.css.push(
40 | `${item?.color ? `color:${item.color}` : ""}${item?.backgroundColor ? `;background:${item.backgroundColor}` : ""};padding: 0px${index === 0 ? ";border-top-left-radius: 25px; border-bottom-left-radius: 8px" : ""}${index === colorInfo.length - 1 ? ";border-top-right-radius: 8px; border-bottom-right-radius: 8px" : ""}`
41 | );
42 | });
43 | console.warn(
44 | `${logData.content}`,
45 | ...logData.css
46 | );
47 | }
48 | return {
49 | log: log2
50 | };
51 | }
52 |
53 | // utils/index.ts
54 | function isOn(key) {
55 | return key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97);
56 | }
57 | function getEventNameByAttrName(attrName) {
58 | return attrName.replace(/^(on:?)?|Once$/g, "").replace(/([A-Z])/g, (_match, letter, index) => index === 0 ? letter.toLowerCase() : `.${letter.toLowerCase()}`);
59 | }
60 |
61 | // renderer/renderer.ts
62 | import { UI } from "leafer-ui";
63 | import { camelize, createRenderer, markRaw } from "vue";
64 |
65 | // tags/Empty.ts
66 | import { Leaf } from "leafer-ui";
67 | var Empty = class extends Leaf {
68 | constructor() {
69 | super(...arguments);
70 | this.visible = false;
71 | }
72 | };
73 |
74 | // renderer/renderer.ts
75 | var { log } = useLogger();
76 | var renderer = createRenderer({
77 | createElement(tag, _, Constructor) {
78 | let element;
79 | if (tag === "Custom") {
80 | if (!Constructor) {
81 | throw new Error("Custom tag must have is attribute");
82 | } else {
83 | element = new Constructor();
84 | }
85 | } else {
86 | element = UI.one({ tag });
87 | }
88 | markRaw(element);
89 | return element;
90 | },
91 | patchProp(el, key, _prevValue, nextValue) {
92 | key = camelize(key);
93 | if (isOn(key)) {
94 | const eventName = getEventNameByAttrName(key);
95 | if (key.endsWith("Once"))
96 | el.once(eventName, nextValue);
97 | else
98 | el.on(eventName, nextValue);
99 | } else {
100 | el[key] = nextValue === "" ? true : nextValue;
101 | }
102 | },
103 | insert(el, parent) {
104 | if (parent?.tag === "App" && el?.tag !== "Leafer") {
105 | return;
106 | }
107 | if (el && parent)
108 | parent.add(el);
109 | },
110 | remove(el) {
111 | el?.destroy();
112 | },
113 | createText(text) {
114 | const trimmedText = text.trim();
115 | if (trimmedText) {
116 | log([
117 | {
118 | content: "Direct text writing is not supported, please use "
119 | },
120 | {
121 | color: "#6eacf8",
122 | backgroundColor: "#222222",
123 | content: ``
124 | },
125 | {
126 | content: " instead"
127 | }
128 | ]);
129 | }
130 | return new Empty();
131 | },
132 | createComment() {
133 | return new Empty();
134 | },
135 | setText() {
136 | },
137 | setElementText() {
138 | },
139 | parentNode(node) {
140 | return node?.parent;
141 | },
142 | nextSibling(node) {
143 | if (!node || !node.parent)
144 | return null;
145 | const children = node.parent.children;
146 | const index = children.findIndex((_node) => _node.innerId === node.innerId);
147 | return index + 1 < children.length ? children[index + 1] : null;
148 | }
149 | });
150 |
151 | // renderer/index.ts
152 | var createApp = renderer.createApp;
153 |
154 | // components/application/index.ts
155 | var LeaferApp = defineComponent({
156 | inheritAttrs: false,
157 | setup(_props, { slots, expose, attrs }) {
158 | const canvas = shallowRef();
159 | const container = shallowRef();
160 | const config = useGetPropsByAttrs(attrs);
161 | function mount() {
162 | const context = new App({
163 | ...config,
164 | view: canvas.value,
165 | width: config.width || 800,
166 | height: config.height || 600
167 | });
168 | container.value = markRaw2(context);
169 | const app = createApp({
170 | render: () => renderSlot(slots, "default")
171 | });
172 | app.mount(container.value);
173 | }
174 | function unMount() {
175 | container.value.destroy();
176 | }
177 | onMounted(() => {
178 | mount();
179 | useEffectUpdate(attrs, container.value);
180 | });
181 | onUnmounted(unMount);
182 | expose({ app: container });
183 | return () => h("canvas", { ref: canvas });
184 | }
185 | });
186 | export {
187 | LeaferApp
188 | };
189 |
--------------------------------------------------------------------------------
/packages/vue3/types/index.ts:
--------------------------------------------------------------------------------
1 | import type { IAppInputData, IBoxInputData, ICanvasInputData, IEllipseInputData, IFrameInputData, IGroupInputData, IImageInputData, ILeaferInputData, ILineInputData, IPathInputData, IPenInputData, IPolygonInputData, IRectInputData, IStarInputData, ITextInputData } from '@leafer-ui/interface'
2 | import type { _LeaferEvent } from './LeaferEventType'
3 | import type { LeaferVueComponent } from './LeaferVueComponent'
4 |
5 | interface LeaferVueComponents {
6 | /**
7 | * Create a Leafer app
8 | * ***
9 | * [Document](https://leafer-vue.netlify.app/guide/components/app/leafer.html)
10 | * |
11 | * [Playground](https://leafer-vue.netlify.app/play/)
12 | */
13 | Leafer: LeaferVueComponent
14 |
15 | /**
16 | * Create a frame, Similar to pages in HTML5
17 | * ***
18 | * [Document](https://leafer-vue.netlify.app/guide/components/container/frame.html)
19 | * |
20 | * [Playground](https://leafer-vue.netlify.app/play/)
21 | */
22 | Frame: LeaferVueComponent
23 | /**
24 | * Create a box, Similar to the DIV in HTML5, it can be nested continuously
25 | * ***
26 | * [Document](https://leafer-vue.netlify.app/guide/components/container/box.html)
27 | * |
28 | * [Playground](https://leafer-vue.netlify.app/play/)
29 | */
30 | Box: LeaferVueComponent
31 | /**
32 | * Create a group, Similar to the DIV in HTML5, it has no style of its own and can be nested continuously
33 | * ***
34 | * [Document](https://leafer-vue.netlify.app/guide/components/container/group.html)
35 | * |
36 | * [Playground](https://leafer-vue.netlify.app/play/)
37 | */
38 | Group: LeaferVueComponent
39 |
40 | /**
41 | * Create a rectangle
42 | * ***
43 | * [Document](https://leafer-vue.netlify.app/guide/components/shape/rect.html)
44 | * |
45 | * [Playground](https://leafer-vue.netlify.app/play/)
46 | */
47 | Rect: LeaferVueComponent
48 | /**
49 | * Create an ellipse
50 | * ***
51 | * [Document](https://leafer-vue.netlify.app/guide/components/shape/ellipse.html)
52 | * |
53 | * [Playground](https://leafer-vue.netlify.app/play/)
54 | */
55 | Ellipse: LeaferVueComponent
56 | /**
57 | * Create a polygon
58 | * ***
59 | * [Document](https://leafer-vue.netlify.app/guide/components/shape/polygon.html)
60 | * |
61 | * [Playground](https://leafer-vue.netlify.app/play/)
62 | */
63 | Polygon: LeaferVueComponent
64 | /**
65 | * Create a star
66 | * ***
67 | * [Document](https://leafer-vue.netlify.app/guide/components/shape/star.html)
68 | * |
69 | * [Playground](https://leafer-vue.netlify.app/play/)
70 | */
71 | Star: LeaferVueComponent
72 | /**
73 | * Create a line
74 | * ***
75 | * [Document](https://leafer-vue.netlify.app/guide/components/shape/line.html)
76 | * |
77 | * [Playground](https://leafer-vue.netlify.app/play/)
78 | */
79 | Line: LeaferVueComponent
80 |
81 | /**
82 | * Create a path, can draw a graphic of any shape.
83 | * ***
84 | * [Document](https://leafer-vue.netlify.app/guide/components/path/path.html)
85 | * |
86 | * [Playground](https://leafer-vue.netlify.app/play/)
87 | */
88 | Path: LeaferVueComponent
89 | /**
90 | * Create a pen, can draw a graphic of any shape.
91 | * ***
92 | * [Document](https://leafer-vue.netlify.app/guide/components/path/pen.html)
93 | * |
94 | * [Playground](https://leafer-vue.netlify.app/play/)
95 | */
96 | Pen: LeaferVueComponent
97 |
98 | /**
99 | * Image object, all graphics support displaying pictures through *fill*
100 | * ***
101 | * [Document](https://leafer-vue.netlify.app/guide/components/image/image.html)
102 | * |
103 | * [Playground](https://leafer-vue.netlify.app/play/)
104 | */
105 | Image: LeaferVueComponent
106 | /**
107 | * Canvas object that can freely draw, manipulate pixels, or directly draw other graphics onto the Canvas
108 | * ***
109 | * [Document](https://leafer-vue.netlify.app/guide/components/image/canvas.html)
110 | * |
111 | * [Playground](https://leafer-vue.netlify.app/play/)
112 | */
113 | Canvas: LeaferVueComponent
114 |
115 | /**
116 | * Draw text. The text display effect is basically the same as that of HTML5
117 | * ***
118 | * [Document](https://leafer-vue.netlify.app/guide/components/text/text.html)
119 | * |
120 | * [Playground](https://leafer-vue.netlify.app/play/)
121 | */
122 | Text: LeaferVueComponent
123 |
124 | /**
125 | * Custom component, must have a *is* property
126 | * ***
127 | * [Document](https://leafer-vue.netlify.app/guide/custom/custom.html)
128 | * |
129 | * [Playground](https://leafer-vue.netlify.app/play/)
130 | */
131 | Custom: LeaferVueComponent<{
132 | is: new () => unknown
133 | }>
134 | }
135 |
136 | declare module 'vue' {
137 | interface GlobalComponents extends LeaferVueComponents {}
138 | }
139 |
140 | declare module '@vue/runtime-core' {
141 | interface GlobalComponents extends LeaferVueComponents {}
142 | }
143 |
144 | declare module '@vue/runtime-dom' {
145 | interface GlobalComponents extends LeaferVueComponents {}
146 | }
147 |
148 | /**
149 | * The root component used to create a Leafer application. All Leafer elements must be child components of it.
150 | * ***
151 | * [Document](https://leafer-vue.netlify.app/guide/components/app/leaferApp.html)
152 | * |
153 | * [Playground](https://leafer-vue.netlify.app/play/)
154 | */
155 | declare const LeaferApp: LeaferVueComponent
156 | export { LeaferApp }
157 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitepress'
2 | import { mdPlugin } from './plugin'
3 |
4 | export default defineConfig({
5 | outDir: './dist',
6 | title: 'leafer-vue',
7 | description: 'leafer-vue document',
8 | appearance: 'dark',
9 | head: [
10 | ['link', { rel: 'icon', href: '/logo.ico' }],
11 | ['meta', { name: 'author', content: 'FliPPeDround' }],
12 | ],
13 | themeConfig: {
14 | logo: '/logo.png',
15 | search: {
16 | provider: 'local',
17 | },
18 | nav: [
19 | {
20 | text: '文档',
21 | items: [
22 | { text: '介绍', link: '/guide/start/introduction' },
23 | { text: '安装', link: '/guide/start/usage' },
24 | { text: '组件', link: '/guide/components/app/leaferApp' },
25 | { text: '事件', link: '/guide/events/index' },
26 | ],
27 | },
28 | {
29 | text: '插件',
30 | link: '/plugin/index',
31 | },
32 | {
33 | text: '演练场',
34 | link: 'https://leafer-vue.netlify.app/play/',
35 | },
36 | {
37 | text: '赞助',
38 | link: 'https://afdian.com/a/flippedround',
39 | },
40 | {
41 | text: '生态',
42 | items: [
43 | { text: 'vue2', link: '/intro/vue2' },
44 | { text: 'uni-app', link: '/intro/uni' },
45 | { text: 'nuxt', link: '/intro/nuxt' },
46 | { text: 'awesome', link: '/awesome' },
47 | ],
48 | },
49 | ],
50 |
51 | sidebar: {
52 | '/guide/': [
53 | {
54 | text: '开始',
55 | items: [
56 | { text: '介绍', link: '/guide/start/introduction' },
57 | { text: '安装', link: '/guide/start/usage' },
58 | ],
59 | },
60 | {
61 | text: '应用组件',
62 | items: [
63 | {
64 | text: 'LeaferApp',
65 | link: '/guide/components/app/leaferApp',
66 | },
67 | {
68 | text: 'Leafer',
69 | link: '/guide/components/app/leafer',
70 | },
71 | ],
72 | },
73 | {
74 | text: '容器组件',
75 | items: [
76 | {
77 | text: 'Frame',
78 | link: '/guide/components/container/frame',
79 | },
80 | {
81 | text: 'Box',
82 | link: '/guide/components/container/box',
83 | },
84 | {
85 | text: 'Group',
86 | link: '/guide/components/container/group',
87 | },
88 | ],
89 | },
90 | {
91 | text: '图形组件',
92 | items: [
93 | {
94 | text: 'Rect',
95 | link: '/guide/components/shape/rect',
96 | },
97 | {
98 | text: 'Ellipse',
99 | link: '/guide/components/shape/ellipse',
100 | },
101 | {
102 | text: 'Polygon',
103 | link: '/guide/components/shape/polygon',
104 | },
105 | {
106 | text: 'Star',
107 | link: '/guide/components/shape/star',
108 | },
109 | {
110 | text: 'Line',
111 | link: '/guide/components/shape/line',
112 | },
113 | ],
114 | },
115 | {
116 | text: '路径组件',
117 | items: [
118 | {
119 | text: 'Path',
120 | link: '/guide/components/path/path',
121 | },
122 | {
123 | text: 'Pen',
124 | link: '/guide/components/path/pen',
125 | },
126 | ],
127 | },
128 | {
129 | text: '图像组件',
130 | items: [
131 | {
132 | text: 'Image',
133 | link: '/guide/components/image/image',
134 | },
135 | {
136 | text: 'Canvas',
137 | link: '/guide/components/image/canvas',
138 | },
139 | ],
140 | },
141 | {
142 | text: '文字组件',
143 | items: [
144 | {
145 | text: 'Text',
146 | link: '/guide/components/text/text',
147 | },
148 | ],
149 | },
150 | {
151 | text: '自定义元素',
152 | items: [
153 | {
154 | text: 'Custom',
155 | link: '/guide/components/custom/custom',
156 | },
157 | ],
158 | },
159 | {
160 | text: '事件',
161 | items: [
162 | {
163 | text: '事件处理',
164 | link: '/guide/events/events',
165 | },
166 | {
167 | text: '交互事件',
168 | collapsed: true,
169 | items: [
170 | { text: 'PointerEvent', link: '/guide/events/action/Pointer/pointer' },
171 | { text: 'DragEvent', link: '/guide/events/action/Drag/drag' },
172 | { text: 'DropEvent', link: '/guide/events/action/Drop/drop' },
173 | { text: 'SwipeEvent', link: '/guide/events/action/Swipe/swipe' },
174 | { text: 'MoveEvent', link: '/guide/events/action/Move/move' },
175 | { text: 'ZoomEvent', link: '/guide/events/action/Zoom/zoom' },
176 | { text: 'RotateEvent', link: '/guide/events/action/Rotate/rotate' },
177 | { text: 'KeyEvent', link: '/guide/events/action/Key/key' },
178 | ],
179 | },
180 | {
181 | text: '元素事件',
182 | collapsed: true,
183 | items: [
184 | { text: 'ImageEvent', link: '/guide/events/element/Image/image' },
185 | { text: 'ChildEvent', link: '/guide/events/element/Child/child' },
186 | { text: 'PropertyEvent', link: '/guide/events/element/Property/property' },
187 | ],
188 | },
189 | {
190 | text: '平台事件',
191 | collapsed: true,
192 | items: [
193 | { text: 'LeaferEvent', link: '/guide/events/leafer/Leafer/leafer' },
194 | { text: 'ResizeEvent', link: '/guide/events/leafer/Resize/resize' },
195 | { text: 'RenderEvent', link: '/guide/events/leafer/Render/render' },
196 | { text: 'LayoutEvent', link: '/guide/events/leafer/Layout/layout' },
197 | { text: 'WatchEvent', link: '/guide/events/leafer/Watch/watch' },
198 | ],
199 | },
200 | ],
201 | },
202 | {
203 | text: '其他',
204 | items: [
205 | {
206 | text: '导出',
207 | link: '/guide/other/export/export',
208 | },
209 | {
210 | text: '动画',
211 | link: '/guide/other/animate/animate',
212 | },
213 | ],
214 | },
215 | {
216 | text: '更多',
217 | items: [
218 | {
219 | text: '插件',
220 | link: '/plugin/index',
221 | },
222 | {
223 | text: 'vue2',
224 | link: '/intro/vue2',
225 | },
226 | {
227 | text: 'Nuxt',
228 | link: 'intro/nuxt',
229 | },
230 | {
231 | text: 'uni-app',
232 | link: '/intro/uni',
233 | },
234 | {
235 | text: 'Awesome',
236 | link: '/awesome',
237 | },
238 | ],
239 | },
240 | ],
241 | },
242 |
243 | editLink: {
244 | pattern: 'https://github.com/FliPPeDround/leafer-vue/edit/main/docs/:path',
245 | text: '帮助改善此页面!',
246 | },
247 | socialLinks: [
248 | {
249 | icon: 'github',
250 | link: 'https://github.com/FliPPeDround/leafer-vue',
251 | },
252 | ],
253 | footer: {
254 | message: 'Released under the MIT License.',
255 | copyright: 'Copyright © 2023-PRESENT FliPPeDround',
256 | },
257 | },
258 |
259 | markdown: {
260 | config: md => md.use(mdPlugin),
261 | },
262 | })
263 |
--------------------------------------------------------------------------------