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.
Files changed (129) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_LICENSES.txt +23 -0
  3. package/dist/apptify.css +1 -1
  4. package/dist/apptify.js +2395 -2451
  5. package/dist/components/AppleCard.vue.d.ts +1 -1
  6. package/dist/components/content.d.ts +81 -20
  7. package/dist/components/forms.d.ts +2 -2
  8. package/dist/components/foundation.d.ts +133 -11
  9. package/dist/components/mobile-image-viewer.d.ts +35 -12
  10. package/dist/components/navibar.d.ts +2 -2
  11. package/dist/components/overlays.d.ts +10 -59
  12. package/dist/components/scrollbar.d.ts +88 -0
  13. package/dist/components/speed-dial.d.ts +157 -0
  14. package/dist/core/context.d.ts +11 -0
  15. package/dist/core/device.d.ts +2 -0
  16. package/dist/core/image-gallery-motion.d.ts +2 -1
  17. package/dist/core/image-motion.d.ts +1 -1
  18. package/dist/core/layers.d.ts +1 -0
  19. package/dist/core/motion-driver.d.ts +40 -0
  20. package/dist/core/motion.d.ts +1 -1
  21. package/dist/core/ripple.d.ts +16 -0
  22. package/dist/core/scroll-motion.d.ts +6 -0
  23. package/dist/core/scrollbar.d.ts +14 -0
  24. package/dist/core/speed-dial.d.ts +21 -0
  25. package/dist/index.d.ts +237 -26
  26. package/docs/COMPONENTS.md +10 -6
  27. package/package.json +5 -7
  28. package/templates/playground/README.md +1 -1
  29. package/templates/playground/package.json +1 -2
  30. package/templates/playground/source-snapshot.json +298 -65
  31. package/templates/playground/src/App.vue +1 -0
  32. package/templates/playground/src/ComponentDemo.vue +30 -21
  33. package/templates/playground/src/catalog.ts +70 -65
  34. package/templates/playground/src/components/ComponentIndex.vue +10 -14
  35. package/templates/playground/src/components/MasonryFeed.vue +52 -0
  36. package/templates/playground/src/components/UserMenu.vue +1 -1
  37. package/templates/playground/src/components/forbidden-paper-scene.vue +367 -0
  38. package/templates/playground/src/components/forbidden-paper-story.ts +233 -0
  39. package/templates/playground/src/editor/ComponentPlayground.vue +89 -0
  40. package/templates/playground/src/editor/ConfigurableEditor.vue +1 -1
  41. package/templates/playground/src/editor/apple-input-source.ts +1 -1
  42. package/templates/playground/src/editor/component-metadata.ts +72 -0
  43. package/templates/playground/src/editor/component-playground.ts +162 -0
  44. package/templates/playground/src/editor/contract.ts +1 -1
  45. package/templates/playground/src/editor/documents.ts +11 -2
  46. package/templates/playground/src/editor/handbook.ts +22 -0
  47. package/templates/playground/src/editor/handbooks/apple-accordion.ts +66 -0
  48. package/templates/playground/src/editor/handbooks/apple-action-sheet.ts +72 -0
  49. package/templates/playground/src/editor/handbooks/apple-alert.ts +67 -0
  50. package/templates/playground/src/editor/handbooks/apple-aside.ts +32 -0
  51. package/templates/playground/src/editor/handbooks/apple-auto-size.ts +32 -0
  52. package/templates/playground/src/editor/handbooks/apple-autocomplete.ts +114 -0
  53. package/templates/playground/src/editor/handbooks/apple-avatar-group.ts +50 -0
  54. package/templates/playground/src/editor/handbooks/apple-avatar.ts +56 -0
  55. package/templates/playground/src/editor/handbooks/apple-back-top.ts +62 -0
  56. package/templates/playground/src/editor/handbooks/apple-badge.ts +62 -0
  57. package/templates/playground/src/editor/handbooks/apple-breadcrumbs.ts +38 -0
  58. package/templates/playground/src/editor/handbooks/apple-button.ts +92 -0
  59. package/templates/playground/src/editor/handbooks/apple-card.ts +112 -0
  60. package/templates/playground/src/editor/handbooks/apple-cascader.ts +102 -0
  61. package/templates/playground/src/editor/handbooks/apple-checkbox.ts +85 -0
  62. package/templates/playground/src/editor/handbooks/apple-color-picker.ts +79 -0
  63. package/templates/playground/src/editor/handbooks/apple-container.ts +26 -0
  64. package/templates/playground/src/editor/handbooks/apple-date-picker.ts +104 -0
  65. package/templates/playground/src/editor/handbooks/apple-dialog.ts +151 -0
  66. package/templates/playground/src/editor/handbooks/apple-divider.ts +38 -0
  67. package/templates/playground/src/editor/handbooks/apple-drawer.ts +151 -0
  68. package/templates/playground/src/editor/handbooks/apple-empty.ts +43 -0
  69. package/templates/playground/src/editor/handbooks/apple-floating-group.ts +51 -0
  70. package/templates/playground/src/editor/handbooks/apple-form-field.ts +69 -0
  71. package/templates/playground/src/editor/handbooks/apple-form.ts +76 -0
  72. package/templates/playground/src/editor/handbooks/apple-grid.ts +32 -0
  73. package/templates/playground/src/editor/handbooks/apple-image.ts +122 -0
  74. package/templates/playground/src/editor/handbooks/apple-infinite-scroll.ts +73 -0
  75. package/templates/playground/src/editor/handbooks/apple-link.ts +68 -0
  76. package/templates/playground/src/editor/handbooks/apple-list.ts +67 -0
  77. package/templates/playground/src/editor/handbooks/apple-menu.ts +72 -0
  78. package/templates/playground/src/editor/handbooks/apple-navibar.ts +100 -0
  79. package/templates/playground/src/editor/handbooks/apple-otp-input.ts +108 -0
  80. package/templates/playground/src/editor/handbooks/apple-overlay-host.ts +13 -0
  81. package/templates/playground/src/editor/handbooks/apple-pagination.ts +61 -0
  82. package/templates/playground/src/editor/handbooks/apple-popover.ts +112 -0
  83. package/templates/playground/src/editor/handbooks/apple-progress.ts +56 -0
  84. package/templates/playground/src/editor/handbooks/apple-provider.ts +32 -0
  85. package/templates/playground/src/editor/handbooks/apple-pull-refresh.ts +61 -0
  86. package/templates/playground/src/editor/handbooks/apple-radio-group.ts +91 -0
  87. package/templates/playground/src/editor/handbooks/apple-rate.ts +97 -0
  88. package/templates/playground/src/editor/handbooks/apple-scroll-bar.ts +15 -0
  89. package/templates/playground/src/editor/handbooks/apple-search.ts +49 -0
  90. package/templates/playground/src/editor/handbooks/apple-segmented-control.ts +85 -0
  91. package/templates/playground/src/editor/handbooks/apple-select.ts +91 -0
  92. package/templates/playground/src/editor/handbooks/apple-sheet.ts +151 -0
  93. package/templates/playground/src/editor/handbooks/apple-skeleton.ts +74 -0
  94. package/templates/playground/src/editor/handbooks/apple-slider.ts +103 -0
  95. package/templates/playground/src/editor/handbooks/apple-snackbar.ts +89 -0
  96. package/templates/playground/src/editor/handbooks/apple-speed-dial-item.ts +16 -0
  97. package/templates/playground/src/editor/handbooks/apple-speed-dial.ts +13 -0
  98. package/templates/playground/src/editor/handbooks/apple-spinner.ts +38 -0
  99. package/templates/playground/src/editor/handbooks/apple-stack.ts +44 -0
  100. package/templates/playground/src/editor/handbooks/apple-statistic.ts +69 -0
  101. package/templates/playground/src/editor/handbooks/apple-stepper.ts +91 -0
  102. package/templates/playground/src/editor/handbooks/apple-steps.ts +61 -0
  103. package/templates/playground/src/editor/handbooks/apple-switch.ts +79 -0
  104. package/templates/playground/src/editor/handbooks/apple-tab-bar.ts +66 -0
  105. package/templates/playground/src/editor/handbooks/apple-table.ts +176 -0
  106. package/templates/playground/src/editor/handbooks/apple-tabs.ts +72 -0
  107. package/templates/playground/src/editor/handbooks/apple-tag.ts +56 -0
  108. package/templates/playground/src/editor/handbooks/apple-textarea.ts +109 -0
  109. package/templates/playground/src/editor/handbooks/apple-timeline.ts +44 -0
  110. package/templates/playground/src/editor/handbooks/apple-tooltip.ts +45 -0
  111. package/templates/playground/src/editor/handbooks/apple-transition.ts +44 -0
  112. package/templates/playground/src/editor/handbooks/apple-tree.ts +84 -0
  113. package/templates/playground/src/editor/handbooks/apple-upload.ts +126 -0
  114. package/templates/playground/src/editor/playground-examples.ts +105 -0
  115. package/templates/playground/src/images.ts +3 -0
  116. package/templates/playground/src/router/index.ts +19 -2
  117. package/templates/playground/src/style.css +4 -4
  118. package/templates/playground/src/views/403.vue +65 -0
  119. package/templates/playground/src/views/component-docs/[component].vue +11 -14
  120. package/templates/playground/src/views/components.vue +11 -41
  121. package/templates/playground/src/views/index.vue +3 -1
  122. package/templates/playground/src/views/settings.vue +5 -2
  123. package/tooling/create-playground.mjs +0 -0
  124. package/tooling/prepare-playground.mjs +3 -3
  125. package/tooling/test-playground-package.mjs +10 -0
  126. package/dist/components/gallery-scrollbar.d.ts +0 -46
  127. package/dist/core/desktop-image-enter.d.ts +0 -6
  128. package/dist/core/desktop-image-return.d.ts +0 -1
  129. 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
