apptify 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_LICENSES.txt +23 -0
- package/dist/apptify.css +1 -1
- package/dist/apptify.js +2395 -2451
- package/dist/components/AppleCard.vue.d.ts +1 -1
- package/dist/components/content.d.ts +81 -20
- package/dist/components/forms.d.ts +2 -2
- package/dist/components/foundation.d.ts +133 -11
- package/dist/components/mobile-image-viewer.d.ts +35 -12
- package/dist/components/navibar.d.ts +2 -2
- package/dist/components/overlays.d.ts +10 -59
- package/dist/components/scrollbar.d.ts +88 -0
- package/dist/components/speed-dial.d.ts +157 -0
- package/dist/core/context.d.ts +11 -0
- package/dist/core/device.d.ts +2 -0
- package/dist/core/image-gallery-motion.d.ts +2 -1
- package/dist/core/image-motion.d.ts +1 -1
- package/dist/core/layers.d.ts +1 -0
- package/dist/core/motion-driver.d.ts +40 -0
- package/dist/core/motion.d.ts +1 -1
- package/dist/core/ripple.d.ts +16 -0
- package/dist/core/scroll-motion.d.ts +6 -0
- package/dist/core/scrollbar.d.ts +14 -0
- package/dist/core/speed-dial.d.ts +21 -0
- package/dist/index.d.ts +237 -26
- package/docs/COMPONENTS.md +10 -6
- package/package.json +5 -7
- package/templates/playground/README.md +1 -1
- package/templates/playground/package.json +1 -2
- package/templates/playground/source-snapshot.json +298 -65
- package/templates/playground/src/App.vue +1 -0
- package/templates/playground/src/ComponentDemo.vue +30 -21
- package/templates/playground/src/catalog.ts +70 -65
- package/templates/playground/src/components/ComponentIndex.vue +10 -14
- package/templates/playground/src/components/MasonryFeed.vue +52 -0
- package/templates/playground/src/components/UserMenu.vue +1 -1
- package/templates/playground/src/components/forbidden-paper-scene.vue +367 -0
- package/templates/playground/src/components/forbidden-paper-story.ts +233 -0
- package/templates/playground/src/editor/ComponentPlayground.vue +89 -0
- package/templates/playground/src/editor/ConfigurableEditor.vue +1 -1
- package/templates/playground/src/editor/apple-input-source.ts +1 -1
- package/templates/playground/src/editor/component-metadata.ts +72 -0
- package/templates/playground/src/editor/component-playground.ts +162 -0
- package/templates/playground/src/editor/contract.ts +1 -1
- package/templates/playground/src/editor/documents.ts +11 -2
- package/templates/playground/src/editor/handbook.ts +22 -0
- package/templates/playground/src/editor/handbooks/apple-accordion.ts +66 -0
- package/templates/playground/src/editor/handbooks/apple-action-sheet.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-alert.ts +67 -0
- package/templates/playground/src/editor/handbooks/apple-aside.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-auto-size.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-autocomplete.ts +114 -0
- package/templates/playground/src/editor/handbooks/apple-avatar-group.ts +50 -0
- package/templates/playground/src/editor/handbooks/apple-avatar.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-back-top.ts +62 -0
- package/templates/playground/src/editor/handbooks/apple-badge.ts +62 -0
- package/templates/playground/src/editor/handbooks/apple-breadcrumbs.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-button.ts +92 -0
- package/templates/playground/src/editor/handbooks/apple-card.ts +112 -0
- package/templates/playground/src/editor/handbooks/apple-cascader.ts +102 -0
- package/templates/playground/src/editor/handbooks/apple-checkbox.ts +85 -0
- package/templates/playground/src/editor/handbooks/apple-color-picker.ts +79 -0
- package/templates/playground/src/editor/handbooks/apple-container.ts +26 -0
- package/templates/playground/src/editor/handbooks/apple-date-picker.ts +104 -0
- package/templates/playground/src/editor/handbooks/apple-dialog.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-divider.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-drawer.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-empty.ts +43 -0
- package/templates/playground/src/editor/handbooks/apple-floating-group.ts +51 -0
- package/templates/playground/src/editor/handbooks/apple-form-field.ts +69 -0
- package/templates/playground/src/editor/handbooks/apple-form.ts +76 -0
- package/templates/playground/src/editor/handbooks/apple-grid.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-image.ts +122 -0
- package/templates/playground/src/editor/handbooks/apple-infinite-scroll.ts +73 -0
- package/templates/playground/src/editor/handbooks/apple-link.ts +68 -0
- package/templates/playground/src/editor/handbooks/apple-list.ts +67 -0
- package/templates/playground/src/editor/handbooks/apple-menu.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-navibar.ts +100 -0
- package/templates/playground/src/editor/handbooks/apple-otp-input.ts +108 -0
- package/templates/playground/src/editor/handbooks/apple-overlay-host.ts +13 -0
- package/templates/playground/src/editor/handbooks/apple-pagination.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-popover.ts +112 -0
- package/templates/playground/src/editor/handbooks/apple-progress.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-provider.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-pull-refresh.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-radio-group.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-rate.ts +97 -0
- package/templates/playground/src/editor/handbooks/apple-scroll-bar.ts +15 -0
- package/templates/playground/src/editor/handbooks/apple-search.ts +49 -0
- package/templates/playground/src/editor/handbooks/apple-segmented-control.ts +85 -0
- package/templates/playground/src/editor/handbooks/apple-select.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-sheet.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-skeleton.ts +74 -0
- package/templates/playground/src/editor/handbooks/apple-slider.ts +103 -0
- package/templates/playground/src/editor/handbooks/apple-snackbar.ts +89 -0
- package/templates/playground/src/editor/handbooks/apple-speed-dial-item.ts +16 -0
- package/templates/playground/src/editor/handbooks/apple-speed-dial.ts +13 -0
- package/templates/playground/src/editor/handbooks/apple-spinner.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-stack.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-statistic.ts +69 -0
- package/templates/playground/src/editor/handbooks/apple-stepper.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-steps.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-switch.ts +79 -0
- package/templates/playground/src/editor/handbooks/apple-tab-bar.ts +66 -0
- package/templates/playground/src/editor/handbooks/apple-table.ts +176 -0
- package/templates/playground/src/editor/handbooks/apple-tabs.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-tag.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-textarea.ts +109 -0
- package/templates/playground/src/editor/handbooks/apple-timeline.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-tooltip.ts +45 -0
- package/templates/playground/src/editor/handbooks/apple-transition.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-tree.ts +84 -0
- package/templates/playground/src/editor/handbooks/apple-upload.ts +126 -0
- package/templates/playground/src/editor/playground-examples.ts +105 -0
- package/templates/playground/src/images.ts +3 -0
- package/templates/playground/src/router/index.ts +19 -2
- package/templates/playground/src/style.css +4 -4
- package/templates/playground/src/views/403.vue +65 -0
- package/templates/playground/src/views/component-docs/[component].vue +11 -14
- package/templates/playground/src/views/components.vue +11 -41
- package/templates/playground/src/views/index.vue +3 -1
- package/templates/playground/src/views/settings.vue +5 -2
- package/tooling/create-playground.mjs +0 -0
- package/tooling/prepare-playground.mjs +3 -3
- package/tooling/test-playground-package.mjs +10 -0
- package/dist/components/gallery-scrollbar.d.ts +0 -46
- package/dist/core/desktop-image-enter.d.ts +0 -6
- package/dist/core/desktop-image-return.d.ts +0 -1
- package/templates/playground/src/components/DirectoryDialog.vue +0 -20
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { defineHandbook } from '../handbook'
|
|
2
|
+
|
|
3
|
+
export default defineHandbook({
|
|
4
|
+
"name": "AppleTransition",
|
|
5
|
+
"source": "src/components/motion.ts",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"name": "motion",
|
|
9
|
+
"type": "'inherit' | 'auto' | 'full' | 'reduced' | 'none'",
|
|
10
|
+
"default": "\"inherit\"",
|
|
11
|
+
"description": "局部动效偏好。inherit 跟随外层;全局与系统的减少动效设置仍是上限。"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "name",
|
|
15
|
+
"type": "'page' | 'slide-x' | 'slide-y' | 'fade'",
|
|
16
|
+
"default": "\"slide-y\"",
|
|
17
|
+
"description": "过渡样式:page、slide-x、slide-y、fade。减少动效时退化为淡入淡出,none 关闭 CSS 过渡。"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "mode",
|
|
21
|
+
"type": "'out-in' | 'in-out' | 'default'",
|
|
22
|
+
"default": "\"out-in\"",
|
|
23
|
+
"description": "out-in 先离开再进入,in-out 先进入再离开,default 同时进行。"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "appear",
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"default": "true",
|
|
29
|
+
"description": "首次渲染时也执行进入过渡。"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"events": [],
|
|
33
|
+
"slots": [
|
|
34
|
+
{
|
|
35
|
+
"name": "default",
|
|
36
|
+
"type": "() => VNode[](无 slot props)",
|
|
37
|
+
"description": "单个可过渡元素或组件;切换时提供不同 key。"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"methods": [],
|
|
41
|
+
"notes": [
|
|
42
|
+
"default 插槽只放一个可过渡根节点。切换内容使用 key;外部减少动效偏好仍约束过渡。"
|
|
43
|
+
]
|
|
44
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { defineHandbook } from '../handbook'
|
|
2
|
+
|
|
3
|
+
export default defineHandbook({
|
|
4
|
+
"name": "AppleTree",
|
|
5
|
+
"source": "src/components/content.ts",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"name": "searchable",
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"default": "true",
|
|
11
|
+
"description": "默认显示顶部搜索,匹配名称、value 和描述并保留祖先路径;清空搜索恢复原展开状态。"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "mobileDirectory",
|
|
15
|
+
"type": "boolean",
|
|
16
|
+
"default": "true",
|
|
17
|
+
"description": "宽度不超过 900px 时自动进入全局 AppleSpeedDial 目录;设为 false 保持原位。没有宿主时保持原位。多个目录使用 AppleTabBar 切换,label 作为标签。"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "motion",
|
|
21
|
+
"type": "'inherit' | 'auto' | 'full' | 'reduced' | 'none'",
|
|
22
|
+
"default": "\"inherit\"",
|
|
23
|
+
"description": "局部动效偏好。inherit 跟随外层;全局与系统的减少动效设置仍是上限。"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "items",
|
|
27
|
+
"type": "AppleTreeItem[]",
|
|
28
|
+
"default": "[]",
|
|
29
|
+
"description": "树节点为 { label: string; value: string | number; disabled?: boolean; description?: string; href?: string; content?: string } & { children?: AppleTreeItem[] };value 在整棵树中应唯一。"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "modelValue",
|
|
33
|
+
"type": "string | number",
|
|
34
|
+
"default": "undefined",
|
|
35
|
+
"description": "选中叶节点的 value;点击父节点只切换展开。折叠后可将高亮显示到最近可见祖先,原选择仍保留。"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "expanded",
|
|
39
|
+
"type": "(string | number)[]",
|
|
40
|
+
"default": "undefined",
|
|
41
|
+
"description": "展开节点 value 的数组,可用 v-model:expanded;省略时内部管理。"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "disabled",
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": "false",
|
|
47
|
+
"description": "禁用控件的交互。选项自身的 disabled 仍然独立生效。"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "label",
|
|
51
|
+
"type": "string",
|
|
52
|
+
"default": "\"树形列表\"",
|
|
53
|
+
"description": "控件名称或区域的可访问名称。无可见名称时应提供 aria-label。"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"events": [
|
|
57
|
+
{
|
|
58
|
+
"name": "update:modelValue",
|
|
59
|
+
"type": "(value: string | number) => void",
|
|
60
|
+
"description": "交互更新时发出新模型值;可使用 v-model 接收。"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "update:expanded",
|
|
64
|
+
"type": "(value: (string | number)[]) => void",
|
|
65
|
+
"description": "交互更新 expanded 时发出新值;可使用对应的命名 v-model 接收。"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "select",
|
|
69
|
+
"type": "(item: AppleTreeItem) => void",
|
|
70
|
+
"description": "选择可用叶节点时发出完整节点;父节点点击仅切换 expanded。"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"slots": [
|
|
74
|
+
{
|
|
75
|
+
"name": "item",
|
|
76
|
+
"type": "({ item: AppleTreeItem, expanded: boolean, selected: boolean }) => VNode[]",
|
|
77
|
+
"description": "替代节点行的文字,不替代节点的展开和键盘处理。"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"methods": [],
|
|
81
|
+
"notes": [
|
|
82
|
+
"方向键、Home/End、Enter/空格可操作树。父节点只展开/折叠,选择事件只来自叶节点;没有复选框级联。"
|
|
83
|
+
]
|
|
84
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { defineHandbook } from '../handbook'
|
|
2
|
+
|
|
3
|
+
export default defineHandbook({
|
|
4
|
+
"name": "AppleUpload",
|
|
5
|
+
"source": "src/components/forms.ts",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"name": "label",
|
|
9
|
+
"type": "string",
|
|
10
|
+
"default": "\"\"",
|
|
11
|
+
"description": "控件名称或区域的可访问名称。无可见名称时应提供 aria-label。"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "hint",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"default": "\"\"",
|
|
17
|
+
"description": "字段帮助文字;error 非空时优先显示错误,帮助文字暂时隐藏。"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "error",
|
|
21
|
+
"type": "string",
|
|
22
|
+
"default": "\"\"",
|
|
23
|
+
"description": "显示字段错误和错误样式,不会自行执行校验或阻止表单提交。"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "disabled",
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"default": "false",
|
|
29
|
+
"description": "禁用控件的交互。选项自身的 disabled 仍然独立生效。"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "loading",
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"default": "false",
|
|
35
|
+
"description": "显示忙碌状态;表单控件在加载时禁止编辑或选择。"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "required",
|
|
39
|
+
"type": "boolean",
|
|
40
|
+
"default": "false",
|
|
41
|
+
"description": "为字段添加必填标记并传递必填约束;不会自动生成 error 文本。"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "motion",
|
|
45
|
+
"type": "'inherit' | 'auto' | 'full' | 'reduced' | 'none'",
|
|
46
|
+
"default": "\"inherit\"",
|
|
47
|
+
"description": "局部动效偏好。inherit 跟随外层;全局与系统的减少动效设置仍是上限。"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "modelValue",
|
|
51
|
+
"type": "File[]",
|
|
52
|
+
"default": "[]",
|
|
53
|
+
"description": "浏览器 File 对象数组,不是 URL 或文件名数组。组件只选择文件,不发送网络请求。"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "accept",
|
|
57
|
+
"type": "string",
|
|
58
|
+
"default": "\"\"",
|
|
59
|
+
"description": "允许的扩展名或 MIME 类型,逗号分隔,例如 .png,image/jpeg,image/*;选择和拖放都会检查。"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "multiple",
|
|
63
|
+
"type": "boolean",
|
|
64
|
+
"default": "false",
|
|
65
|
+
"description": "允许多个文件;开启时保留既有文件并追加,关闭时本次选择替换原数组。"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "maxSize",
|
|
69
|
+
"type": "number",
|
|
70
|
+
"default": "Infinity",
|
|
71
|
+
"description": "单个文件大小上限,单位字节。默认 Infinity;超限通过 reject 返回。"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "maxFiles",
|
|
75
|
+
"type": "number",
|
|
76
|
+
"default": "Infinity",
|
|
77
|
+
"description": "文件数量上限。multiple=false 时实际最多一个;重复文件会跳过。"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "capture",
|
|
81
|
+
"type": "'user' | 'environment'",
|
|
82
|
+
"default": "undefined",
|
|
83
|
+
"description": "原生 file 输入的移动设备采集提示:user 或 environment,是否生效由浏览器决定。"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "buttonText",
|
|
87
|
+
"type": "string",
|
|
88
|
+
"default": "\"选择文件\"",
|
|
89
|
+
"description": "文件选择区域的操作文字。"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"events": [
|
|
93
|
+
{
|
|
94
|
+
"name": "update:modelValue",
|
|
95
|
+
"type": "(value: File[]) => void",
|
|
96
|
+
"description": "交互更新时发出新模型值;可使用 v-model 接收。"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "change",
|
|
100
|
+
"type": "(value: File[]) => void",
|
|
101
|
+
"description": "用户提交选择或调整时发出当前值;直接从父组件赋值不会主动发出此事件。"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"name": "reject",
|
|
105
|
+
"type": "(rejections: { file: File; reason: string }[]) => void",
|
|
106
|
+
"description": "返回本次未通过类型、大小或数量检查的文件及原因。"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "remove",
|
|
110
|
+
"type": "(file: File) => void",
|
|
111
|
+
"description": "移除单个文件时,在模型更新和 change 之后发出被移除的 File。"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"slots": [
|
|
115
|
+
{
|
|
116
|
+
"name": "default",
|
|
117
|
+
"type": "() => VNode[](无 slot props)",
|
|
118
|
+
"description": "追加到文件选择区域中的内容;插槽内按钮保留自身操作。"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"methods": [],
|
|
122
|
+
"notes": [
|
|
123
|
+
"选中数据是本地 File[]。上传请求由应用实现;示例只记录文件选择与拒绝结果。",
|
|
124
|
+
"字段名称、帮助和错误文字只用于说明。直接操作控件;原生 name、autocomplete 等属性需按实际输入类型使用。"
|
|
125
|
+
]
|
|
126
|
+
})
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export interface SlotExample { scope?: string; code: string; enabled?: boolean }
|
|
2
|
+
export interface PlaygroundExample {
|
|
3
|
+
props?: Record<string, string>
|
|
4
|
+
slots?: Record<string, SlotExample>
|
|
5
|
+
before?: string
|
|
6
|
+
actions?: { label: string; expression: string }[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const options = JSON.stringify([{ label: '第一项', value: 'first' }, { label: '第二项', value: 'second' }, { label: '不可用', value: 'disabled', disabled: true }])
|
|
10
|
+
const content = '<p>这里是组件内容。</p>'
|
|
11
|
+
const blocks = '<div v-for="n in 3" :key="n" class="tile">{{ n }}</div>'
|
|
12
|
+
const actions = JSON.stringify([{ label: '收藏', value: 'favorite' }, { label: '分享', value: 'share' }, { label: '删除', value: 'delete', danger: true }, { label: '不可用', value: 'disabled', disabled: true }])
|
|
13
|
+
const image = (color: string, text: string) => 'data:image/svg+xml,' + encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480"><rect width="640" height="480" fill="${color}"/><text x="320" y="260" fill="white" text-anchor="middle" font-size="64" font-family="sans-serif">${text}</text></svg>`)
|
|
14
|
+
const gallery = JSON.stringify([{ src: image('#0071e3', 'Apptify'), alt: '蓝色示例图片', label: '蓝色' }, { src: image('#bf5af2', 'Vue'), alt: '紫色示例图片', label: '紫色' }, { src: image('#34c759', 'Hello'), alt: '绿色示例图片', label: '绿色' }])
|
|
15
|
+
const scroll = '<div id="demo-scroll" class="scroll-demo" tabindex="0" aria-label="滚动示例"><div style="height: 900px">向下滚动,体验回到顶部。</div></div>'
|
|
16
|
+
|
|
17
|
+
const examples: Record<string, PlaygroundExample> = {
|
|
18
|
+
AppleScrollBar: { props: { target: '"#demo-scroll"' }, before: '<div id="demo-scroll" class="scroll-demo" tabindex="0" aria-label="滚动示例"><div style="width: 760px; height: 900px; display: grid; gap: 16px"><div v-for="n in 12" :key="n" class="tile">{{ n }}</div></div></div>' },
|
|
19
|
+
AppleButton: { slots: { default: { code: '继续', enabled: true } } },
|
|
20
|
+
AppleLink: { props: { as: '"button"' }, slots: { default: { code: '了解更多', enabled: true } } },
|
|
21
|
+
AppleProvider: { props: { theme: '"dark"' }, slots: { default: { code: '<AppleInput v-model="fieldValue" label="局部主题" /><AppleButton @click="record(\'click\')">继续</AppleButton>', enabled: true } } },
|
|
22
|
+
AppleContainer: { props: { width: '360' }, slots: { default: { code: blocks, enabled: true } } },
|
|
23
|
+
AppleStack: { props: { direction: '"row"' }, slots: { default: { code: blocks, enabled: true } } },
|
|
24
|
+
AppleGrid: { props: { min: '120' }, slots: { default: { code: blocks, enabled: true } } },
|
|
25
|
+
AppleCard: { props: { title: '"Apptify"', subtitle: '"组件文档"', text: '"修改配置,体验卡片。"', image: JSON.stringify(image('#0071e3', 'Apptify')), imageAlt: '"蓝色示例图片"' } },
|
|
26
|
+
AppleImage: { props: { gallery, index: '0' }, slots: { item: { scope: '{ item, index, active }', code: '<div class="tile">{{ index + 1 }} · {{ item.label }} · {{ active }}</div>' } } },
|
|
27
|
+
AppleSearch: { props: { modelValue: '"Apptify"' } },
|
|
28
|
+
AppleTextarea: { props: { label: '"个人简介"', modelValue: '"写下你的想法。"', maxlength: '160', counter: 'true' } },
|
|
29
|
+
AppleSelect: { props: { label: '"选择项目"', items: options, modelValue: '"first"' } },
|
|
30
|
+
AppleAutocomplete: { props: { label: '"搜索项目"', items: options, modelValue: 'null' } },
|
|
31
|
+
AppleCheckbox: { props: { label: '"同意接收更新"' } },
|
|
32
|
+
AppleSwitch: { props: { label: '"接收通知"' } },
|
|
33
|
+
AppleRadioGroup: { props: { label: '"配送方式"', items: options, modelValue: '"first"' } },
|
|
34
|
+
AppleSegmentedControl: { props: { label: '"统计周期"', items: options, modelValue: '"first"' } },
|
|
35
|
+
AppleSlider: { props: { label: '"音量"', modelValue: '40' } },
|
|
36
|
+
AppleStepper: { props: { label: '"数量"', modelValue: '2', min: '0', max: '10' } },
|
|
37
|
+
AppleDatePicker: { props: { label: '"到店日期"', modelValue: '"2026-10-09"' } },
|
|
38
|
+
AppleColorPicker: { props: { label: '"强调色"' } },
|
|
39
|
+
AppleUpload: { props: { label: '"选择文件"', multiple: 'true', maxFiles: '3', maxSize: '5242880' } },
|
|
40
|
+
AppleOtpInput: { props: { modelValue: '""' } },
|
|
41
|
+
AppleCascader: { props: { label: '"所在地区"', items: JSON.stringify([{ label: '华东', value: 'east', children: [{ label: '上海', value: 'shanghai' }, { label: '杭州', value: 'hangzhou' }] }, { label: '华南', value: 'south', children: [{ label: '深圳', value: 'shenzhen' }] }]), levelLabels: '["地区", "城市"]' } },
|
|
42
|
+
AppleRate: { props: { modelValue: '3' } },
|
|
43
|
+
AppleForm: { props: { validator: '(data) => data.get("name") ? true : "请输入姓名"' }, slots: { default: { scope: '{ loading }', code: '<AppleInput v-model="fieldValue" name="name" label="姓名" required @keydown.enter.prevent="$refs.demo.submit()" /><AppleButton :loading="loading" @click="$refs.demo.submit()">提交</AppleButton><AppleLink as="button" @click="$refs.demo.reset()">重置</AppleLink>', enabled: true } } },
|
|
44
|
+
AppleFormField: { props: { label: '"个人网站"', hint: '"输入你的主页地址"' }, slots: { default: { scope: 'field', code: '<input v-bind="field" v-model="fieldValue" class="apple-control" type="url" placeholder="https://example.com" />', enabled: true } } },
|
|
45
|
+
AppleNavibar: { props: { brand: '"Apptify"', brandHref: '"#"', fixed: 'false', items: options, modelValue: '"first"' } },
|
|
46
|
+
AppleAside: { slots: { default: { code: '<p>侧栏导航</p>' + blocks, enabled: true } } },
|
|
47
|
+
AppleTabs: { props: { items: options, modelValue: '"first"' }, slots: { default: { scope: '{ item, value }', code: '<p>{{ item?.label }} · {{ value }}</p>', enabled: true } } },
|
|
48
|
+
AppleTabBar: { props: { items: options, modelValue: '"first"' }, slots: { default: { scope: '{ item, value }', code: '<p>{{ item?.label }} · {{ value }}</p>', enabled: true } } },
|
|
49
|
+
AppleBreadcrumbs: { props: { items: '[{"label":"首页","value":"home"},{"label":"组件","value":"components"},{"label":"当前页","value":"current"}]' } },
|
|
50
|
+
ApplePagination: { props: { total: '120' } },
|
|
51
|
+
AppleSteps: { props: { items: options, clickable: 'true' } },
|
|
52
|
+
AppleAccordion: { props: { items: '[{"label":"如何使用?","value":"first","content":"从 apptify 导入组件。"},{"label":"支持键盘吗?","value":"second","content":"按钮可以用 Enter 或空格操作。"}]' } },
|
|
53
|
+
AppleTable: { props: { columns: '[{"key":"name","label":"姓名","sortable":true},{"key":"score","label":"评分","sortable":true,"align":"right"}]', rows: '[{"id":1,"name":"林初","score":92},{"id":2,"name":"Alex","score":85},{"id":3,"name":"Taylor","score":97},{"id":4,"name":"Sam","score":88}]', selectable: 'true', pageSize: '3' } },
|
|
54
|
+
AppleTree: { props: { items: '[{"label":"文档","value":"docs","children":[{"label":"指南","value":"guide"},{"label":"API","value":"api"}]},{"label":"设置","value":"settings"}]', expanded: '["docs"]', mobileDirectory: 'false' } },
|
|
55
|
+
AppleList: { props: { items: options, selectable: 'true', modelValue: '"first"' } },
|
|
56
|
+
AppleAvatar: { props: { name: '"林初"', size: '48' } },
|
|
57
|
+
AppleAvatarGroup: { props: { items: '[{"name":"林初"},{"name":"Alex"},{"name":"Taylor"},{"name":"Sam"},{"name":"陈安"}]' } },
|
|
58
|
+
AppleBadge: { props: { value: '8' }, slots: { default: { code: '<AppleButton @click="props.value = Number(props.value || 0) + 1">消息</AppleButton>', enabled: true } } },
|
|
59
|
+
AppleTag: { props: { label: '"设计系统"', closable: 'true' } },
|
|
60
|
+
AppleAlert: { props: { title: '"保存成功"', message: '"内容已同步。"', closable: 'true' } },
|
|
61
|
+
AppleProgress: { props: { modelValue: '68', showValue: 'true' } },
|
|
62
|
+
AppleSpinner: {}, AppleSkeleton: {},
|
|
63
|
+
AppleEmpty: { props: { title: '"还没有收藏"', description: '"收藏的内容会出现在这里。"' } },
|
|
64
|
+
AppleDivider: { props: { label: '"更多选择"' } },
|
|
65
|
+
AppleTimeline: { props: { items: '[{"label":"已创建","value":"created","time":"09:00","description":"订单已创建。"},{"label":"已完成","value":"done","time":"10:00","tone":"success","description":"订单已完成。"}]' } },
|
|
66
|
+
AppleStatistic: { props: { label: '"本月访问"', value: '12840', suffix: '"次"' } },
|
|
67
|
+
AppleDialog: { props: { title: '"保存更改?"', message: '"你的更改会同步到所有设备。"' } },
|
|
68
|
+
AppleDrawer: { props: { title: '"偏好设置"', message: '"修改配置后再次打开体验。"' } },
|
|
69
|
+
AppleSheet: { props: { title: '"分享"', message: '"选择分享方式。"' } },
|
|
70
|
+
AppleSnackbar: { props: { message: '"内容已保存。"', duration: '0', action: '"撤销"' } },
|
|
71
|
+
ApplePopover: { slots: { default: { scope: '{ close }', code: '<p>面板内容</p><AppleButton @click="close()">完成</AppleButton>', enabled: true } } },
|
|
72
|
+
AppleTooltip: { props: { text: '"添加到收藏"' }, slots: { default: { scope: '{ props: triggerProps }', code: '<AppleButton v-bind="triggerProps">收藏</AppleButton>', enabled: true } } },
|
|
73
|
+
AppleMenu: { props: { items: actions } },
|
|
74
|
+
AppleActionSheet: { props: { title: '"选择操作"', items: actions } },
|
|
75
|
+
ApplePullRefresh: { slots: { default: { scope: '{ refreshing }', code: '<p>{{ refreshing ? \'正在刷新…\' : \'内容已就绪\' }} · {{ batches }} 次刷新</p>', enabled: true } } },
|
|
76
|
+
AppleInfiniteScroll: { slots: { default: { code: '<p v-for="n in batches" :key="n" class="tile">已加载第 {{ n }} 批</p>', enabled: true } } },
|
|
77
|
+
AppleBackTop: { props: { target: '"#demo-scroll"', threshold: '120', fixed: 'false' }, before: scroll },
|
|
78
|
+
AppleFloatingGroup: { props: { target: '"#demo-scroll"', threshold: '120' }, before: scroll, slots: { default: { code: '<AppleButton @click="record(\'action\')">帮助</AppleButton>', enabled: true } } },
|
|
79
|
+
AppleSpeedDial: { props: { target: '"#demo-scroll"', threshold: '120' }, before: scroll + '<AppleTree label="示例目录" :items="[{ label: \'概览\', value: \'overview\' }]" /><AppleSpeedDialItem label="新增" @click="record(\'create\')" />' },
|
|
80
|
+
AppleSpeedDialItem: { props: { label: '"新增"' }, before: '<AppleSpeedDial :threshold="0" />' },
|
|
81
|
+
AppleAutoSize: { before: '<AppleLink as="button" @click="expanded = !expanded">切换内容</AppleLink>', slots: { default: { code: '<div class="tile"><p>内容</p><p v-if="expanded">更多内容<br />更多内容<br />更多内容</p></div>', enabled: true } } },
|
|
82
|
+
AppleTransition: { before: '<AppleLink as="button" @click="expanded = !expanded">切换内容</AppleLink>', slots: { default: { code: '<div :key="String(expanded)" class="tile">{{ expanded ? \'第二页\' : \'第一页\' }}</div>', enabled: true } } },
|
|
83
|
+
AppleOverlayHost: { before: '<AppleButton @click="$apple.notify(\'内容已保存\', { tone: \'success\' })">显示通知</AppleButton><AppleButton variant="secondary" @click="$apple.dialog({ title: \'宿主对话框\', message: \'由 OverlayHost 渲染\' })">打开对话框</AppleButton>' },
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function playgroundExample(name: string): PlaygroundExample { return examples[name] || {} }
|
|
87
|
+
|
|
88
|
+
export function slotExample(component: string, name: string): SlotExample {
|
|
89
|
+
const explicit = examples[component]?.slots?.[name]
|
|
90
|
+
if (explicit) return explicit
|
|
91
|
+
if (name === 'activator') return { scope: '{ props: triggerProps }', code: '<AppleButton v-bind="triggerProps">打开面板</AppleButton>' }
|
|
92
|
+
if (name === 'footer') return { scope: '{ close, confirm, cancel }', code: '<AppleButton @click="confirm()">确定</AppleButton><AppleLink as="button" @click="cancel()">取消</AppleLink>' }
|
|
93
|
+
if (name.startsWith('cell-')) return { scope: '{ row, value, index }', code: '<strong>{{ value }}</strong>' }
|
|
94
|
+
if (name.startsWith('panel-')) return { scope: '{ item }', code: '<p>{{ item?.label }}</p>' }
|
|
95
|
+
if (name.startsWith('item')) {
|
|
96
|
+
const scope = component === 'AppleTree' ? '{ item, expanded, selected }' : component === 'AppleList' ? '{ item, selected }' : component === 'AppleAccordion' ? '{ item, open }' : '{ item }'
|
|
97
|
+
return { scope, code: '<strong>{{ item.label }}</strong>' }
|
|
98
|
+
}
|
|
99
|
+
if (name === 'icon') return { code: '<Star :size="32" />' }
|
|
100
|
+
if (name === 'media') return { code: '<div class="tile">自定义媒体</div>' }
|
|
101
|
+
if (name === 'actions') return { scope: component === 'AppleNavibar' ? '{ close }' : undefined, code: '<AppleButton @click="record(\'action\')">操作</AppleButton>' }
|
|
102
|
+
if (name === 'default' && ['AppleDialog', 'AppleDrawer', 'AppleSheet'].includes(component)) return { scope: '{ close }', code: '<p>自定义正文</p><AppleButton @click="close(\'done\')">完成</AppleButton>' }
|
|
103
|
+
if (name === 'default' && component === 'AppleActionSheet') return { scope: '{ select, close }', code: '<AppleButton @click="select({ label: \'收藏\', value: \'favorite\' })">收藏</AppleButton>' }
|
|
104
|
+
return { code: content }
|
|
105
|
+
}
|
|
@@ -1,24 +1,41 @@
|
|
|
1
1
|
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
2
|
import routes from 'virtual:generated-pages'
|
|
3
3
|
import { nextTick } from 'vue'
|
|
4
|
+
import { cancelMotionScroll, scrollToWithMotion } from 'apptify'
|
|
5
|
+
import { componentDocuments } from '../editor/documents'
|
|
4
6
|
|
|
5
7
|
const router = createRouter({
|
|
6
8
|
history: createWebHistory(import.meta.env.BASE_URL),
|
|
7
9
|
routes,
|
|
8
10
|
async scrollBehavior(to, from, savedPosition) {
|
|
9
11
|
await nextTick()
|
|
12
|
+
if (to.path === '/components' && to.hash && to.path !== from.path) {
|
|
13
|
+
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()))
|
|
14
|
+
await Promise.allSettled(document.getAnimations().filter(animation =>
|
|
15
|
+
Number.isFinite(Number(animation.effect?.getComputedTiming().endTime)),
|
|
16
|
+
).map(animation => animation.finished))
|
|
17
|
+
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()))
|
|
18
|
+
}
|
|
10
19
|
if (savedPosition) return { ...savedPosition, behavior: 'instant' }
|
|
11
20
|
if (to.hash && document.getElementById(to.hash.slice(1))) {
|
|
12
21
|
const motion = document.querySelector('#app > .apple-provider')?.getAttribute('data-apple-motion')
|
|
13
|
-
return { el: to.hash, top: 96, behavior:
|
|
22
|
+
if (motion !== 'full') return { el: to.hash, top: 96, behavior: 'instant' }
|
|
23
|
+
if (router.currentRoute.value !== to) return false
|
|
24
|
+
const target = document.getElementById(to.hash.slice(1))!
|
|
25
|
+
const top = () => Math.min(target.getBoundingClientRect().top + window.scrollY - 96, Math.max(0, document.documentElement.scrollHeight - window.innerHeight))
|
|
26
|
+
await scrollToWithMotion(window, top).finished
|
|
27
|
+
return false
|
|
14
28
|
}
|
|
15
29
|
return { top: 0, behavior: 'instant' }
|
|
16
30
|
},
|
|
17
31
|
})
|
|
18
32
|
|
|
33
|
+
router.beforeEach(() => { cancelMotionScroll(window) })
|
|
34
|
+
|
|
19
35
|
router.afterEach(to => {
|
|
20
36
|
const titles: Record<string, string> = { '/': '首页', '/components': '组件', '/settings': '设置' }
|
|
21
|
-
|
|
37
|
+
const handbook = to.path.startsWith('/component-docs/') ? componentDocuments[String(to.params.component)] : undefined
|
|
38
|
+
document.title = `${handbook ? `${handbook.label} ${handbook.name}` : to.meta.title ?? titles[to.path] ?? '页面不存在'} · Apptify`
|
|
22
39
|
})
|
|
23
40
|
|
|
24
41
|
export default router
|
|
@@ -16,7 +16,7 @@ button { font: inherit; }
|
|
|
16
16
|
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--apple-text); text-decoration: none; font-weight: 650; font-size: 20px; }
|
|
17
17
|
.brand-symbol { width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; background: var(--apple-text); color: var(--apple-surface); font-weight: 600; font-size: 24px; line-height: 1; padding-bottom: 4px; font-family: Georgia,serif; }
|
|
18
18
|
.user-trigger { width: 38px; height: 38px; border: 1px solid var(--apple-border); border-radius: 50%; display: grid; place-items: center; background: var(--apple-surface); color: var(--apple-text); transition: background var(--apple-fast); }
|
|
19
|
-
.user-trigger:hover
|
|
19
|
+
.user-trigger:hover,.user-trigger[aria-expanded=true] { background: var(--apple-selected); }
|
|
20
20
|
.user-popup.apple-popover { padding: 8px; }
|
|
21
21
|
.user-summary { display: flex; align-items: center; gap: 12px; padding: 14px 10px 20px; }
|
|
22
22
|
.user-summary > div { min-width: 0; }
|
|
@@ -25,7 +25,7 @@ button { font: inherit; }
|
|
|
25
25
|
.user-summary > div > span { display: block; color: var(--apple-secondary); font-size: 11px; margin-top: 4px; overflow-wrap: anywhere; }
|
|
26
26
|
.user-menu-list { border-top: 1px solid var(--apple-border); padding: 6px 0; }
|
|
27
27
|
.user-menu-list > :is(button,a) { display: flex; align-items: center; gap: 11px; width: 100%; padding: 12px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--apple-text); font-size: 13px; text-align: left; text-decoration: none; }
|
|
28
|
-
.user-menu-list > :is(button,a):hover
|
|
28
|
+
.user-menu-list > :is(button,a):hover { background: var(--apple-hover); }
|
|
29
29
|
.user-menu-list > :is(button,a):active { background: var(--apple-pressed); }
|
|
30
30
|
.user-menu-list span { flex: 1; }
|
|
31
31
|
.user-menu-list svg,.user-menu-list small { color: var(--apple-secondary); }
|
|
@@ -91,8 +91,8 @@ button { font: inherit; }
|
|
|
91
91
|
|
|
92
92
|
.specimen > .apple-card__body > .apple-search { margin-bottom: 12px; }
|
|
93
93
|
.directory-layout { display: grid; grid-template-columns: 224px minmax(0, 1fr); gap: 32px; align-items: start; }
|
|
94
|
-
.directory-index { position: sticky; top: 100px; max-height: calc(100dvh - 120px); overflow:
|
|
95
|
-
.directory-index > span { color: var(--apple-secondary); font-size: 12px; display: block; margin: 0 0 12px 8px; }
|
|
94
|
+
.directory-index { position: sticky; top: 100px; display: flex; flex-direction: column; max-height: calc(100dvh - 120px); overflow: hidden; padding-top: 16px; font-size: 13px; min-width: 0; }
|
|
95
|
+
.directory-index > span { flex-shrink: 0; color: var(--apple-secondary); font-size: 12px; display: block; margin: 0 0 12px 8px; }
|
|
96
96
|
.directory-index .apple-tree__row { min-height: 34px; }
|
|
97
97
|
.directory-index .apple-tree__row > span:last-child { min-width: 0; overflow-wrap: anywhere; }
|
|
98
98
|
.components-page { padding: 40px 40px 0; }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<main class="main-content paper-403-page">
|
|
3
|
+
<apple-card class="paper-403-card" shadow="none" zoom="none">
|
|
4
|
+
<template #title>
|
|
5
|
+
<div class="paper-403-heading">
|
|
6
|
+
<p class="paper-403-number" aria-hidden="true">403</p>
|
|
7
|
+
<h1>无权访问</h1>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
<p class="paper-403-copy">这只鸡腿,可不能随便拿。<br />你暂时没有访问此页面的权限。</p>
|
|
11
|
+
<div class="paper-403-actions">
|
|
12
|
+
<router-link v-slot="{ href, navigate }" :to="homePath" custom>
|
|
13
|
+
<apple-button :href="href" @click="navigate">返回首页</apple-button>
|
|
14
|
+
</router-link>
|
|
15
|
+
<apple-link as="button" @click="goBack"><ArrowLeft :size="16" aria-hidden="true" /> 返回上一页</apple-link>
|
|
16
|
+
</div>
|
|
17
|
+
</apple-card>
|
|
18
|
+
<ForbiddenPaperScene class="paper-403-illustration" />
|
|
19
|
+
</main>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<route lang="json">
|
|
23
|
+
{ "alias": "/error/403", "meta": { "title": "无权访问" } }
|
|
24
|
+
</route>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { ArrowLeft } from 'lucide-vue-next'
|
|
28
|
+
import { useRouter } from 'vue-router'
|
|
29
|
+
import ForbiddenPaperScene from '../components/forbidden-paper-scene.vue'
|
|
30
|
+
|
|
31
|
+
const router = useRouter()
|
|
32
|
+
const homePath = router.getRoutes().some(page => page.path === '/') ? '/' : '/settings'
|
|
33
|
+
function goBack() {
|
|
34
|
+
if (typeof router.options.history.state.back === 'string') router.back()
|
|
35
|
+
else void router.push(homePath)
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<style scoped>
|
|
40
|
+
.paper-403-page { max-width: 1360px; display: grid; grid-template-columns: minmax(260px, .78fr) minmax(0, 1.65fr); align-content: center; align-items: center; column-gap: 16px; padding-top: 38px; padding-bottom: calc(38px + 64px); }
|
|
41
|
+
.paper-403-card { background: transparent; border: 0; overflow: visible; }
|
|
42
|
+
.paper-403-card :deep(.apple-card__body) { padding: 0 0 0 8px; }
|
|
43
|
+
.paper-403-card :deep(.apple-card__heading) { margin: 0; }
|
|
44
|
+
.paper-403-heading { width: 100%; }
|
|
45
|
+
.paper-403-number { color: var(--apple-text); font-family: Georgia, 'Times New Roman', serif; font-size: clamp(78px, 8.2vw, 118px); line-height: .9; letter-spacing: -.075em; margin: 0 0 28px; font-weight: 400; }
|
|
46
|
+
.paper-403-heading h1 { font-size: clamp(26px, 2.7vw, 35px); line-height: 1.35; letter-spacing: -.035em; font-weight: 600; margin: 0 0 17px; }
|
|
47
|
+
.paper-403-copy { font-size: 13px; line-height: 1.95; color: var(--apple-secondary); margin: 0; text-wrap: pretty; }
|
|
48
|
+
.paper-403-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 27px; }
|
|
49
|
+
.paper-403-actions .apple-link { font-size: 12px; padding-inline: 8px; color: var(--apple-secondary); }
|
|
50
|
+
.paper-403-illustration { min-width: 0; }
|
|
51
|
+
@media (max-width: 880px) {
|
|
52
|
+
.paper-403-page { grid-template-columns: minmax(0, 1fr); max-width: 680px; padding-top: 18px; }
|
|
53
|
+
.paper-403-illustration { grid-row: 1; }
|
|
54
|
+
.paper-403-card { margin-top: 26px; }
|
|
55
|
+
.paper-403-card :deep(.apple-card__body) { padding: 0; text-align: center; }
|
|
56
|
+
.paper-403-number { font-size: 70px; margin-bottom: 18px; }
|
|
57
|
+
.paper-403-heading h1 { font-size: 26px; margin-bottom: 12px; }
|
|
58
|
+
.paper-403-actions { justify-content: center; margin-top: 18px; }
|
|
59
|
+
}
|
|
60
|
+
@media (max-width: 420px) {
|
|
61
|
+
.paper-403-page { padding-inline: 18px; padding-top: 12px; }
|
|
62
|
+
.paper-403-copy { font-size: 12px; }
|
|
63
|
+
.paper-403-card { margin-top: 22px; }
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -6,28 +6,26 @@
|
|
|
6
6
|
<div class="documentation-layout directory-layout">
|
|
7
7
|
<aside ref="documentationIndex" class="documentation-index directory-index" aria-label="API 导航"><span>本页接口</span><apple-tree v-model="selectedApi" v-model:expanded="expandedSections" :items="apiTree" label="组件 API 树" @select="navigateApi" /></aside>
|
|
8
8
|
<div ref="documentationContent" class="documentation-content">
|
|
9
|
-
<section class="component-overview" aria-label="组件演示"><component :is="registration.preview" v-if="registration.preview" /></section>
|
|
10
|
-
<ComponentWorkshop v-if="doc" :doc="doc" />
|
|
9
|
+
<section class="component-overview" aria-label="组件演示"><component :is="registration.preview" v-if="registration.preview" /><ComponentPlayground v-else-if="doc" :key="doc.name" :doc="doc" /></section>
|
|
10
|
+
<ComponentWorkshop v-if="doc" :key="doc.name" :doc="doc" />
|
|
11
11
|
<apple-alert v-else-if="failed" tone="danger" title="文档加载失败"><apple-button variant="secondary" @click="loadDocument">重试</apple-button></apple-alert>
|
|
12
12
|
<apple-spinner v-else label="正在加载组件 API" />
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
</template>
|
|
16
|
-
<apple-empty v-else title="
|
|
16
|
+
<apple-empty v-else title="未找到组件文档"><router-link to="/components" custom v-slot="{ href, navigate }"><apple-link :href="href" @click="navigate">返回组件目录</apple-link></router-link></apple-empty>
|
|
17
17
|
</apple-container>
|
|
18
|
-
<DirectoryDialog v-if="registration" v-model="mobileIndex" title="本页接口">
|
|
19
|
-
<apple-tree v-model="selectedApi" v-model:expanded="expandedSections" :items="apiTree" label="组件 API 树" @select="navigateApi" />
|
|
20
|
-
</DirectoryDialog>
|
|
21
18
|
</template>
|
|
22
19
|
<script setup lang="ts">
|
|
23
|
-
|
|
20
|
+
defineOptions({ inheritAttrs: false })
|
|
21
|
+
import { computed, defineAsyncComponent, shallowRef, ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
|
24
22
|
import { useRoute, useRouter } from 'vue-router'
|
|
25
23
|
import type { AppleItem, AppleTreeItem, AppleValue } from 'apptify'
|
|
26
24
|
import { componentDocuments } from '../../editor/documents'
|
|
27
25
|
import type { ComponentDocument } from '../../editor/contract'
|
|
28
26
|
import { documentSections } from '../../editor/sections'
|
|
29
27
|
import ComponentWorkshop from '../../editor/ComponentWorkshop.vue'
|
|
30
|
-
|
|
28
|
+
const ComponentPlayground = defineAsyncComponent(() => import('../../editor/ComponentPlayground.vue'))
|
|
31
29
|
const route = useRoute()
|
|
32
30
|
const router = useRouter()
|
|
33
31
|
const breadcrumbs = computed(() => [
|
|
@@ -56,7 +54,6 @@ watch(registration, loadDocument, { immediate: true })
|
|
|
56
54
|
onBeforeUnmount(() => { loadVersion++ })
|
|
57
55
|
const documentationIndex = ref<HTMLElement>()
|
|
58
56
|
const documentationContent = ref<HTMLElement>()
|
|
59
|
-
const mobileIndex = ref(false)
|
|
60
57
|
const selectedApi = ref<AppleValue>('props')
|
|
61
58
|
const expandedSections = ref<AppleValue[]>(['props', 'events', 'slots', 'methods'])
|
|
62
59
|
const apiTree = computed<AppleTreeItem[]>(() => doc.value ? [
|
|
@@ -105,10 +102,11 @@ function queueScrollSelection() {
|
|
|
105
102
|
function revealSelection() {
|
|
106
103
|
const index = documentationIndex.value
|
|
107
104
|
const row = index?.querySelector<HTMLElement>('.apple-tree__row.is-selected')
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
const scroll = index?.querySelector<HTMLElement>('.apple-tree-scroll')
|
|
106
|
+
if (!scroll || !row) return
|
|
107
|
+
const bounds = scroll.getBoundingClientRect(), selected = row.getBoundingClientRect()
|
|
108
|
+
if (selected.top < bounds.top) scroll.scrollTop += selected.top - bounds.top
|
|
109
|
+
else if (selected.bottom > bounds.bottom) scroll.scrollTop += selected.bottom - bounds.bottom
|
|
112
110
|
}
|
|
113
111
|
watch([selectedApi, expandedSections], async () => {
|
|
114
112
|
await nextTick()
|
|
@@ -140,7 +138,6 @@ onBeforeUnmount(() => {
|
|
|
140
138
|
|
|
141
139
|
async function navigateApi(item: AppleTreeItem) {
|
|
142
140
|
if (item.children?.length) return
|
|
143
|
-
mobileIndex.value = false
|
|
144
141
|
const id = String(item.value)
|
|
145
142
|
await nextTick()
|
|
146
143
|
await router.replace({ hash: `#${id}` })
|