+ }
@@ -0,0 +1,3 @@
1
+ export function imageUrl(filename: string) {
2
+ return `${import.meta.env.BASE_URL}images/${filename}`
3
+ }
@@ -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: to.path === from.path && motion === 'full' ? 'smooth' : 'instant' }
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
- document.title = `${titles[to.path] ?? '页面不存在'} · Apptify`
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:where(:not([data-apple-touch] *)),.user-trigger[aria-expanded=true] { background: var(--apple-selected); }
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:where(:not([data-apple-touch] *)) { background: var(--apple-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: auto; overscroll-behavior: contain; scrollbar-width: thin; padding-top: 16px; font-size: 13px; min-width: 0; }
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="组件文档尚未开放" description="当前阶段仅开放 AppleInput 样板。"><router-link to="/components" custom v-slot="{ href, navigate }"><apple-link :href="href" @click="navigate">返回组件目录</apple-link></router-link></apple-empty>
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
- import { computed, shallowRef, ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
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
- import DirectoryDialog from '../../components/DirectoryDialog.vue'
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
- if (!index || !row) return
109
- const bounds = index.getBoundingClientRect(), selected = row.getBoundingClientRect()
110
- if (selected.top < bounds.top) index.scrollTop += selected.top - bounds.top
111
- else if (selected.bottom > bounds.bottom) index.scrollTop += selected.bottom - bounds.bottom
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}` })