@woisol-g/configurable-cross-menu 1.0.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/ENGINEERING_REVIEW.md +414 -0
- package/LICENSE +15 -0
- package/README.en.md +87 -0
- package/README.md +279 -0
- package/dist/config.d.ts +82 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/configurable-cross-menu.css +3 -0
- package/dist/configurable-cross-menu.css.map +1 -0
- package/dist/configurable-cross-menu.js +2 -0
- package/dist/configurable-cross-menu.js.map +1 -0
- package/dist/crossMenu.d.ts +65 -0
- package/dist/crossMenu.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/utils/utils.d.ts +2 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# 工程评审文档
|
|
2
|
+
|
|
3
|
+
这份文档的目标不是“挑刺”,而是帮助你把一个已经能工作的个人项目,逐步推进到更稳定、更容易发布、更适合长期维护的公开组件。
|
|
4
|
+
|
|
5
|
+
我会把问题分成四个层级:
|
|
6
|
+
|
|
7
|
+
- `High`:已经明显影响发布质量、稳定性或公共 API 可信度
|
|
8
|
+
- `Medium`:不一定立刻炸,但会拖慢后续迭代
|
|
9
|
+
- `Low`:可读性、整洁度、命名与习惯问题
|
|
10
|
+
- `Ideas`:当前不是问题,但值得进入下一轮演进计划
|
|
11
|
+
|
|
12
|
+
这轮我遵守了“不修改现有 `src` 逻辑”的边界,所以这里的内容主要用于你后续学习和改进。
|
|
13
|
+
|
|
14
|
+
## High
|
|
15
|
+
|
|
16
|
+
### 1. 生命周期清理不完整
|
|
17
|
+
|
|
18
|
+
位置:
|
|
19
|
+
|
|
20
|
+
- `src/crossMenu.ts`
|
|
21
|
+
|
|
22
|
+
现象:
|
|
23
|
+
|
|
24
|
+
- `registerKeyboardEvents()` 会往 `document` 注册 `keydown`
|
|
25
|
+
- `registerParallaxEffect()` 会往 `document.body` 和 `document` 注册事件
|
|
26
|
+
- `destroy()` 目前只清空容器和定时器,没有对应移除这些监听器
|
|
27
|
+
|
|
28
|
+
影响:
|
|
29
|
+
|
|
30
|
+
- 多次 `render()`、多实例并存、Storybook 切换 story、测试反复挂载时,都可能出现事件叠加
|
|
31
|
+
- 用户会看到按一次键触发多次响应
|
|
32
|
+
- 调试时很容易误以为“键盘逻辑坏了”,实际上是监听器泄漏
|
|
33
|
+
|
|
34
|
+
建议方向:
|
|
35
|
+
|
|
36
|
+
- 给每个监听器保存稳定引用
|
|
37
|
+
- `destroy()` 中统一移除
|
|
38
|
+
- 更现代一点的写法可以考虑 `AbortController`
|
|
39
|
+
|
|
40
|
+
为什么它是高优先级:
|
|
41
|
+
|
|
42
|
+
- 这是典型的“Demo 能跑,组件化就出问题”的边界
|
|
43
|
+
- 一旦公开发布,生命周期问题往往最先被用户撞到
|
|
44
|
+
|
|
45
|
+
### 2. `renderMenuItems()` 会修改调用方传入的 `items`
|
|
46
|
+
|
|
47
|
+
位置:
|
|
48
|
+
|
|
49
|
+
- `src/crossMenu.ts`
|
|
50
|
+
|
|
51
|
+
现象:
|
|
52
|
+
|
|
53
|
+
- 代码会把 `item.action` 从函数改写成字符串形式的 `__ccm_dispatch_func(...)`
|
|
54
|
+
|
|
55
|
+
影响:
|
|
56
|
+
|
|
57
|
+
- 调用方传入的数据被污染
|
|
58
|
+
- 如果外部代码复用同一个 `items` 数组,第二次使用时行为会变得不直观
|
|
59
|
+
- 这会让组件带上“隐式副作用”,后续很难排查
|
|
60
|
+
|
|
61
|
+
建议方向:
|
|
62
|
+
|
|
63
|
+
- 渲染前先做内部映射,不要直接改原对象
|
|
64
|
+
- 可以把渲染数据和业务数据分成两个类型
|
|
65
|
+
|
|
66
|
+
为什么它重要:
|
|
67
|
+
|
|
68
|
+
- 这是一个公共组件,不应该悄悄改用户传进来的数据
|
|
69
|
+
|
|
70
|
+
### 3. 使用全局 `window.__ccm_dispatch_func` 存在实例冲突风险
|
|
71
|
+
|
|
72
|
+
位置:
|
|
73
|
+
|
|
74
|
+
- `src/crossMenu.ts`
|
|
75
|
+
|
|
76
|
+
现象:
|
|
77
|
+
|
|
78
|
+
- 每次渲染都会把动作函数挂到全局
|
|
79
|
+
- 多个实例会共享同一个全局入口名
|
|
80
|
+
|
|
81
|
+
影响:
|
|
82
|
+
|
|
83
|
+
- 实例之间可能互相覆盖
|
|
84
|
+
- 多页面片段并存时不安全
|
|
85
|
+
- 这类全局约定很难维护,也不利于封装
|
|
86
|
+
|
|
87
|
+
建议方向:
|
|
88
|
+
|
|
89
|
+
- 尽量避免把函数分发交给全局字符串 `onclick`
|
|
90
|
+
- 更稳的做法是创建元素后直接绑定事件监听
|
|
91
|
+
|
|
92
|
+
为什么它重要:
|
|
93
|
+
|
|
94
|
+
- 一旦组件被当成“库”使用,全局命名冲突会比单页 Demo 更快暴露
|
|
95
|
+
|
|
96
|
+
### 4. 动态注入到 `<head>` 的样式没有回收机制
|
|
97
|
+
|
|
98
|
+
位置:
|
|
99
|
+
|
|
100
|
+
- `src/crossMenu.ts`
|
|
101
|
+
|
|
102
|
+
现象:
|
|
103
|
+
|
|
104
|
+
- `updateCSS()` 每次初始化会新建一个 `<style>`
|
|
105
|
+
- 没有记录引用,也没有在 `destroy()` 清理
|
|
106
|
+
|
|
107
|
+
影响:
|
|
108
|
+
|
|
109
|
+
- 多次创建实例时可能不断累积样式节点
|
|
110
|
+
- 后续改主题或局部更新时会出现“旧样式仍在影响页面”的问题
|
|
111
|
+
|
|
112
|
+
建议方向:
|
|
113
|
+
|
|
114
|
+
- 保存样式节点引用
|
|
115
|
+
- 重渲染时复用或更新它
|
|
116
|
+
- 销毁时移除
|
|
117
|
+
|
|
118
|
+
### 5. 可访问性与语义结构存在问题
|
|
119
|
+
|
|
120
|
+
位置:
|
|
121
|
+
|
|
122
|
+
- `src/templates/menuItem.pug`
|
|
123
|
+
- `src/crossMenu.ts`
|
|
124
|
+
|
|
125
|
+
现象:
|
|
126
|
+
|
|
127
|
+
- 当前模板是 `button` 内再包 `a`
|
|
128
|
+
- 这是交互元素嵌套交互元素
|
|
129
|
+
|
|
130
|
+
影响:
|
|
131
|
+
|
|
132
|
+
- HTML 语义不理想
|
|
133
|
+
- 键盘焦点、屏幕阅读器、点击行为都可能出现不一致
|
|
134
|
+
- 某些浏览器环境下的默认行为可能不稳定
|
|
135
|
+
|
|
136
|
+
建议方向:
|
|
137
|
+
|
|
138
|
+
- 链接型项直接输出 `<a>`
|
|
139
|
+
- 动作型项输出 `<button>`
|
|
140
|
+
- 两种模式分别渲染,而不是嵌套
|
|
141
|
+
|
|
142
|
+
为什么它重要:
|
|
143
|
+
|
|
144
|
+
- 一旦对外公开,这是“组件质量感”的重要部分
|
|
145
|
+
|
|
146
|
+
### 6. 配置类型里有 `keyBindings`,但实际逻辑仍然硬编码
|
|
147
|
+
|
|
148
|
+
位置:
|
|
149
|
+
|
|
150
|
+
- `src/config.ts`
|
|
151
|
+
- `src/crossMenu.ts`
|
|
152
|
+
|
|
153
|
+
现象:
|
|
154
|
+
|
|
155
|
+
- `CCMConfig` 已定义 `keyBindings`
|
|
156
|
+
- `CCMConfigBuilder` 也会填默认值
|
|
157
|
+
- 但 `registerKeyboardEvents()` 里仍直接写死 `w/a/s/d` 与方向键
|
|
158
|
+
|
|
159
|
+
影响:
|
|
160
|
+
|
|
161
|
+
- 类型承诺和实际行为不一致
|
|
162
|
+
- README 很难写得完全诚实
|
|
163
|
+
- 用户会以为自己可以配置键位,结果并不能
|
|
164
|
+
|
|
165
|
+
建议方向:
|
|
166
|
+
|
|
167
|
+
- 统一由 `config.keyBindings` 驱动
|
|
168
|
+
- 类型与实现保持一致
|
|
169
|
+
|
|
170
|
+
## Medium
|
|
171
|
+
|
|
172
|
+
### 1. `startingDirections` 已存在,但当前对外意义不清晰
|
|
173
|
+
|
|
174
|
+
位置:
|
|
175
|
+
|
|
176
|
+
- `src/config.ts`
|
|
177
|
+
|
|
178
|
+
现象:
|
|
179
|
+
|
|
180
|
+
- 配置中定义了 `startingDirections`
|
|
181
|
+
- 但渲染和交互逻辑中看不出它的真实作用
|
|
182
|
+
|
|
183
|
+
影响:
|
|
184
|
+
|
|
185
|
+
- API 会显得“半完成”
|
|
186
|
+
- 用户看文档会困惑
|
|
187
|
+
|
|
188
|
+
建议方向:
|
|
189
|
+
|
|
190
|
+
- 要么明确实现它
|
|
191
|
+
- 要么在下一版中删除或标记为保留字段
|
|
192
|
+
|
|
193
|
+
### 2. `style.width` 的语义不够明确
|
|
194
|
+
|
|
195
|
+
位置:
|
|
196
|
+
|
|
197
|
+
- `src/config.ts`
|
|
198
|
+
- `src/crossMenu.ts`
|
|
199
|
+
|
|
200
|
+
现象:
|
|
201
|
+
|
|
202
|
+
- 变量名像是整体宽度
|
|
203
|
+
- 但从实际样式与布局来看,含义没有完全讲清楚
|
|
204
|
+
|
|
205
|
+
影响:
|
|
206
|
+
|
|
207
|
+
- 配置项可理解性不足
|
|
208
|
+
- 后续扩展时容易越改越绕
|
|
209
|
+
|
|
210
|
+
建议方向:
|
|
211
|
+
|
|
212
|
+
- 给它更清晰的名字
|
|
213
|
+
- 或者在 README 中明确它到底控制什么
|
|
214
|
+
|
|
215
|
+
### 3. 存在已经废弃或实验性的文件
|
|
216
|
+
|
|
217
|
+
位置:
|
|
218
|
+
|
|
219
|
+
- `src/pug.d.ts.not`
|
|
220
|
+
- `src/styles/menu.scss`
|
|
221
|
+
- `project/reference/*`
|
|
222
|
+
|
|
223
|
+
现象:
|
|
224
|
+
|
|
225
|
+
- 这些文件看起来像学习记录、历史草稿或未接入正式产物的实验资产
|
|
226
|
+
|
|
227
|
+
影响:
|
|
228
|
+
|
|
229
|
+
- 新读者会分不清“正式代码”和“参考代码”
|
|
230
|
+
- 发布前也更容易把不该发的文件打包进去
|
|
231
|
+
|
|
232
|
+
建议方向:
|
|
233
|
+
|
|
234
|
+
- 把学习资料、参考代码和正式源码明确分层
|
|
235
|
+
- 非正式文件移到 `docs/`、`notes/` 或独立分支
|
|
236
|
+
|
|
237
|
+
### 4. `tsconfig.json` 里有陈旧痕迹
|
|
238
|
+
|
|
239
|
+
位置:
|
|
240
|
+
|
|
241
|
+
- `tsconfig.json`
|
|
242
|
+
|
|
243
|
+
现象:
|
|
244
|
+
|
|
245
|
+
- `include` 中存在 `src/CrossMenu.ts.bak`
|
|
246
|
+
- 注释中还有明显的临时实验痕迹和乱码
|
|
247
|
+
|
|
248
|
+
影响:
|
|
249
|
+
|
|
250
|
+
- 会给后续维护者传达“不确定哪些是正式配置”的感觉
|
|
251
|
+
|
|
252
|
+
建议方向:
|
|
253
|
+
|
|
254
|
+
- 精简成只保留当前项目真的需要的编译选项
|
|
255
|
+
- 把学习过程中的注释另存到笔记文档
|
|
256
|
+
|
|
257
|
+
### 5. 目前以 UMD 为主,对现代打包生态还不够友好
|
|
258
|
+
|
|
259
|
+
位置:
|
|
260
|
+
|
|
261
|
+
- `webpack.config.js`
|
|
262
|
+
- `package.json`
|
|
263
|
+
|
|
264
|
+
现象:
|
|
265
|
+
|
|
266
|
+
- 当前核心发布产物是 UMD
|
|
267
|
+
|
|
268
|
+
影响:
|
|
269
|
+
|
|
270
|
+
- 浏览器直引很方便
|
|
271
|
+
- 但对现代包生态来说,后续仍建议补 ESM / CJS 更清晰的双入口
|
|
272
|
+
|
|
273
|
+
建议方向:
|
|
274
|
+
|
|
275
|
+
- 下一轮可以把“浏览器直引友好”和“npm 包生态友好”分开设计
|
|
276
|
+
|
|
277
|
+
### 6. CSS 特性兼容性较强依赖现代浏览器
|
|
278
|
+
|
|
279
|
+
位置:
|
|
280
|
+
|
|
281
|
+
- `src/styles/ccm-center.scss`
|
|
282
|
+
- `src/styles/ccm-items.scss`
|
|
283
|
+
|
|
284
|
+
现象:
|
|
285
|
+
|
|
286
|
+
- 用到了 `:has`
|
|
287
|
+
- 用到了 `color-mix`
|
|
288
|
+
- 用到了 `backdrop-filter`
|
|
289
|
+
- 用到了较强的 `writing-mode`
|
|
290
|
+
|
|
291
|
+
影响:
|
|
292
|
+
|
|
293
|
+
- 视觉效果很好
|
|
294
|
+
- 但如果未来希望面向更广泛环境,就需要明确浏览器支持范围
|
|
295
|
+
|
|
296
|
+
建议方向:
|
|
297
|
+
|
|
298
|
+
- 在 README 中写清最低浏览器预期
|
|
299
|
+
- 对关键特性准备降级策略
|
|
300
|
+
|
|
301
|
+
## Low
|
|
302
|
+
|
|
303
|
+
### 1. 命名已经在变好,但仍有历史痕迹
|
|
304
|
+
|
|
305
|
+
现象:
|
|
306
|
+
|
|
307
|
+
- 现在产物名和全局名都已经统一到 `Configurable`
|
|
308
|
+
- 但仓库目录名还是 `ConfigableCrossMenu`
|
|
309
|
+
|
|
310
|
+
建议方向:
|
|
311
|
+
|
|
312
|
+
- 下一轮统一仓库目录、包名、文档标题和示例命名
|
|
313
|
+
|
|
314
|
+
### 2. 代码中保留了很多“边写边想”的注释
|
|
315
|
+
|
|
316
|
+
现象:
|
|
317
|
+
|
|
318
|
+
- 有些注释很真实,也能看出你的思考过程
|
|
319
|
+
- 但一部分属于草稿式思维,不适合长期留在公共源码中
|
|
320
|
+
|
|
321
|
+
建议方向:
|
|
322
|
+
|
|
323
|
+
- 把“原理说明”保留
|
|
324
|
+
- 把“调试碎碎念”移动到笔记
|
|
325
|
+
|
|
326
|
+
### 3. 存在编码乱码注释
|
|
327
|
+
|
|
328
|
+
现象:
|
|
329
|
+
|
|
330
|
+
- 部分中文注释在当前环境中显示乱码
|
|
331
|
+
|
|
332
|
+
影响:
|
|
333
|
+
|
|
334
|
+
- 阅读体验下降
|
|
335
|
+
- 在开源场景里会显得不够干净
|
|
336
|
+
|
|
337
|
+
建议方向:
|
|
338
|
+
|
|
339
|
+
- 下一轮统一文件编码为 UTF-8
|
|
340
|
+
|
|
341
|
+
### 4. 有些写法还能更现代一点
|
|
342
|
+
|
|
343
|
+
例子:
|
|
344
|
+
|
|
345
|
+
- `substr()` 可以逐步换成 `slice()`
|
|
346
|
+
- 某些 `querySelector` 和类型断言可以收口成更稳的辅助函数
|
|
347
|
+
- 动画和样式变量构建可以进一步模块化
|
|
348
|
+
|
|
349
|
+
这类问题不急,但很适合当作练手重构题。
|
|
350
|
+
|
|
351
|
+
## Ideas
|
|
352
|
+
|
|
353
|
+
### 1. 做一个真正的“发布级 API”
|
|
354
|
+
|
|
355
|
+
你现在已经有了很好看的交互原型。下一步最值得投入的,不是继续堆动画,而是把 API 打磨得更清晰:
|
|
356
|
+
|
|
357
|
+
- 哪些字段是稳定承诺
|
|
358
|
+
- 哪些字段是实验能力
|
|
359
|
+
- 哪些字段以后可能删除
|
|
360
|
+
|
|
361
|
+
一旦这三类边界清楚了,README、测试和版本管理都会轻松很多。
|
|
362
|
+
|
|
363
|
+
### 2. 拆分“渲染层”和“交互层”
|
|
364
|
+
|
|
365
|
+
当前 `CCM` 类里同时负责:
|
|
366
|
+
|
|
367
|
+
- 配置合并
|
|
368
|
+
- HTML 渲染
|
|
369
|
+
- CSS 变量注入
|
|
370
|
+
- 键盘交互
|
|
371
|
+
- 视差交互
|
|
372
|
+
- 销毁逻辑
|
|
373
|
+
|
|
374
|
+
这对原型开发很高效,但对长期维护来说有点重。
|
|
375
|
+
|
|
376
|
+
下一轮可以考虑拆成:
|
|
377
|
+
|
|
378
|
+
- config 层
|
|
379
|
+
- view/render 层
|
|
380
|
+
- interaction 层
|
|
381
|
+
- lifecycle 层
|
|
382
|
+
|
|
383
|
+
### 3. 为“无动画 / 低动画”场景做更明确的产品策略
|
|
384
|
+
|
|
385
|
+
现在项目已经有 `prefers-reduced-motion` 的意识,这很好。
|
|
386
|
+
|
|
387
|
+
下一轮可以再往前一步:
|
|
388
|
+
|
|
389
|
+
- 明确哪些动画是核心体验
|
|
390
|
+
- 哪些动画可以完全降级
|
|
391
|
+
- 哪些效果在移动端要自动弱化
|
|
392
|
+
|
|
393
|
+
### 4. 补一个正式 demo 站点
|
|
394
|
+
|
|
395
|
+
现在 `demo.html` 很有帮助,但它更像开发演示页。
|
|
396
|
+
|
|
397
|
+
如果你以后继续维护这个项目,可以考虑:
|
|
398
|
+
|
|
399
|
+
- 用 Storybook 作为正式示例站
|
|
400
|
+
- 或单独做一个 GitHub Pages demo
|
|
401
|
+
|
|
402
|
+
这样用户会更容易理解这个组件的价值。
|
|
403
|
+
|
|
404
|
+
## 总结
|
|
405
|
+
|
|
406
|
+
这个项目已经过了“想法阶段”,已经是一个真实可用的交互组件原型了。
|
|
407
|
+
|
|
408
|
+
你当前最需要的不是一次性把所有代码都重写,而是分三步推进:
|
|
409
|
+
|
|
410
|
+
1. 先把文档、测试、发布链路和项目边界搭好
|
|
411
|
+
2. 再修生命周期、副作用、语义结构这些高优先级问题
|
|
412
|
+
3. 最后再做 API 打磨和结构重构
|
|
413
|
+
|
|
414
|
+
这个顺序很重要,因为它能让你的优化过程从“凭感觉改代码”变成“有证据、有边界地演进工程”。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Woisol
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Configurable Cross Menu
|
|
2
|
+
|
|
3
|
+
An animated and highly configurable cross menu component that can be used directly in plain HTML pages.
|
|
4
|
+
|
|
5
|
+
This repository currently focuses on four things:
|
|
6
|
+
|
|
7
|
+
- keeping the existing source behavior intact
|
|
8
|
+
- adding release-ready documentation
|
|
9
|
+
- adding Storybook and Vitest for visibility and verification
|
|
10
|
+
- preparing GitHub Actions for npm publishing and jsDelivr consumption
|
|
11
|
+
|
|
12
|
+
For the main Chinese documentation, see [README.md](./README.md).
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @woisol-g/configurable-cross-menu
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Bundler usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { CCM } from '@woisol-g/configurable-cross-menu';
|
|
24
|
+
import '@woisol-g/configurable-cross-menu/styles.css';
|
|
25
|
+
|
|
26
|
+
const ccm = new CCM(
|
|
27
|
+
{
|
|
28
|
+
container: '#ccm-con',
|
|
29
|
+
style: {
|
|
30
|
+
center: {
|
|
31
|
+
title: { content: 'C C M' },
|
|
32
|
+
subtitle: { content: 'Configurable Cross Menu' },
|
|
33
|
+
style: { direction: 'column' },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
true,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
ccm.render([
|
|
41
|
+
{ direction: 'up', label: 'Home', url: '#home' },
|
|
42
|
+
{ direction: 'right', label: 'About', url: '#about' },
|
|
43
|
+
{ direction: 'down', label: 'Contact', url: '#contact' },
|
|
44
|
+
{ direction: 'left', label: 'Settings', action: () => console.log('Settings') },
|
|
45
|
+
]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Browser usage
|
|
49
|
+
|
|
50
|
+
After publishing, you can use the package directly from jsDelivr:
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<link
|
|
54
|
+
rel="stylesheet"
|
|
55
|
+
href="https://cdn.jsdelivr.net/npm/@woisol-g/configurable-cross-menu@latest/dist/configurable-cross-menu.css"
|
|
56
|
+
/>
|
|
57
|
+
<script src="https://cdn.jsdelivr.net/npm/@woisol-g/configurable-cross-menu@latest/dist/configurable-cross-menu.js"></script>
|
|
58
|
+
<script>
|
|
59
|
+
const { CCM } = globalThis.ConfigurableCrossMenu || {};
|
|
60
|
+
</script>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## What is included
|
|
64
|
+
|
|
65
|
+
- `README.md`: primary Chinese documentation
|
|
66
|
+
- `README.en.md`: English companion document
|
|
67
|
+
- `ENGINEERING_REVIEW.md`: detailed improvement notes and learning-oriented review
|
|
68
|
+
- `docs/publish-checklist.md`: first-release checklist for npm publishing
|
|
69
|
+
|
|
70
|
+
## Development commands
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm install
|
|
74
|
+
pnpm typecheck
|
|
75
|
+
pnpm build
|
|
76
|
+
pnpm test
|
|
77
|
+
pnpm storybook
|
|
78
|
+
pnpm build-storybook
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Notes
|
|
82
|
+
|
|
83
|
+
- The current package is centered around the existing UMD build.
|
|
84
|
+
- Source code in `src` was intentionally left unchanged in this round.
|
|
85
|
+
- Improvement ideas are documented instead of applied directly to the implementation.
|
|
86
|
+
|
|
87
|
+
For a deeper review of the current implementation, see [ENGINEERING_REVIEW.md](./ENGINEERING_REVIEW.md).
|
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Configurable Cross Menu
|
|
2
|
+
|
|
3
|
+
一个高可配置、动画表现优雅、可以直接在 HTML 中引入并使用的十字菜单组件。
|
|
4
|
+
|
|
5
|
+
当前项目已经具备以下特点:
|
|
6
|
+
|
|
7
|
+
- 直接在浏览器中引入 UMD 产物即可使用
|
|
8
|
+
- 支持 TypeScript 配置
|
|
9
|
+
- 支持中心区域和四个方向菜单项的高自由度配置
|
|
10
|
+
- 自带较明显的动态动画与视差效果
|
|
11
|
+
- 支持链接型菜单项和动作型菜单项
|
|
12
|
+
- 支持基础键盘操作
|
|
13
|
+
|
|
14
|
+
如果你想快速了解项目状态、设计选择和后续优化建议,可以先看:
|
|
15
|
+
|
|
16
|
+
- 工程评审文档:[ENGINEERING_REVIEW.md](./ENGINEERING_REVIEW.md)
|
|
17
|
+
- 首次发布清单:[docs/publish-checklist.md](./docs/publish-checklist.md)
|
|
18
|
+
- 英文副文档:[README.en.md](./README.en.md)
|
|
19
|
+
|
|
20
|
+
## 安装
|
|
21
|
+
|
|
22
|
+
推荐的 npm 包名是:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add @woisol-g/configurable-cross-menu
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
如果你使用 npm:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @woisol-g/configurable-cross-menu
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 快速开始
|
|
35
|
+
|
|
36
|
+
### 1. 在构建工具项目中使用
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { CCM } from '@woisol-g/configurable-cross-menu';
|
|
40
|
+
import '@woisol-g/configurable-cross-menu/styles.css';
|
|
41
|
+
|
|
42
|
+
const ccm = new CCM(
|
|
43
|
+
{
|
|
44
|
+
container: '#ccm-con',
|
|
45
|
+
style: {
|
|
46
|
+
center: {
|
|
47
|
+
title: { content: 'C C M' },
|
|
48
|
+
subtitle: { content: 'Configurable Cross Menu' },
|
|
49
|
+
style: { direction: 'column' },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
true,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
ccm.render([
|
|
57
|
+
{ direction: 'up', label: 'Home', url: '#home' },
|
|
58
|
+
{ direction: 'right', label: 'About', url: '#about' },
|
|
59
|
+
{ direction: 'right', label: 'Profile', action: () => console.log('Profile') },
|
|
60
|
+
{ direction: 'down', label: 'Contact', url: '#contact' },
|
|
61
|
+
{ direction: 'left', label: 'Settings', action: () => console.log('Settings') },
|
|
62
|
+
]);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<div id="ccm-con"></div>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. 在浏览器中直接引入
|
|
70
|
+
|
|
71
|
+
发布后可以直接使用 jsDelivr:
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!DOCTYPE html>
|
|
75
|
+
<html lang="zh-CN">
|
|
76
|
+
<head>
|
|
77
|
+
<meta charset="UTF-8" />
|
|
78
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
79
|
+
<title>CCM Demo</title>
|
|
80
|
+
<link
|
|
81
|
+
rel="stylesheet"
|
|
82
|
+
href="https://cdn.jsdelivr.net/npm/@woisol-g/configurable-cross-menu@latest/dist/configurable-cross-menu.css"
|
|
83
|
+
/>
|
|
84
|
+
</head>
|
|
85
|
+
<body>
|
|
86
|
+
<div id="ccm-con"></div>
|
|
87
|
+
|
|
88
|
+
<script src="https://cdn.jsdelivr.net/npm/@woisol-g/configurable-cross-menu@latest/dist/configurable-cross-menu.js"></script>
|
|
89
|
+
<script>
|
|
90
|
+
const { CCM } = globalThis.ConfigurableCrossMenu || {};
|
|
91
|
+
|
|
92
|
+
if (!CCM) {
|
|
93
|
+
throw new Error('ConfigurableCrossMenu failed to load.');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const ccm = new CCM(
|
|
97
|
+
{
|
|
98
|
+
container: '#ccm-con',
|
|
99
|
+
style: {
|
|
100
|
+
center: {
|
|
101
|
+
title: { content: 'C C M' },
|
|
102
|
+
subtitle: { content: 'Configurable Cross Menu' },
|
|
103
|
+
style: { direction: 'column' },
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
true,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
ccm.render([
|
|
111
|
+
{ direction: 'up', label: 'Home', url: '#home' },
|
|
112
|
+
{ direction: 'right', label: 'About', url: '#about' },
|
|
113
|
+
{ direction: 'down', label: 'Contact', url: '#contact' },
|
|
114
|
+
{ direction: 'left', label: 'Settings', action: () => console.log('Settings') },
|
|
115
|
+
]);
|
|
116
|
+
</script>
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 公开 API
|
|
122
|
+
|
|
123
|
+
### `new CCM(config, useDefaultKeyBindings?)`
|
|
124
|
+
|
|
125
|
+
- `config`: `Partial<CCMConfig>`
|
|
126
|
+
- `useDefaultKeyBindings`: 是否启用默认键位映射,当前构造函数默认是 `true`
|
|
127
|
+
|
|
128
|
+
### `ccm.render(items?, container?, config?)`
|
|
129
|
+
|
|
130
|
+
- `items`: 菜单项数组
|
|
131
|
+
- `container`: 可选,新的容器选择器
|
|
132
|
+
- `config`: 可选,本次渲染要叠加的配置
|
|
133
|
+
|
|
134
|
+
### `ccm.destroy()`
|
|
135
|
+
|
|
136
|
+
- 清空当前容器中的菜单内容
|
|
137
|
+
- 清理当前实例维护的部分状态
|
|
138
|
+
|
|
139
|
+
## 配置概览
|
|
140
|
+
|
|
141
|
+
下面是当前版本最常用的配置字段说明。更完整的默认值可以参考 [`src/config.ts`](./src/config.ts)。
|
|
142
|
+
|
|
143
|
+
| 字段 | 说明 |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| `container` | 菜单挂载容器选择器,默认是 `#ccm-con` |
|
|
146
|
+
| `startingDirections` | 当前版本中已存在于配置类型中,但暂未形成完整对外行为 |
|
|
147
|
+
| `style.width` | 菜单整体宽度相关变量 |
|
|
148
|
+
| `style.background.menuColor` | 菜单背景色,支持字符串或 `{ light, dark }` |
|
|
149
|
+
| `style.background.centerColor` | 中心区域背景色 |
|
|
150
|
+
| `style.background.opacity` | 背景透明度 |
|
|
151
|
+
| `style.background.blur` | 背景模糊强度 |
|
|
152
|
+
| `style.center` | 中心区域配置,支持默认内容或自定义渲染 |
|
|
153
|
+
| `style.menu.length` | 菜单项伸展长度 |
|
|
154
|
+
| `style.menu.color` | 菜单项前景色 |
|
|
155
|
+
| `style.menu.radius` | 菜单项圆角 |
|
|
156
|
+
| `style.showAnimation.center.duration` | 中心区域展示动画时长 |
|
|
157
|
+
| `style.showAnimation.menu.durationPerItem` | 菜单项分批出现的间隔时长 |
|
|
158
|
+
| `keyBindings` | 类型中已预留,当前实现仍以内置键位逻辑为主 |
|
|
159
|
+
|
|
160
|
+
## 菜单项格式
|
|
161
|
+
|
|
162
|
+
当前菜单项支持两种模式:
|
|
163
|
+
|
|
164
|
+
### 链接型
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
{
|
|
168
|
+
direction: 'up',
|
|
169
|
+
label: 'Home',
|
|
170
|
+
url: '#home',
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 动作型
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
{
|
|
178
|
+
direction: 'left',
|
|
179
|
+
label: 'Settings',
|
|
180
|
+
action: () => {
|
|
181
|
+
console.log('Open settings');
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 菜单项字段说明
|
|
187
|
+
|
|
188
|
+
| 字段 | 说明 |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `direction` | `up` / `right` / `down` / `left` |
|
|
191
|
+
| `label` | 菜单文本 |
|
|
192
|
+
| `size` | 字号或尺寸 |
|
|
193
|
+
| `bgColor` | 单项背景色 |
|
|
194
|
+
| `offset` | 相对中心的偏移距离 |
|
|
195
|
+
| `hoverOffset` | hover 时额外偏移 |
|
|
196
|
+
| `url` | 链接型菜单项使用 |
|
|
197
|
+
| `action` | 动作型菜单项使用 |
|
|
198
|
+
|
|
199
|
+
## 自定义中心区域
|
|
200
|
+
|
|
201
|
+
如果你不想用默认的标题/副标题/图标结构,可以使用 `render` 自定义:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
const ccm = new CCM({
|
|
205
|
+
container: '#ccm-con',
|
|
206
|
+
style: {
|
|
207
|
+
center: {
|
|
208
|
+
render: () => {
|
|
209
|
+
const el = document.createElement('div');
|
|
210
|
+
el.className = 'custom-center';
|
|
211
|
+
el.textContent = 'Custom Center';
|
|
212
|
+
return el;
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 键盘操作
|
|
220
|
+
|
|
221
|
+
当前版本内置了以下键位:
|
|
222
|
+
|
|
223
|
+
- `W` / `ArrowUp`
|
|
224
|
+
- `D` / `ArrowRight`
|
|
225
|
+
- `S` / `ArrowDown`
|
|
226
|
+
- `A` / `ArrowLeft`
|
|
227
|
+
|
|
228
|
+
已知情况:
|
|
229
|
+
|
|
230
|
+
- 类型系统中存在 `keyBindings` 配置
|
|
231
|
+
- 当前实际逻辑仍然使用内置硬编码键位
|
|
232
|
+
- 这部分已在工程评审文档中列为重点改进项
|
|
233
|
+
|
|
234
|
+
## 本地开发
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
pnpm install
|
|
238
|
+
pnpm typecheck
|
|
239
|
+
pnpm build
|
|
240
|
+
pnpm test
|
|
241
|
+
pnpm storybook
|
|
242
|
+
pnpm build-storybook
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## 测试与文档
|
|
246
|
+
|
|
247
|
+
本仓库当前采用两套互补方式:
|
|
248
|
+
|
|
249
|
+
- Storybook:用来展示典型场景、视觉效果和交互状态
|
|
250
|
+
- Vitest:用来验证配置合并、基础渲染和键盘选择等行为
|
|
251
|
+
|
|
252
|
+
## 发布
|
|
253
|
+
|
|
254
|
+
项目的推荐发布链路是:
|
|
255
|
+
|
|
256
|
+
1. 发布到 npm
|
|
257
|
+
2. 使用 jsDelivr 自动消费 npm 包
|
|
258
|
+
|
|
259
|
+
GitHub Actions 已按这条链路准备好:
|
|
260
|
+
|
|
261
|
+
- `CI`: 负责类型检查、构建、测试、Storybook 构建和 `npm pack --dry-run`
|
|
262
|
+
- `Publish`: 负责正式发布到 npm,并输出 jsDelivr 链接
|
|
263
|
+
|
|
264
|
+
第一次发布前,请先看:
|
|
265
|
+
|
|
266
|
+
- [docs/publish-checklist.md](./docs/publish-checklist.md)
|
|
267
|
+
|
|
268
|
+
## 已知限制
|
|
269
|
+
|
|
270
|
+
这个仓库已经能工作,但仍保留一些“个人项目进入公开包之前常见的原型期痕迹”,例如:
|
|
271
|
+
|
|
272
|
+
- 生命周期清理还不完整
|
|
273
|
+
- 某些配置项已经在类型里存在,但实现还没有完全跟上
|
|
274
|
+
- 目前仍以 UMD 构建为主,后续可以考虑补更现代的 ESM/CJS 双产物
|
|
275
|
+
- 某些 CSS 特性对兼容性要求较高
|
|
276
|
+
|
|
277
|
+
这些问题我没有在这轮直接改动源码,而是统一写进了工程评审文档,方便后续你按节奏逐项优化:
|
|
278
|
+
|
|
279
|
+
- [ENGINEERING_REVIEW.md](./ENGINEERING_REVIEW.md)
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
type CSSSize = string | number;
|
|
2
|
+
type CSSColor = {
|
|
3
|
+
light: string;
|
|
4
|
+
dark?: string;
|
|
5
|
+
} | string;
|
|
6
|
+
export type MenuDirection = 'up' | 'right' | 'down' | 'left';
|
|
7
|
+
interface CenterConfig {
|
|
8
|
+
icon?: {
|
|
9
|
+
url: string;
|
|
10
|
+
size?: CSSSize;
|
|
11
|
+
radius?: CSSSize;
|
|
12
|
+
};
|
|
13
|
+
title?: {
|
|
14
|
+
content: string;
|
|
15
|
+
size?: CSSSize;
|
|
16
|
+
color?: CSSColor;
|
|
17
|
+
};
|
|
18
|
+
subtitle?: {
|
|
19
|
+
content: string;
|
|
20
|
+
size?: CSSSize;
|
|
21
|
+
color?: CSSColor;
|
|
22
|
+
};
|
|
23
|
+
style?: {
|
|
24
|
+
direction?: 'column' | 'row';
|
|
25
|
+
color: CSSColor;
|
|
26
|
+
borderSize?: CSSSize;
|
|
27
|
+
radius?: CSSSize;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface CenterCustom {
|
|
31
|
+
render: () => HTMLElement;
|
|
32
|
+
}
|
|
33
|
+
export type CenterStyle = (CenterConfig & {
|
|
34
|
+
render?: never;
|
|
35
|
+
}) | CenterCustom;
|
|
36
|
+
export type MenuItem = {
|
|
37
|
+
direction: MenuDirection;
|
|
38
|
+
label: string;
|
|
39
|
+
size?: CSSSize;
|
|
40
|
+
bgColor?: CSSColor;
|
|
41
|
+
offset?: CSSSize;
|
|
42
|
+
hoverOffset?: CSSSize;
|
|
43
|
+
} & ({
|
|
44
|
+
url?: string;
|
|
45
|
+
action?: never;
|
|
46
|
+
} | {
|
|
47
|
+
action: () => void | Promise<void>;
|
|
48
|
+
url?: never;
|
|
49
|
+
});
|
|
50
|
+
export interface CCMConfig {
|
|
51
|
+
container: string;
|
|
52
|
+
startingDirections: MenuDirection;
|
|
53
|
+
style: {
|
|
54
|
+
width: CSSSize;
|
|
55
|
+
background: {
|
|
56
|
+
menuColor?: CSSColor;
|
|
57
|
+
centerColor?: CSSColor;
|
|
58
|
+
opacity?: number;
|
|
59
|
+
blur?: number;
|
|
60
|
+
};
|
|
61
|
+
center: CenterStyle;
|
|
62
|
+
menu: {
|
|
63
|
+
length: CSSSize;
|
|
64
|
+
color?: CSSColor;
|
|
65
|
+
radius?: CSSSize;
|
|
66
|
+
};
|
|
67
|
+
showAnimation: {
|
|
68
|
+
center: {
|
|
69
|
+
duration: number;
|
|
70
|
+
};
|
|
71
|
+
menu: {
|
|
72
|
+
durationPerItem: number;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
keyBindings: Partial<Record<string, MenuDirection>>;
|
|
77
|
+
}
|
|
78
|
+
export declare function mergeConfig(config: Partial<CCMConfig>, origin: CCMConfig): CCMConfig;
|
|
79
|
+
export declare function CCMConfigBuilder(config: Partial<CCMConfig>): CCMConfig;
|
|
80
|
+
export declare function CCMConfigBuilder(config: Partial<CCMConfig>, useDefaultKeyBindings: boolean): CCMConfig;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAC/B,KAAK,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC;AAC1D,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7D,UAAU,YAAY;IAGpB,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACzD,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;IAC9D,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;IACjE,KAAK,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;QAAC,KAAK,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACnG;AAED,UAAU,YAAY;IAEpB,MAAM,EAAE,MAAM,WAAW,CAAC;CAC3B;AAGD,MAAM,MAAM,WAAW,GACnB,CAAC,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,GACnC,YAAY,CAAC;AAIjB,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,CAAC;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,KAAK,CAAA;CACf,GAAG;IACF,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,GAAG,CAAC,EAAE,KAAK,CAAA;CACZ,CAAC,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,aAAa,CAAC;IAClC,KAAK,EAAE;QACL,KAAK,EAAE,OAAO,CAAC;QAEf,UAAU,EAAE;YACV,SAAS,CAAC,EAAE,QAAQ,CAAC;YACrB,WAAW,CAAC,EAAE,QAAQ,CAAC;YACvB,OAAO,CAAC,EAAE,MAAM,CAAA;YAChB,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAA;QACD,MAAM,EAAE,WAAW,CAAC;QACpB,IAAI,EAAE;YACJ,MAAM,EAAE,OAAO,CAAC;YAChB,KAAK,CAAC,EAAE,QAAQ,CAAC;YACjB,MAAM,CAAC,EAAE,OAAO,CAAC;SAClB,CAAA;QACD,aAAa,EAAE;YACb,MAAM,EAAE;gBACN,QAAQ,EAAE,MAAM,CAAC;aAClB,CAAA;YACD,IAAI,EAAE;gBACJ,eAAe,EAAE,MAAM,CAAC;aACzB,CAAA;SACF,CAAC;KACH,CAAA;IAED,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;CACpD;AA4CD,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,CAmCpF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;AACxE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,qBAAqB,EAAE,OAAO,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
@keyframes ccm-center-aurora{0%{transform:translate(-50%, -50%) rotate(0deg) scale(1)}50%{transform:translate(-50%, -50%) rotate(180deg) scale(1.08)}100%{transform:translate(-50%, -50%) rotate(360deg) scale(1)}}@keyframes ccm-center-sheen{0%{transform:translateX(-120%)}55%{transform:translateX(130%)}100%{transform:translateX(130%)}}@media(prefers-reduced-motion: reduce){.ccm-con{transform:none;transition:none}.ccm-con .ccm-center{transition:none;transform:none}.ccm-con .ccm-center::before,.ccm-con .ccm-center::after{animation:none}}@keyframes ccm-item-show-rotate{0%{filter:opacity(0) blur(20px);transform:translate(-50%, -50%) rotate(calc(var(--ccm-item-deg) - 90deg)) translateX(0) translateY(0) scale(0.5)}40%{filter:opacity(0.55) blur(10px)}100%{filter:opacity(0.5);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1)) scale(1)}}@keyframes ccm-item-show-rotate-down{0%{filter:opacity(0) blur(20px);transform:translate(-50%, -50%) rotate(calc(var(--ccm-item-deg) - 90deg)) translateX(0) translateY(0) scale(0.5)}40%{filter:opacity(0.55) blur(10px)}100%{filter:opacity(0.5);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1)) translateY(var(--ccm-item-y)) scale(1)}}@keyframes ccm-center-show{0%{filter:opacity(0.2) blur(20px);transform:translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(0.4)}100%{filter:opacity(1);transform:translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1)}}@keyframes ccm-item-show-ripple{0%{filter:opacity(0);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(0) translateY(0) scale(0.6)}70%{filter:opacity(0.55);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * 1.05)) translateY(calc(var(--ccm-item-y) * -1.05)) scale(1.05)}100%{filter:opacity(0.5);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1)) scale(1)}}@keyframes ccm-item-show-ripple-down{0%{filter:opacity(0);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(0) translateY(0) scale(0.6)}70%{filter:opacity(0.55);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1.05)) translateY(calc(var(--ccm-item-y) * 1.05)) scale(1.05)}100%{filter:opacity(0.5);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1)) translateY(var(--ccm-item-y)) scale(1)}}@keyframes ccm-center-expand{0%{filter:opacity(0);clip-path:circle(0% at 50% 50%);transform:translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(0.9)}40%{filter:opacity(0.7);clip-path:circle(60% at 50% 50%)}70%{clip-path:circle(120% at 50% 50%);transform:translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1.03)}100%{filter:opacity(1);clip-path:circle(200% at 50% 50%);transform:translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1)}}.ccm-center{min-width:150px;min-height:150px;width:fit-content;height:fit-content;position:relative;overflow:hidden;z-index:2;padding:12px 20px;border:var(--ccm-center-border-size, 0px) solid var(--ccm-center-border-color, #333);border-radius:var(--ccm-center-radius, 20px);background-color:color-mix(in srgb, var(--ccm-bg-center-color, hsl(0, 0%, 100%)) calc(var(--ccm-bg-opacity, 1) * 100%), transparent);box-shadow:0 4px 8px rgba(0,0,0,.1);-webkit-backdrop-filter:blur(var(--ccm-bg-blur, 0px));backdrop-filter:blur(var(--ccm-bg-blur, 0px));transform:translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0);transition:transform 220ms ease-out,box-shadow 220ms ease-out;display:flex;gap:.5em;flex-direction:column;justify-content:center;align-items:center;animation:ccm-center-show var(--ccm-center-show-duration, 500ms) cubic-bezier(0.3, 0.3, 0.4, 1.15) none}.ccm-center.column{gap:.5em;flex-direction:column}.ccm-center.row{gap:.8em;flex-direction:row}.ccm-center::before{content:"";position:absolute;top:50%;left:50%;width:260%;height:260%;z-index:0;background:radial-gradient(circle at 22% 32%, hsla(194, 90%, 68%, 0.45) 0%, transparent 45%),radial-gradient(circle at 78% 72%, hsla(328, 95%, 68%, 0.35) 0%, transparent 40%),radial-gradient(circle at 50% 50%, hsla(42, 95%, 72%, 0.25) 0%, transparent 56%);transform-origin:center;transform:translate(-50%, -50%);animation:ccm-center-aurora 13s linear infinite;pointer-events:none}.ccm-center::after{content:"";position:absolute;inset:0;z-index:0;opacity:.22;background:linear-gradient(115deg, transparent 22%, rgba(255, 255, 255, 0.85) 49%, transparent 78%);transform:translateX(-120%);animation:ccm-center-sheen 7s ease-in-out infinite;pointer-events:none}.ccm-center img,.ccm-center h3,.ccm-center p{width:fit-content;margin:0 auto}.ccm-center img{display:block;width:var(--ccm-center-icon-size, 100px);height:var(--ccm-center-icon-size, 100px);border-radius:var(--ccm-center-icon-radius, calc(var(--ccm-center-radius, 25%) * 0.8))}.ccm-center h3{font-size:var(--ccm-center-title-size, 30px);color:var(--ccm-center-title-color, #333)}.ccm-center p{font-size:var(--ccm-center-subtitle-size, 15px);color:var(--ccm-center-subtitle-color, #666)}.ccm-items{--ccm-item-y: calc(var(--ccm-item-offset, 0px) + 100px);--ccm-item-hover-shift: var(--ccm-item-hover-offset, 30px);min-height:max(var(--ccm-menu-length, 0),80px);padding:12px 2px;background-color:var(--ccm-item-bg-color, var(--ccm-bg-menu-color, hsl(0, 0%, 93%)));border:0px solid rgba(0,0,0,0);border-radius:var(--ccm-items-radius, 10px);position:absolute;left:50%;top:50%;box-shadow:0 4px 8px rgba(0,0,0,.1);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1));transition:padding .3s ease,filter .3s ease,transform .5s cubic-bezier(0.175, 0.885, 0.32, 1.275),border .3s ease;color:var(--ccm-menu-color, hsl(0, 0%, 20%));white-space:nowrap;font-size:var(--ccm-item-size, 30px);writing-mode:vertical-rl;text-orientation:mixed;text-align:center;will-change:transform;filter:opacity(0.5);animation:ccm-item-show-rotate 1s cubic-bezier(0.3, 0.3, 0.4, 1.15) none}.ccm-items.right{writing-mode:sideways-lr}.ccm-items.left{writing-mode:sideways-rl}.ccm-items.down{transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1)) translateY(var(--ccm-item-y));writing-mode:vertical-lr;animation:ccm-item-show-rotate-down 1s cubic-bezier(0.3, 0.3, 0.4, 1.15) none}.ccm-items.selecting,.ccm-items:hover,.ccm-items:focus,.ccm-items:active{filter:opacity(1);padding:25px 10px;box-shadow:0 6px 12px rgba(0,0,0,.15);transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1 - var(--ccm-item-hover-shift))) scale(1.08);border:var(--ccm-center-border-size, 2px) solid var(--ccm-center-border-color, #333);z-index:1}.ccm-items.selecting.down,.ccm-items:hover.down,.ccm-items:focus.down,.ccm-items:active.down{transform:translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) + var(--ccm-item-hover-shift))) scale(1.08)}.ccm-items:has(a){padding:0}.ccm-items:has(a) a{padding:12px 2px}.ccm-items.selecting:has(a),.ccm-items:hover:has(a),.ccm-items:focus:has(a),.ccm-items:active:has(a){padding:0}.ccm-items.selecting:has(a) a,.ccm-items:hover:has(a) a,.ccm-items:focus:has(a) a,.ccm-items:active:has(a) a{padding:25px 10px}.ccm-items a{color:inherit;text-decoration:none;display:block;width:100%;height:100%;transition:padding .3s ease}.ccm-con{--ccm-parallax-x: 0px;--ccm-parallax-y: 0px;--ccm-tilt-x: 0deg;--ccm-tilt-y: 0deg;margin:0;padding:0;box-sizing:border-box;z-index:10;transform-style:preserve-3d;transform:perspective(300px) rotateX(var(--ccm-tilt-y)) rotateY(var(--ccm-tilt-x));transition:transform 180ms ease-out}.ccm-con.row{flex-direction:row}.ccm-con *{margin:0}.ccm-con *:focus-visible{outline:none}
|
|
2
|
+
|
|
3
|
+
/*# sourceMappingURL=configurable-cross-menu.css.map*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configurable-cross-menu.css","mappings":"AAAA,6BACE,GACE,sDAGF,IACE,2DAGF,KACE,yDAIJ,4BACE,GACE,4BAGF,IACE,2BAGF,KACE,4BAIJ,uCACE,SACE,eACA,gBAEA,qBACE,gBACA,eAEA,yDAEE,gBAUR,gCACE,GACE,6BACA,iHAGF,IACE,gCAIF,KAEE,oBACA,6IAIJ,qCACE,GACE,6BACA,iHAGF,IACE,gCAIF,KACE,oBACA,6IAIJ,2BACE,GACE,+BAEA,8GAGF,KACE,kBACA,6GA+BJ,gCACE,GACE,kBACA,mGAGF,IACE,qBACA,+JAGF,KACE,oBACA,6IAIJ,qCACE,GACE,kBACA,mGAGF,IACE,qBACA,+JAGF,KACE,oBACA,6IAIJ,6BACE,GACE,kBACA,gCACA,8GAGF,IACE,oBACA,iCAGF,IACE,kCACA,+GAGF,KACE,kBACA,kCACA,6GClLJ,YACE,gBACA,iBACA,kBACA,mBACA,kBACA,gBAEA,UACA,kBAEA,qFACA,6CAEA,qIACA,oCAGA,sDACA,8CACA,mGACA,8DACA,aACA,SACA,sBACA,uBACA,mBACA,wGAEA,mBACE,SACA,sBAGF,gBACE,SACA,mBAGF,oBACE,WACA,kBACA,QACA,SACA,WACA,YACA,UACA,+PACE,CAGF,wBACA,gCACA,gDACA,oBAGF,mBACE,WACA,kBACA,QACA,UACA,YACA,mGACE,CACF,4BACA,mDACA,oBAGF,6CAKE,kBACA,cAGF,gBACE,cACA,yCACA,0CACA,uFAKF,eACE,6CACA,0CAGF,cACE,gDACA,6CC/FF,WACE,wDACA,2DAGA,+CACA,iBACA,qFACA,+BACA,4CACA,kBACA,SACA,QACA,oCACA,mIACA,kHAIA,6CACA,mBACA,qCACA,yBACA,uBACA,kBAEA,sBACA,oBAGA,yEAKA,iBACE,yBAGF,gBACE,yBAGF,gBACE,mIACA,yBACA,8EASF,yEAIE,kBACA,kBACA,sCACA,6KACA,qFACA,UAEA,6FACE,wKAKJ,kBACE,UAEA,oBACE,iBAIJ,qGAIE,UAEA,6GACE,kBAIJ,aAEE,cACA,qBACA,cAEA,WACA,YACA,4BC/FN,SACE,sBACA,sBACA,mBACA,mBAOA,SACA,UACA,sBACA,WAKA,4BACA,mFACA,oCAIA,aACE,mBAGF,WACE,SAIA,yBACE,a","sources":["webpack://ConfigurableCrossMenu/./src/styles/animation.scss","webpack://ConfigurableCrossMenu/./src/styles/ccm-center.scss","webpack://ConfigurableCrossMenu/./src/styles/ccm-items.scss","webpack://ConfigurableCrossMenu/./src/styles/index.scss"],"sourcesContent":["@keyframes ccm-center-aurora {\n 0% {\n transform: translate(-50%, -50%) rotate(0deg) scale(1);\n }\n\n 50% {\n transform: translate(-50%, -50%) rotate(180deg) scale(1.08);\n }\n\n 100% {\n transform: translate(-50%, -50%) rotate(360deg) scale(1);\n }\n}\n\n@keyframes ccm-center-sheen {\n 0% {\n transform: translateX(-120%);\n }\n\n 55% {\n transform: translateX(130%);\n }\n\n 100% {\n transform: translateX(130%);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ccm-con {\n transform: none;\n transition: none;\n\n .ccm-center {\n transition: none;\n transform: none;\n\n &::before,\n &::after {\n animation: none;\n }\n }\n\n // .ccm-items {\n // transition: none;\n // }\n }\n}\n\n@keyframes ccm-item-show-rotate {\n 0% {\n filter: opacity(0) blur(20px);\n transform: translate(-50%, -50%) rotate(calc(var(--ccm-item-deg) - 90deg)) translateX(0) translateY(0) scale(0.5);\n }\n\n 40% {\n filter: opacity(0.55) blur(10px);\n // transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * 1.05)) translateY(calc(var(--ccm-item-y) * -1.05)) scale(1.05);\n }\n\n 100% {\n // 比默认高 0.1 试试?\n filter: opacity(0.5);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1)) scale(1);\n }\n}\n\n@keyframes ccm-item-show-rotate-down {\n 0% {\n filter: opacity(0) blur(20px);\n transform: translate(-50%, -50%) rotate(calc(var(--ccm-item-deg) - 90deg)) translateX(0) translateY(0) scale(0.5);\n }\n\n 40% {\n filter: opacity(0.55) blur(10px);\n // transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * 1.05)) translateY(calc(var(--ccm-item-y) * -1.05)) scale(1.05);\n }\n\n 100% {\n filter: opacity(0.5);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1)) translateY(var(--ccm-item-y)) scale(1);\n }\n}\n\n@keyframes ccm-center-show {\n 0% {\n filter: opacity(0.2) blur(20px);\n // transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1.3);\n transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(0.4);\n }\n\n 100% {\n filter: opacity(1);\n transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1);\n }\n}\n\n// 其它可能的方案\n// @keyframes ccm-item-show-rotate {\n// 0% {\n// filter: opacity(0);\n// transform: translate(-50%, -50%) rotate(calc(var(--ccm-item-deg) - 180deg)) translateX(0) translateY(0) scale(0.5);\n// }\n\n// 100% {\n// // 比默认高 0.1 试试?\n// filter: opacity(0.5);\n// transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1)) scale(1);\n// }\n// }\n\n// @keyframes ccm-center-show {\n// 0% {\n// filter: opacity(0);\n// transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(0.4);\n// }\n\n// 100% {\n// filter: opacity(1);\n// transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1);\n// }\n// }\n\n\n@keyframes ccm-item-show-ripple {\n 0% {\n filter: opacity(0);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(0) translateY(0) scale(0.6);\n }\n\n 70% {\n filter: opacity(0.55);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * 1.05)) translateY(calc(var(--ccm-item-y) * -1.05)) scale(1.05);\n }\n\n 100% {\n filter: opacity(0.5);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1)) scale(1);\n }\n}\n\n@keyframes ccm-item-show-ripple-down {\n 0% {\n filter: opacity(0);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(0) translateY(0) scale(0.6);\n }\n\n 70% {\n filter: opacity(0.55);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1.05)) translateY(calc(var(--ccm-item-y) * 1.05)) scale(1.05);\n }\n\n 100% {\n filter: opacity(0.5);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1)) translateY(var(--ccm-item-y)) scale(1);\n }\n}\n\n@keyframes ccm-center-expand {\n 0% {\n filter: opacity(0);\n clip-path: circle(0% at 50% 50%);\n transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(0.9);\n }\n\n 40% {\n filter: opacity(0.7);\n clip-path: circle(60% at 50% 50%);\n }\n\n 70% {\n clip-path: circle(120% at 50% 50%);\n transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1.03);\n }\n\n 100% {\n filter: opacity(1);\n clip-path: circle(200% at 50% 50%);\n transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0) scale(1);\n }\n}",".ccm-center {\n min-width: 150px;\n min-height: 150px;\n width: fit-content;\n height: fit-content;\n position: relative;\n overflow: hidden;\n // isolation: isolate;\n z-index: 2;\n padding: 12px 20px;\n // padding: min(calc(var(--ccm-center-radius) - 8), 12px) 20px;\n border: var(--ccm-center-border-size, 0px) solid var(--ccm-center-border-color, #333);\n border-radius: var(--ccm-center-radius, 20px);\n // background-color: var(--ccm-bg-center-color / var(--ccm-bg-opacity, 1), hsl(0, 0%, 100%));\n background-color: color-mix(in srgb, var(--ccm-bg-center-color, hsl(0, 0%, 100%)) calc(var(--ccm-bg-opacity, 1) * 100%), transparent);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n // 又踩坑…… opacity 和 blur 不兼容……\n // opacity: var(--ccm-bg-opacity, 1);\n -webkit-backdrop-filter: blur(var(--ccm-bg-blur, 0px));\n backdrop-filter: blur(var(--ccm-bg-blur, 0px));\n transform: translate3d(calc(var(--ccm-parallax-x) * -0.08), calc(var(--ccm-parallax-y) * -0.08), 0);\n transition: transform 220ms ease-out, box-shadow 220ms ease-out;\n display: flex;\n gap: 0.5em;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n animation: ccm-center-show var(--ccm-center-show-duration, 500ms) cubic-bezier(0.30, 0.30, 0.40, 1.15) none;\n\n &.column {\n gap: 0.5em;\n flex-direction: column;\n }\n\n &.row {\n gap: 0.8em;\n flex-direction: row;\n }\n\n &::before {\n content: \"\";\n position: absolute;\n top: 50%;\n left: 50%;\n width: 260%;\n height: 260%;\n z-index: 0;\n background:\n radial-gradient(circle at 22% 32%, hsl(194 90% 68% / 0.45) 0%, transparent 45%),\n radial-gradient(circle at 78% 72%, hsl(328 95% 68% / 0.35) 0%, transparent 40%),\n radial-gradient(circle at 50% 50%, hsl(42 95% 72% / 0.25) 0%, transparent 56%);\n transform-origin: center;\n transform: translate(-50%, -50%);\n animation: ccm-center-aurora 13s linear infinite;\n pointer-events: none;\n }\n\n &::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: 0;\n opacity: 0.22;\n background:\n linear-gradient(115deg, transparent 22%, rgb(255 255 255 / 0.85) 49%, transparent 78%);\n transform: translateX(-120%);\n animation: ccm-center-sheen 7s ease-in-out infinite;\n pointer-events: none;\n }\n\n img,\n h3,\n p {\n // position: relative;\n // z-index: 1;\n width: fit-content;\n margin: 0 auto;\n }\n\n img {\n display: block;\n width: var(--ccm-center-icon-size, 100px);\n height: var(--ccm-center-icon-size, 100px);\n border-radius: var(--ccm-center-icon-radius, calc(var(--ccm-center-radius, 25%) * 0.8));\n // object-fit: cover;\n // overflow: hidden;\n }\n\n h3 {\n font-size: var(--ccm-center-title-size, 30px);\n color: var(--ccm-center-title-color, #333);\n }\n\n p {\n font-size: var(--ccm-center-subtitle-size, 15px);\n color: var(--ccm-center-subtitle-color, #666);\n }\n}"," .ccm-items {\n --ccm-item-y: calc(var(--ccm-item-offset, 0px) + 100px);\n --ccm-item-hover-shift: var(--ccm-item-hover-offset, 30px);\n // --ccm-item-hover-shift: calc(var(--ccm-item-hover-offset, 0px) + 15px);\n // --ccm-item-hover-shift: clamp(15px, 30%, 72px);\n min-height: max(var(--ccm-menu-length, 0), 80px);\n padding: 12px 2px;\n background-color: var(--ccm-item-bg-color, var(--ccm-bg-menu-color, hsl(0, 0%, 93%)));\n border: 0px solid transparent;\n border-radius: var(--ccm-items-radius, 10px);\n position: absolute;\n left: 50%;\n top: 50%;\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1));\n transition: padding 0.3s ease,\n filter 0.3s ease,\n transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275),\n border 0.3s ease;\n color: var(--ccm-menu-color, hsl(0, 0%, 20%));\n white-space: nowrap;\n font-size: var(--ccm-item-size, 30px);\n writing-mode: vertical-rl;\n text-orientation: mixed;\n text-align: center;\n // 指明元素将要发生的变化,以便浏览器可以提前做好优化,提升动画性能。一般已经尽力优化作为最后手段,过多使用可能导致内存占用 https: //developer.mozilla.org/zh-CN/docs/Web/CSS/Reference/Properties/will-change\n will-change: transform;\n filter: opacity(0.5);\n // 不能用 forward 否则结束后保留动画状态 hover 的 translate 不生效\n // https: //cubic-bezier.tupulin.com/#cubic-bezier(0.30,0.30,0.40,1.15)\n animation: ccm-item-show-rotate 1s cubic-bezier(0.30, 0.30, 0.40, 1.15) none;\n\n &.up {}\n\n // haiku 4.5 都补不出来的属性😋\n &.right {\n writing-mode: sideways-lr;\n }\n\n &.left {\n writing-mode: sideways-rl;\n }\n\n &.down {\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(calc(var(--ccm-item-x) * -1)) translateY(var(--ccm-item-y));\n writing-mode: vertical-lr;\n animation: ccm-item-show-rotate-down 1s cubic-bezier(0.30, 0.30, 0.40, 1.15) none;\n }\n\n // &.selecting:not(:hover):not(:focus):not(:active) {\n // // padding: 20px 8px;\n // border: 0px;\n // filter: opacity(0.8);\n // }\n\n &.selecting,\n &:hover,\n &:focus,\n &:active {\n filter: opacity(1);\n padding: 25px 10px;\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) * -1 - var(--ccm-item-hover-shift))) scale(1.08);\n border: var(--ccm-center-border-size, 2px) solid var(--ccm-center-border-color, #333);\n z-index: 1;\n\n &.down {\n transform: translate(-50%, -50%) rotate(var(--ccm-item-deg)) translateX(var(--ccm-item-x)) translateY(calc(var(--ccm-item-y) + var(--ccm-item-hover-shift))) scale(1.08);\n }\n }\n\n // 判断子元素!\n &:has(a) {\n padding: 0;\n\n a {\n padding: 12px 2px;\n }\n }\n\n &.selecting:has(a),\n &:hover:has(a),\n &:focus:has(a),\n &:active:has(a) {\n padding: 0;\n\n a {\n padding: 25px 10px;\n }\n }\n\n a {\n // hhh 666\n color: inherit;\n text-decoration: none;\n display: block;\n // 搞不懂 width 默认……\n width: 100%;\n height: 100%;\n transition: padding 0.3s ease;\n }\n }","@use 'animation' as *;\n@use 'ccm-center' as center;\n@use 'ccm-items' as items;\n\n.ccm-con {\n --ccm-parallax-x: 0px;\n --ccm-parallax-y: 0px;\n --ccm-tilt-x: 0deg;\n --ccm-tilt-y: 0deg;\n // max-width: 100lvw;\n // max-height: 100lvh;\n // width: 100lvw;\n // height: 100lvh;\n // perspective 和 overflow 互斥,前者会设置 overflow: flat\n // overflow: hidden;\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n z-index: 10;\n // display: flex;\n // place-items: center;\n // flex-direction: column;\n // overflow: hidden;\n transform-style: preserve-3d;\n transform: perspective(300px) rotateX(var(--ccm-tilt-y)) rotateY(var(--ccm-tilt-x));\n transition: transform 180ms ease-out;\n // opacity: var(--ccm-bg-opacity, 1);\n // backdrop-filter: blur(var(--ccm-bg-blur, 0px));\n\n &.row {\n flex-direction: row;\n }\n\n * {\n margin: 0;\n // 不知为何这个优先级这么高\n // padding: 0;\n\n &:focus-visible {\n outline: none;\n }\n }\n\n .ccm-center {\n // 别问问就是报错要加上 !optional\n @extend center !optional;\n }\n\n .ccm-items {\n @extend items !optional;\n }\n\n}"],"names":[],"sourceRoot":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ConfigurableCrossMenu=t():e.ConfigurableCrossMenu=t()}(globalThis,()=>(()=>{var e={711(e,t,n){var i=n(561);e.exports=function(e){var t,n="",o=e||{};return function(e,o,r,c,l){n+='<div class="ccm-center">',r?n+="\x3c!-- 自定义渲染的内容会通过 JS 插入--\x3e":(l||o)&&(o&&(n=n+"<img"+i.attr("src",o,!0,!0)+' alt="icon">'),l&&(n=n+"<div"+i.attr("class",i.classes(["column"===e?"col":"row"],[!0]),!1,!0)+"><h3>"+i.escape(null==(t=l)?"":t)+"</h3>",c&&(n=n+"<p>"+i.escape(null==(t=c)?"":t)+"</p>"),n+="</div>")),n+="</div>"}.call(this,"direction"in o?o.direction:"undefined"!=typeof direction?direction:void 0,"icon"in o?o.icon:"undefined"!=typeof icon?icon:void 0,"render"in o?o.render:"undefined"!=typeof render?render:void 0,"subtitle"in o?o.subtitle:"undefined"!=typeof subtitle?subtitle:void 0,"title"in o?o.title:"undefined"!=typeof title?title:void 0),n}},548(e,t,n){var i=n(561);e.exports=function(e){var t,n="",o=e||{};return function(e,o,r,c,l,s,a,u,d,f,h){const m=[null!=d&&`--ccm-item-size:${d}`,null!=u&&`--ccm-item-deg:${u}`,null!=h&&`--ccm-item-x:${h}`,null!=a&&`--ccm-item-offset:${a}`,null!=l&&`--ccm-item-hover-offset:${l}`,null!=r&&`--ccm-item-bg-color:${r}`].filter(e).join(";");n=n+"<button"+(i.attr("class",i.classes([`ccm-items ${c}`],[!0]),!1,!0)+i.attr("onclick",o,!0,!0)+i.attr("style",i.style(m),!0,!0))+">",f?n=n+"<a"+i.attr("href",f,!0,!0)+' rel="noopener noreferrer">'+i.escape(null==(t=s)?"":t)+"</a>":n+=i.escape(null==(t=s)?"":t),n+="</button>"}.call(this,"Boolean"in o?o.Boolean:"undefined"!=typeof Boolean?Boolean:void 0,"action"in o?o.action:"undefined"!=typeof action?action:void 0,"bgColor"in o?o.bgColor:"undefined"!=typeof bgColor?bgColor:void 0,"direction"in o?o.direction:"undefined"!=typeof direction?direction:void 0,"hoverOffset"in o?o.hoverOffset:"undefined"!=typeof hoverOffset?hoverOffset:void 0,"label"in o?o.label:"undefined"!=typeof label?label:void 0,"offset"in o?o.offset:"undefined"!=typeof offset?offset:void 0,"rotate"in o?o.rotate:"undefined"!=typeof rotate?rotate:void 0,"size"in o?o.size:"undefined"!=typeof size?size:void 0,"url"in o?o.url:"undefined"!=typeof url?url:void 0,"x"in o?o.x:"undefined"!=typeof x?x:void 0),n}},561(e,t,n){"use strict";var i=Object.prototype.hasOwnProperty;function o(e,t){return Array.isArray(e)?function(e,t){for(var n,i="",r="",c=Array.isArray(t),l=0;l<e.length;l++)(n=o(e[l]))&&(c&&t[l]&&(n=s(n)),i=i+r+n,r=" ");return i}(e,t):e&&"object"==typeof e?function(e){var t="",n="";for(var o in e)o&&e[o]&&i.call(e,o)&&(t=t+n+o,n=" ");return t}(e):e||""}function r(e){if(!e)return"";if("object"==typeof e){var t="";for(var n in e)i.call(e,n)&&(t=t+n+":"+e[n]+";");return t}return e+""}function c(e,t,n,i){if(!1===t||null==t||!t&&("class"===e||"style"===e))return"";if(!0===t)return" "+(i?e:e+'="'+e+'"');var o=typeof t;return"object"!==o&&"function"!==o||"function"!=typeof t.toJSON||(t=t.toJSON()),"string"==typeof t||(t=JSON.stringify(t),n||-1===t.indexOf('"'))?(n&&(t=s(t))," "+e+'="'+t+'"'):" "+e+"='"+t.replace(/'/g,"'")+"'"}t.merge=function e(t,n){if(1===arguments.length){for(var i=t[0],o=1;o<t.length;o++)i=e(i,t[o]);return i}for(var c in n)if("class"===c){var l=t[c]||[];t[c]=(Array.isArray(l)?l:[l]).concat(n[c]||[])}else if("style"===c){l=(l=r(t[c]))&&";"!==l[l.length-1]?l+";":l;var s=r(n[c]);s=s&&";"!==s[s.length-1]?s+";":s,t[c]=l+s}else t[c]=n[c];return t},t.classes=o,t.style=r,t.attr=c,t.attrs=function(e,t){var n="";for(var l in e)if(i.call(e,l)){var s=e[l];if("class"===l){n=c(l,s=o(s),!1,t)+n;continue}"style"===l&&(s=r(s)),n+=c(l,s,!1,t)}return n};var l=/["&<>]/;function s(e){var t=""+e,n=l.exec(t);if(!n)return e;var i,o,r,c="";for(i=n.index,o=0;i<t.length;i++){switch(t.charCodeAt(i)){case 34:r=""";break;case 38:r="&";break;case 60:r="<";break;case 62:r=">";break;default:continue}o!==i&&(c+=t.substring(o,i)),o=i+1,c+=r}return o!==i?c+t.substring(o,i):c}t.escape=s,t.rethrow=function e(t,i,o,r){if(!(t instanceof Error))throw t;if(!("undefined"==typeof window&&i||r))throw t.message+=" on line "+o,t;var c,l,s,a;try{r=r||n(354).readFileSync(i,{encoding:"utf8"}),c=3,l=r.split("\n"),s=Math.max(o-c,0),a=Math.min(l.length,o+c)}catch(n){return t.message+=" - could not read from "+i+" ("+n.message+")",void e(t,null,o)}c=l.slice(s,a).map(function(e,t){var n=t+s+1;return(n==o?" > ":" ")+n+"| "+e}).join("\n"),t.path=i;try{t.message=(i||"Pug")+":"+o+"\n"+c+"\n\n"+t.message}catch(e){}throw t}},354(){}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var r=t[i]={exports:{}};return e[i](r,r.exports,n),r.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{"use strict";n.r(i),n.d(i,{CCM:()=>a});const e={container:"#ccm-con",startingDirections:"up",style:{width:200,radius:50,background:{menuColor:{light:"hsl(0, 0%, 93%)",dark:"hsl(0, 0%, 30%)"},centerColor:{light:"hsl(0, 0%, 100%)",dark:"hsl(0, 0%, 50%)"},opacity:.5,blur:3},center:{title:{content:"CCM",size:16,color:{light:"hsl(0, 0%, 20%)",dark:"hsl(0, 0%, 90%)"},radius:"5%"},subtitle:{content:"Configurable Cross Menu",size:12,color:{light:"hsl(0, 0%, 50%)",dark:"hsl(0, 0%, 80%)"}},style:{direction:"column",color:{light:"hsl(0, 0%, 20%)",dark:"hsl(0, 0%, 90%)"},radius:20}},menu:{length:100,color:{light:"hsl(0, 0%, 40%)",dark:"hsl(0, 0%, 60%)"},radius:8},showAnimation:{center:{duration:500},menu:{durationPerItem:100}}},keyBindings:{}};function t(e,t){var n,i,o,r,c,l;const s=t;return{...s,...e,style:{...s.style,...e.style,background:{...s.style.background,...null===(n=e.style)||void 0===n?void 0:n.background},menu:{...s.style.menu,...null===(i=e.style)||void 0===i?void 0:i.menu},center:(()=>{var t;const n="render"in s.style.center?void 0:s.style.center,i=null===(t=e.style)||void 0===t?void 0:t.center;return i?"render"in i?i:{...i,style:{...null==n?void 0:n.style,...i.style}}:s.style.center})(),showAnimation:{center:{...s.style.showAnimation.center,...null===(r=null===(o=e.style)||void 0===o?void 0:o.showAnimation)||void 0===r?void 0:r.center},menu:{...s.style.showAnimation.menu,...null===(l=null===(c=e.style)||void 0===c?void 0:c.showAnimation)||void 0===l?void 0:l.menu}}}}}var o=n(711),r=n.n(o),c=n(548),l=n.n(c);async function s(e,t=5,n=1e3){try{return await e()}catch(i){if(t<=0)throw i;return await new Promise(e=>setTimeout(e,n)),s(e,t-1,2*n)}}class a{get config(){return this._config}set config(e){this._config={...this._config,...e}}get container(){var e;return this._containerEle&&(null===(e=document.querySelector(this.config.container))||void 0===e?void 0:e.isSameNode(this._containerEle))||(this._containerEle=document.querySelector(this.config.container)),this._containerEle}constructor(n,i=!0){this.items=[],this.initialized=!1,this._containerEle=null,this.selectAwaitingDirection=null,this.selectAwaitingTimer=null,this.selectAwaitDelayMS=5e3,this._config=function(n,i){const o=t(n,e);return i&&(o.keyBindings={w:"up",d:"right",s:"down",a:"left",arrowup:"up",arrowright:"right",arrowdown:"down",arrowleft:"left"}),o}(n,i)}render(e=[],n,i){try{this.destroy()}catch(e){console.error("Error during previous CCM destroy:",e)}e&&(this.items=e),i&&(this._config=t(i,this._config)),n&&(this.config.container=n),this.initialized||(this.initialized=!0,this.updateCSS(),this.registerParallaxEffect(),this.registerKeyboardEvents());try{this.renderCenter(),setTimeout(()=>{this.renderMenuItems()},Math.max(0,this.config.style.showAnimation.center.duration-500))}catch(e){console.error("Error rendering CCM:",e),this.destroy()}}updateCSS(){var e,t,n,i,o,r,c,l,s,a,u,d;this.container.classList.add("ccm-con");const f=document.head,h=this.config.style,m=document.createElement("style"),g=e=>e?"string"==typeof e?e:e.light:"",y=e=>{var t;return e?"string"==typeof e?e:null!==(t=e.dark)&&void 0!==t?t:e.light:""},v=e=>null!=e?"number"==typeof e?`${e}px`:e:"",p="render"in h.center?null:h.center,b=(e,t)=>null!=t&&""!==t?` ${e}: ${t};`:"";m.innerHTML=[":root {",b("--ccm-width",v(h.width)),b("--ccm-bg-menu-color",g(h.background.menuColor)),b("--ccm-bg-center-color",g(h.background.centerColor)),b("--ccm-bg-opacity",h.background.opacity),null!=h.background.blur?` --ccm-bg-blur: ${h.background.blur}px;`:"",null!=(null===(e=null==p?void 0:p.icon)||void 0===e?void 0:e.size)?` --ccm-center-icon-size: ${v(p.icon.size)};`:"",null!=(null===(t=null==p?void 0:p.title)||void 0===t?void 0:t.size)?` --ccm-center-title-size: ${v(p.title.size)};`:"",(null===(n=null==p?void 0:p.title)||void 0===n?void 0:n.color)?b("--ccm-center-title-color",g(p.title.color)):"",null!=(null===(i=null==p?void 0:p.subtitle)||void 0===i?void 0:i.size)?` --ccm-center-subtitle-size: ${v(p.subtitle.size)};`:"",(null===(o=null==p?void 0:p.subtitle)||void 0===o?void 0:o.color)?b("--ccm-center-subtitle-color",g(p.subtitle.color)):"",null!=(null===(r=null==p?void 0:p.style)||void 0===r?void 0:r.borderSize)?` --ccm-center-border-size: ${v(p.style.borderSize)};`:"",(null===(c=null==p?void 0:p.style)||void 0===c?void 0:c.color)?b("--ccm-center-border-color",g(p.style.color)):"",null!=(null===(l=null==p?void 0:p.style)||void 0===l?void 0:l.radius)?` --ccm-center-radius: ${v(p.style.radius)};`:"",b("--ccm-menu-length",v(h.menu.length)),b("--ccm-menu-color",g(h.menu.color)),null!=h.menu.radius?` --ccm-menu-radius: ${v(h.menu.radius)};`:"","}","",".dark {",b("--ccm-bg-menu-color",y(h.background.menuColor)),b("--ccm-bg-center-color",y(h.background.centerColor)),(null===(s=null==p?void 0:p.title)||void 0===s?void 0:s.color)?b("--ccm-center-title-color",y(p.title.color)):"",(null===(a=null==p?void 0:p.subtitle)||void 0===a?void 0:a.color)?b("--ccm-center-subtitle-color",y(p.subtitle.color)):"",(null===(u=null==p?void 0:p.style)||void 0===u?void 0:u.color)?b("--ccm-center-border-color",y(p.style.color)):"",b("--ccm-menu-color",y(h.menu.color)),(null===(d=null==p?void 0:p.style)||void 0===d?void 0:d.color)?b("color",y(p.style.color)):"","}",h.showAnimation.menu.durationPerItem?`--ccm-menu-show-duration: ${h.showAnimation.menu.durationPerItem}ms`:"",h.showAnimation.center.duration?`--ccm-center-show-duration: ${h.showAnimation.center.duration}ms`:""].filter(Boolean).join("\n"),f.appendChild(m)}renderMenuItems(){const e=this.items,t=[e.filter(e=>"up"===e.direction),e.filter(e=>"right"===e.direction),e.filter(e=>"down"===e.direction),e.filter(e=>"left"===e.direction)],n={};e.forEach(e=>{if("function"==typeof e.action){const t=Math.random().toString(36).substr(2,9);n[t]=e.action,e.action=`__ccm_dispatch_func('${t}')`}}),window.__ccm_dispatch_func=function(e){const t=n[e];t?t():console.warn(`No function found for hash: ${e}`)},(async()=>{var e;const n=null!==(e=this.config.style.showAnimation.menu.durationPerItem)&&void 0!==e?e:0;for(const e of t)0!==e.length&&(await new Promise(e=>setTimeout(e,n)),1===e.length?this._createMenuItem(e[0]):2===e.length&&(this._createMenuItem(e[0],-20,6),await new Promise(e=>setTimeout(e,n)),this._createMenuItem(e[1],20,-6)))})()}_createMenuItem(e,t=0,n=0){const i=l()({...e,action:e.action,rotate:`${{up:0,right:90,down:0,left:270}[e.direction]+t}deg`,x:`${n}px`});this.container.insertAdjacentHTML("beforeend",i)}renderCenter(){var e,t;if(this.config.style.center.render&&"function"==typeof this.config.style.center.render){const e=this.config.style.center.render();if(!(e instanceof HTMLElement))throw new Error("Custom center render function must return an HTMLElement");return void this.container.appendChild(e)}const n=r()({title:this.config.style.center.title.content,subtitle:null===(e=this.config.style.center.subtitle)||void 0===e?void 0:e.content,icon:null===(t=this.config.style.center.icon)||void 0===t?void 0:t.url}),i=document.querySelector(this.config.container);if(!i)throw new Error(`Container element not found for selector: ${this.config.container}`);i.insertAdjacentHTML("beforeend",n)}registerParallaxEffect(){const e=async()=>{const e=document.body;if(await s(()=>Promise.resolve(this.container),10,500),!e||!this.container)throw new Error("Parallax container or CCM container not found");const t=window.matchMedia("(prefers-reduced-motion: reduce)").matches,n=window.matchMedia("(hover: hover) and (pointer: fine)").matches;if(t||!n)return;let i=null,o=!1;const r=()=>{if(!i)return;const t=e.getBoundingClientRect(),n=2*((i.clientX-t.left)/t.width-.5),o=2*((i.clientY-t.top)/t.height-.5);this.container.style.setProperty("--ccm-parallax-x",`${(80*n).toFixed(2)}px`),this.container.style.setProperty("--ccm-parallax-y",`${(80*o).toFixed(2)}px`),this.container.style.setProperty("--ccm-tilt-x",`${(25*n).toFixed(2)}deg`),this.container.style.setProperty("--ccm-tilt-y",`${(20*-o).toFixed(2)}deg`)},c=()=>{this.container.style.setProperty("--ccm-parallax-x","0px"),this.container.style.setProperty("--ccm-parallax-y","0px"),this.container.style.setProperty("--ccm-tilt-x","0deg"),this.container.style.setProperty("--ccm-tilt-y","0deg")};e.addEventListener("pointermove",e=>{i=e,o||(o=!0,requestAnimationFrame(()=>{r(),o=!1}))},{passive:!0}),e.addEventListener("pointerleave",()=>{i=null,requestAnimationFrame(c)},{passive:!0})};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}registerKeyboardEvents(){document.addEventListener("keydown",e=>{const t=e.key.toLowerCase(),n=this.config.keyBindings[t];n&&(e.preventDefault(),this._ccmHandlePress(n))})}_clear(){const e=document.querySelectorAll(".ccm-items.selecting");this.selectAwaitingDirection=null,e.forEach(e=>{e.classList.remove("selecting")})}_ccmClearSelecting(e=!1){this.selectAwaitingTimer&&(clearTimeout(this.selectAwaitingTimer),this.selectAwaitingTimer=null),e?this.selectAwaitingTimer=setTimeout(()=>this._clear(),!0===e?this.selectAwaitDelayMS:e):this._clear()}_ccmHandlePress(e){if(!e)throw new Error("direction is required");if(null!==this.selectAwaitingDirection){if(e===this.selectAwaitingDirection)return void this._ccmClearSelecting(!0);if(this._ccmTriggerAwaitingSelection(e))return;this._ccmClearSelecting()}const t=document.querySelectorAll(`.ccm-items.${e}`);if(t)if(t.forEach(e=>{e.classList.add("selecting")}),t.length>1)this.selectAwaitingDirection=e,this._ccmClearSelecting(!0);else if(1===t.length){const e=t[0];e.firstElementChild?e.children[0].click():e.click(),this._ccmClearSelecting(500)}}_ccmIsVertical(e){return!!e&&["up","down"].includes(e)}_ccmIsHorizontal(e){return!!e&&["left","right"].includes(e)}_ccmTriggerAwaitingSelection(e){var t,n;const i=document.querySelectorAll(`.ccm-items.${this.selectAwaitingDirection}`);if(0===i.length)return!1;const o=this._ccmIsVertical(this.selectAwaitingDirection);if(!(o?this._ccmIsHorizontal(e):this._ccmIsVertical(e)))return this._ccmClearSelecting(),!0;const r=Array.from(i).sort((e,t)=>{const n=e.getBoundingClientRect(),i=t.getBoundingClientRect();return o?n.left-i.left:n.top-i.top}),c=o?"left"===e?r[0]:r[r.length-1]:"up"===e?r[0]:r[r.length-1];return!!c&&(null===(n=null!==(t=c.firstElementChild)&&void 0!==t?t:c)||void 0===n||n.click(),this._ccmClearSelecting(1e3),!0)}destroy(){const e=document.querySelector(this.config.container);e&&(e.innerHTML="",this.selectAwaitingDirection=null,this.selectAwaitingTimer&&(clearTimeout(this.selectAwaitingTimer),this.selectAwaitingTimer=null),this.items=[])}}})(),i})());
|
|
2
|
+
//# sourceMappingURL=configurable-cross-menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configurable-cross-menu.js","mappings":"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","sources":["webpack://ConfigurableCrossMenu/webpack/universalModuleDefinition","webpack://ConfigurableCrossMenu/./src/templates/center.pug","webpack://ConfigurableCrossMenu/./src/templates/menuItem.pug","webpack://ConfigurableCrossMenu/./node_modules/.pnpm/pug-runtime@3.0.1/node_modules/pug-runtime/index.js","webpack://ConfigurableCrossMenu/webpack/bootstrap","webpack://ConfigurableCrossMenu/webpack/runtime/compat get default export","webpack://ConfigurableCrossMenu/webpack/runtime/define property getters","webpack://ConfigurableCrossMenu/webpack/runtime/hasOwnProperty shorthand","webpack://ConfigurableCrossMenu/webpack/runtime/make namespace object","webpack://ConfigurableCrossMenu/./src/config.ts","webpack://ConfigurableCrossMenu/./src/utils/utils.ts","webpack://ConfigurableCrossMenu/./src/crossMenu.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ConfigurableCrossMenu\"] = factory();\n\telse\n\t\troot[\"ConfigurableCrossMenu\"] = factory();\n})(globalThis, () => {\nreturn ","var pug = require(\"!../../node_modules/.pnpm/pug@3.0.4/node_modules/pug-runtime/index.js\");\n\nfunction template(locals) {var pug_html = \"\", pug_mixins = {}, pug_interp;;\n var locals_for_with = (locals || {});\n \n (function (direction, icon, render, subtitle, title) {\n pug_html = pug_html + \"\\u003Cdiv class=\\\"ccm-center\\\"\\u003E\";\nif (render) {\npug_html = pug_html + \"\\u003C!-- 自定义渲染的内容会通过 JS 插入--\\u003E\";\n}\nelse\nif (title || icon) {\nif (icon) {\npug_html = pug_html + \"\\u003Cimg\" + (pug.attr(\"src\", icon, true, true)+\" alt=\\\"icon\\\"\") + \"\\u003E\";\n}\nif (title) {\npug_html = pug_html + \"\\u003Cdiv\" + (pug.attr(\"class\", pug.classes([direction === 'column'? 'col': 'row'], [true]), false, true)) + \"\\u003E\\u003Ch3\\u003E\" + (pug.escape(null == (pug_interp = title) ? \"\" : pug_interp)) + \"\\u003C\\u002Fh3\\u003E\";\nif (subtitle) {\npug_html = pug_html + \"\\u003Cp\\u003E\" + (pug.escape(null == (pug_interp = subtitle) ? \"\" : pug_interp)) + \"\\u003C\\u002Fp\\u003E\";\n}\npug_html = pug_html + \"\\u003C\\u002Fdiv\\u003E\";\n}\n}\npug_html = pug_html + \"\\u003C\\u002Fdiv\\u003E\";\n }.call(this, \"direction\" in locals_for_with ?\n locals_for_with.direction :\n typeof direction !== 'undefined' ? direction : undefined, \"icon\" in locals_for_with ?\n locals_for_with.icon :\n typeof icon !== 'undefined' ? icon : undefined, \"render\" in locals_for_with ?\n locals_for_with.render :\n typeof render !== 'undefined' ? render : undefined, \"subtitle\" in locals_for_with ?\n locals_for_with.subtitle :\n typeof subtitle !== 'undefined' ? subtitle : undefined, \"title\" in locals_for_with ?\n locals_for_with.title :\n typeof title !== 'undefined' ? title : undefined));\n ;;return pug_html;};\nmodule.exports = template;","var pug = require(\"!../../node_modules/.pnpm/pug@3.0.4/node_modules/pug-runtime/index.js\");\n\nfunction template(locals) {var pug_html = \"\", pug_mixins = {}, pug_interp;;\n var locals_for_with = (locals || {});\n \n (function (Boolean, action, bgColor, direction, hoverOffset, label, offset, rotate, size, url, x) {\n const styleParts = [size != null && `--ccm-item-size:${size}`, rotate != null && `--ccm-item-deg:${rotate}`, x != null && `--ccm-item-x:${x}`, offset != null && `--ccm-item-offset:${offset}`, hoverOffset != null && `--ccm-item-hover-offset:${hoverOffset}`, bgColor != null && `--ccm-item-bg-color:${bgColor}`].filter(Boolean).join(';')\npug_html = pug_html + \"\\u003Cbutton\" + (pug.attr(\"class\", pug.classes([`ccm-items ${direction}`], [true]), false, true)+pug.attr(\"onclick\", action, true, true)+pug.attr(\"style\", pug.style(styleParts), true, true)) + \"\\u003E\";\nif (url) {\npug_html = pug_html + \"\\u003Ca\" + (pug.attr(\"href\", url, true, true)+\" rel=\\\"noopener noreferrer\\\"\") + \"\\u003E\" + (pug.escape(null == (pug_interp = label) ? \"\" : pug_interp)) + \"\\u003C\\u002Fa\\u003E\";\n}\nelse {\npug_html = pug_html + (pug.escape(null == (pug_interp = label) ? \"\" : pug_interp));\n}\npug_html = pug_html + \"\\u003C\\u002Fbutton\\u003E\";\n }.call(this, \"Boolean\" in locals_for_with ?\n locals_for_with.Boolean :\n typeof Boolean !== 'undefined' ? Boolean : undefined, \"action\" in locals_for_with ?\n locals_for_with.action :\n typeof action !== 'undefined' ? action : undefined, \"bgColor\" in locals_for_with ?\n locals_for_with.bgColor :\n typeof bgColor !== 'undefined' ? bgColor : undefined, \"direction\" in locals_for_with ?\n locals_for_with.direction :\n typeof direction !== 'undefined' ? direction : undefined, \"hoverOffset\" in locals_for_with ?\n locals_for_with.hoverOffset :\n typeof hoverOffset !== 'undefined' ? hoverOffset : undefined, \"label\" in locals_for_with ?\n locals_for_with.label :\n typeof label !== 'undefined' ? label : undefined, \"offset\" in locals_for_with ?\n locals_for_with.offset :\n typeof offset !== 'undefined' ? offset : undefined, \"rotate\" in locals_for_with ?\n locals_for_with.rotate :\n typeof rotate !== 'undefined' ? rotate : undefined, \"size\" in locals_for_with ?\n locals_for_with.size :\n typeof size !== 'undefined' ? size : undefined, \"url\" in locals_for_with ?\n locals_for_with.url :\n typeof url !== 'undefined' ? url : undefined, \"x\" in locals_for_with ?\n locals_for_with.x :\n typeof x !== 'undefined' ? x : undefined));\n ;;return pug_html;};\nmodule.exports = template;","'use strict';\n\nvar pug_has_own_property = Object.prototype.hasOwnProperty;\n\n/**\n * Merge two attribute objects giving precedence\n * to values in object `b`. Classes are special-cased\n * allowing for arrays and merging/joining appropriately\n * resulting in a string.\n *\n * @param {Object} a\n * @param {Object} b\n * @return {Object} a\n * @api private\n */\n\nexports.merge = pug_merge;\nfunction pug_merge(a, b) {\n if (arguments.length === 1) {\n var attrs = a[0];\n for (var i = 1; i < a.length; i++) {\n attrs = pug_merge(attrs, a[i]);\n }\n return attrs;\n }\n\n for (var key in b) {\n if (key === 'class') {\n var valA = a[key] || [];\n a[key] = (Array.isArray(valA) ? valA : [valA]).concat(b[key] || []);\n } else if (key === 'style') {\n var valA = pug_style(a[key]);\n valA = valA && valA[valA.length - 1] !== ';' ? valA + ';' : valA;\n var valB = pug_style(b[key]);\n valB = valB && valB[valB.length - 1] !== ';' ? valB + ';' : valB;\n a[key] = valA + valB;\n } else {\n a[key] = b[key];\n }\n }\n\n return a;\n}\n\n/**\n * Process array, object, or string as a string of classes delimited by a space.\n *\n * If `val` is an array, all members of it and its subarrays are counted as\n * classes. If `escaping` is an array, then whether or not the item in `val` is\n * escaped depends on the corresponding item in `escaping`. If `escaping` is\n * not an array, no escaping is done.\n *\n * If `val` is an object, all the keys whose value is truthy are counted as\n * classes. No escaping is done.\n *\n * If `val` is a string, it is counted as a class. No escaping is done.\n *\n * @param {(Array.<string>|Object.<string, boolean>|string)} val\n * @param {?Array.<string>} escaping\n * @return {String}\n */\nexports.classes = pug_classes;\nfunction pug_classes_array(val, escaping) {\n var classString = '',\n className,\n padding = '',\n escapeEnabled = Array.isArray(escaping);\n for (var i = 0; i < val.length; i++) {\n className = pug_classes(val[i]);\n if (!className) continue;\n escapeEnabled && escaping[i] && (className = pug_escape(className));\n classString = classString + padding + className;\n padding = ' ';\n }\n return classString;\n}\nfunction pug_classes_object(val) {\n var classString = '',\n padding = '';\n for (var key in val) {\n if (key && val[key] && pug_has_own_property.call(val, key)) {\n classString = classString + padding + key;\n padding = ' ';\n }\n }\n return classString;\n}\nfunction pug_classes(val, escaping) {\n if (Array.isArray(val)) {\n return pug_classes_array(val, escaping);\n } else if (val && typeof val === 'object') {\n return pug_classes_object(val);\n } else {\n return val || '';\n }\n}\n\n/**\n * Convert object or string to a string of CSS styles delimited by a semicolon.\n *\n * @param {(Object.<string, string>|string)} val\n * @return {String}\n */\n\nexports.style = pug_style;\nfunction pug_style(val) {\n if (!val) return '';\n if (typeof val === 'object') {\n var out = '';\n for (var style in val) {\n /* istanbul ignore else */\n if (pug_has_own_property.call(val, style)) {\n out = out + style + ':' + val[style] + ';';\n }\n }\n return out;\n } else {\n return val + '';\n }\n}\n\n/**\n * Render the given attribute.\n *\n * @param {String} key\n * @param {String} val\n * @param {Boolean} escaped\n * @param {Boolean} terse\n * @return {String}\n */\nexports.attr = pug_attr;\nfunction pug_attr(key, val, escaped, terse) {\n if (\n val === false ||\n val == null ||\n (!val && (key === 'class' || key === 'style'))\n ) {\n return '';\n }\n if (val === true) {\n return ' ' + (terse ? key : key + '=\"' + key + '\"');\n }\n var type = typeof val;\n if (\n (type === 'object' || type === 'function') &&\n typeof val.toJSON === 'function'\n ) {\n val = val.toJSON();\n }\n if (typeof val !== 'string') {\n val = JSON.stringify(val);\n if (!escaped && val.indexOf('\"') !== -1) {\n return ' ' + key + \"='\" + val.replace(/'/g, ''') + \"'\";\n }\n }\n if (escaped) val = pug_escape(val);\n return ' ' + key + '=\"' + val + '\"';\n}\n\n/**\n * Render the given attributes object.\n *\n * @param {Object} obj\n * @param {Object} terse whether to use HTML5 terse boolean attributes\n * @return {String}\n */\nexports.attrs = pug_attrs;\nfunction pug_attrs(obj, terse) {\n var attrs = '';\n\n for (var key in obj) {\n if (pug_has_own_property.call(obj, key)) {\n var val = obj[key];\n\n if ('class' === key) {\n val = pug_classes(val);\n attrs = pug_attr(key, val, false, terse) + attrs;\n continue;\n }\n if ('style' === key) {\n val = pug_style(val);\n }\n attrs += pug_attr(key, val, false, terse);\n }\n }\n\n return attrs;\n}\n\n/**\n * Escape the given string of `html`.\n *\n * @param {String} html\n * @return {String}\n * @api private\n */\n\nvar pug_match_html = /[\"&<>]/;\nexports.escape = pug_escape;\nfunction pug_escape(_html) {\n var html = '' + _html;\n var regexResult = pug_match_html.exec(html);\n if (!regexResult) return _html;\n\n var result = '';\n var i, lastIndex, escape;\n for (i = regexResult.index, lastIndex = 0; i < html.length; i++) {\n switch (html.charCodeAt(i)) {\n case 34:\n escape = '"';\n break;\n case 38:\n escape = '&';\n break;\n case 60:\n escape = '<';\n break;\n case 62:\n escape = '>';\n break;\n default:\n continue;\n }\n if (lastIndex !== i) result += html.substring(lastIndex, i);\n lastIndex = i + 1;\n result += escape;\n }\n if (lastIndex !== i) return result + html.substring(lastIndex, i);\n else return result;\n}\n\n/**\n * Re-throw the given `err` in context to the\n * the pug in `filename` at the given `lineno`.\n *\n * @param {Error} err\n * @param {String} filename\n * @param {String} lineno\n * @param {String} str original source\n * @api private\n */\n\nexports.rethrow = pug_rethrow;\nfunction pug_rethrow(err, filename, lineno, str) {\n if (!(err instanceof Error)) throw err;\n if ((typeof window != 'undefined' || !filename) && !str) {\n err.message += ' on line ' + lineno;\n throw err;\n }\n var context, lines, start, end;\n try {\n str = str || require('fs').readFileSync(filename, {encoding: 'utf8'});\n context = 3;\n lines = str.split('\\n');\n start = Math.max(lineno - context, 0);\n end = Math.min(lines.length, lineno + context);\n } catch (ex) {\n err.message +=\n ' - could not read from ' + filename + ' (' + ex.message + ')';\n pug_rethrow(err, null, lineno);\n return;\n }\n\n // Error context\n context = lines\n .slice(start, end)\n .map(function(line, i) {\n var curr = i + start + 1;\n return (curr == lineno ? ' > ' : ' ') + curr + '| ' + line;\n })\n .join('\\n');\n\n // Alter exception message\n err.path = filename;\n try {\n err.message =\n (filename || 'Pug') +\n ':' +\n lineno +\n '\\n' +\n context +\n '\\n\\n' +\n err.message;\n } catch (e) {}\n throw err;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","type CSSSize = string | number;\ntype CSSColor = { light: string, dark?: string } | string; // 支持单一颜色字符串或根据主题自动切换的颜色对象\nexport type MenuDirection = 'up' | 'right' | 'down' | 'left';\n\ninterface CenterConfig {\n // 方式1:预设图标 + 文字\n // icon radius 默认为 center radius 的 80%,可以通过此处覆盖\n icon?: { url: string; size?: CSSSize, radius?: CSSSize };\n title?: { content: string; size?: CSSSize, color?: CSSColor };\n subtitle?: { content: string; size?: CSSSize, color?: CSSColor };\n style?: { direction?: 'column' | 'row', color: CSSColor, borderSize?: CSSSize, radius?: CSSSize }; // 图标和文字的排列方式,默认为 'column'(图标在上,文字在下),'row'(图标在左,文字在右)\n}\n\ninterface CenterCustom {\n // 方式2:完全自定义\n render: () => HTMLElement;\n}\n\n// 用 discriminated union 替代 union of objects\nexport type CenterStyle =\n | (CenterConfig & { render?: never })\n | CenterCustom;\n\ntype MenuStyle = {}\n\nexport type MenuItem = {\n direction: MenuDirection,\n label: string,\n size?: CSSSize,\n bgColor?: CSSColor,\n offset?: CSSSize, // 菜单项相对于中心的偏移距离,默认偏移 50px,可能需要手动调整\n hoverOffset?: CSSSize, // 鼠标悬停时菜单项的额外偏移距离,默认为 15px,可能需要手动调整\n} & ({\n url?: string\n action?: never\n} | {\n action: () => void | Promise<void>\n url?: never\n});\n\nexport interface CCMConfig {\n container: string, // 菜单挂载的容器\n startingDirections: MenuDirection,\n style: {\n width: CSSSize, // ?这是干啥的?弃用?\n // radius: CSSSize,\n background: {\n menuColor?: CSSColor,\n centerColor?: CSSColor,\n opacity?: number\n blur?: number,\n }\n center: CenterStyle,\n menu: {\n length: CSSSize, // 菜单长度\n color?: CSSColor,\n radius?: CSSSize,\n }\n showAnimation: {\n center: {\n duration: number, // 设置 0 关闭\n }\n menu: {\n durationPerItem: number, // 设置 0 关闭\n }\n },\n }\n // items: MenuItem[],\n keyBindings: Partial<Record<string, MenuDirection>>\n}\n\n\n\nconst defaultConfig = {\n container: \"#ccm-con\",\n startingDirections: 'up',\n style: {\n width: 200,\n radius: 50,\n background: {\n menuColor: { light: 'hsl(0, 0%, 93%)', dark: 'hsl(0, 0%, 30%)' },\n // ~~现在不需要注意了 | 注意,如果需要 blur 效果,必须在背景色中使用 alpha 通道(如 rgba 或 hsla),下方的 opacity 为整个元素的透明度与 blur 无关\n centerColor: { light: 'hsl(0, 0%, 100%)', dark: 'hsl(0, 0%, 50%)' },\n opacity: 0.5,\n blur: 3,\n },\n center: {\n title: { content: 'CCM', size: 16, color: { light: 'hsl(0, 0%, 20%)', dark: 'hsl(0, 0%, 90%)' }, radius: '5%' },\n subtitle: { content: 'Configurable Cross Menu', size: 12, color: { light: 'hsl(0, 0%, 50%)', dark: 'hsl(0, 0%, 80%)' } },\n //TODO color 没有实现\n style: { direction: 'column', color: { light: 'hsl(0, 0%, 20%)', dark: 'hsl(0, 0%, 90%)' }, radius: 20 }, // borderSize: 2, borderColor: 'hsl(0, 0%, 80%)',\n },\n menu: {\n length: 100,\n\n color: { light: 'hsl(0, 0%, 40%)', dark: 'hsl(0, 0%, 60%)' },\n radius: 8,\n },\n showAnimation: {\n center: {\n duration: 500,\n },\n menu: {\n durationPerItem: 100,\n }\n }\n },\n // items: [],\n keyBindings: {}\n} as CCMConfig;\n\n// export function mergeConfig(config: Partial<CCMConfig>): CCMConfig;\n// export function mergeConfig(config: Partial<CCMConfig>, origin: CCMConfig): CCMConfig;\nexport function mergeConfig(config: Partial<CCMConfig>, origin: CCMConfig): CCMConfig {\n // let base: CCMConfig;\n // if (origin) {\n // base = origin;\n // } else {\n // // @ts-expect-error this\n // if ('config' in this) {\n // // @ts-expect-error this\n // base = this.config;\n // } else {\n // throw new Error('No origin config provided and this.config is not available');\n // }\n // }\n const base = origin;\n return {\n ...base,\n ...config,\n style: {\n ...base.style,\n ...config.style,\n background: { ...base.style.background, ...config.style?.background },\n menu: { ...base.style.menu, ...config.style?.menu },\n center: (() => {\n const dc = 'render' in base.style.center ? undefined : base.style.center;\n const uc = config.style?.center;\n if (!uc) return base.style.center;\n if ('render' in uc) return uc;\n return { ...uc, style: { ...dc?.style, ...uc.style } as NonNullable<typeof uc.style> };\n })(),\n showAnimation: {\n center: { ...base.style.showAnimation.center, ...config.style?.showAnimation?.center },\n menu: { ...base.style.showAnimation.menu, ...config.style?.showAnimation?.menu },\n },\n },\n }\n}\n\nexport function CCMConfigBuilder(config: Partial<CCMConfig>): CCMConfig;\nexport function CCMConfigBuilder(config: Partial<CCMConfig>, useDefaultKeyBindings: boolean): CCMConfig;\nexport function CCMConfigBuilder(config: Partial<CCMConfig>, useDefaultKeyBindings?: boolean): CCMConfig {\n const merged: CCMConfig = mergeConfig(config, defaultConfig);\n if (useDefaultKeyBindings) merged.keyBindings = {\n w: 'up',\n d: 'right',\n s: 'down',\n a: 'left',\n arrowup: 'up',\n arrowright: 'right',\n arrowdown: 'down',\n arrowleft: 'left',\n };\n return merged;\n}\n\n// export { CCMConfigBuilder };\n\nconst testItems: MenuItem[] = [\n { direction: 'up', label: 'Up Item', action: () => alert('Up') },\n { direction: 'right', label: 'Right Item', action: () => alert('Right') },\n { direction: 'down', label: 'Down Item1', action: () => alert('Down') },\n { direction: 'down', label: 'Down Item2', url: 'https://bilibili.com' },\n { direction: 'left', label: 'Left Item', action: () => alert('Left') },\n]","export async function retry<T>(\n fn: () => Promise<T>,\n retries = 5,\n delay = 1000,\n): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (retries <= 0) throw err;\n await new Promise(r => setTimeout(r, delay));\n return retry(fn, retries - 1, delay * 2);\n }\n}","import { CCMConfigBuilder, mergeConfig, type CCMConfig, type MenuDirection, type MenuItem } from \"./config\";\n// import * as pug from \"pug\";\nimport centerTemplate from './templates/center.pug';\nimport menuItemTemplate from './templates/menuItem.pug';\nimport { retry } from \"./utils/utils\";\n\n/**\n * 配置化十字菜单库\n *\n * 使用方式:\n * const ccm = new CCM({ ... });\n * ccm.render();\n */\nexport class CCM {\n private _config: CCMConfig;\n private items: MenuItem[] = [];\n // private crossMenu: CrossMenu | null = null;\n private initialized = false;\n private _containerEle: HTMLElement | null = null;\n\n get config(): CCMConfig {\n return this._config;\n }\n\n set config(config: Partial<CCMConfig>) {\n this._config = { ...this._config, ...config };\n }\n\n get container() {\n // 666 isSameNode\n if (!this._containerEle || !(document.querySelector(this.config.container) as HTMLElement)?.isSameNode(this._containerEle)) {\n this._containerEle = document.querySelector(this.config.container) as HTMLElement;\n }\n return this._containerEle;\n }\n\n constructor(\n config: Partial<CCMConfig>,\n useDefaultKeyBindings: boolean = true,\n ) {\n this._config = CCMConfigBuilder(config, useDefaultKeyBindings);\n }\n\n /**\n * 渲染菜单\n * container 参数有点多余了但显式放出来又有必要\n */\n render(items: MenuItem[] = [], container?: string, config?: Partial<CCMConfig>): void {\n // 销毁旧的菜单\n try {\n this.destroy();\n } catch (err) {\n console.error('Error during previous CCM destroy:', err);\n }\n\n if (items) this.items = items;\n // 原本用的 call this,但是 ts 的重载误认为两个参数是重载 2 然后需要三个参数(?)还是直接传 this.config\n if (config) this._config = mergeConfig(config, this._config);\n if (container) this.config.container = container;\n\n if (!this.initialized) {\n this.initialized = true;\n this.updateCSS();\n this.registerParallaxEffect();\n this.registerKeyboardEvents()\n }\n\n try {\n // 渲染中心元素\n this.renderCenter();\n setTimeout(() => {\n // 渲染菜单项\n this.renderMenuItems();\n // 好奇怪为什么设 0 了都好像还是慢点……\n }, Math.max(0, this.config.style.showAnimation.center.duration - 500));\n } catch (error) {\n console.error('Error rendering CCM:', error);\n this.destroy();\n }\n }\n /**\n * 更新 CSS\n */\n updateCSS(): void {\n // throw new Error('Not implemented yet');\n this.container.classList.add('ccm-con');\n\n const head = document.head;\n const style = this.config.style;\n const styleEle = document.createElement('style');\n // styleEle.type = 'text/css'; // type 弃用\n const cl = (c: typeof style.background.menuColor) => !c ? '' : typeof c === 'string' ? c : c.light;\n const cd = (c: typeof style.background.menuColor) => !c ? '' : typeof c === 'string' ? c : (c.dark ?? c.light);\n const numOrStr = (val: string | number | undefined) => val != null ? typeof val === 'number' ? `${val}px` : val : '';\n const center = 'render' in style.center ? null : style.center;\n const v = (prop: string, val: string | number | null | undefined) => val != null && val !== '' ? ` ${prop}: ${val};` : '';\n // 这部分非常适合 vibe……\n // 确实这种数组 + '' + filter(Boolean) 的方式不错的\n styleEle.innerHTML = [\n `:root {`,\n v('--ccm-width', numOrStr(style.width)),\n v('--ccm-bg-menu-color', cl(style.background.menuColor)),\n v('--ccm-bg-center-color', cl(style.background.centerColor)),\n v('--ccm-bg-opacity', style.background.opacity),\n style.background.blur != null ? ` --ccm-bg-blur: ${style.background.blur}px;` : '',\n center?.icon?.size != null ? ` --ccm-center-icon-size: ${numOrStr(center.icon.size)};` : '',\n center?.title?.size != null ? ` --ccm-center-title-size: ${numOrStr(center.title.size)};` : '',\n center?.title?.color ? v('--ccm-center-title-color', cl(center.title.color)) : '',\n center?.subtitle?.size != null ? ` --ccm-center-subtitle-size: ${numOrStr(center.subtitle.size)};` : '',\n center?.subtitle?.color ? v('--ccm-center-subtitle-color', cl(center.subtitle.color)) : '',\n center?.style?.borderSize != null ? ` --ccm-center-border-size: ${numOrStr(center.style.borderSize)};` : '',\n center?.style?.color ? v('--ccm-center-border-color', cl(center.style.color)) : '',\n center?.style?.radius != null ? ` --ccm-center-radius: ${numOrStr(center.style.radius)};` : '',\n v('--ccm-menu-length', numOrStr(style.menu.length)),\n v('--ccm-menu-color', cl(style.menu.color)),\n style.menu.radius != null ? ` --ccm-menu-radius: ${numOrStr(style.menu.radius)};` : '',\n `}`,\n ``,\n `.dark {`,\n v('--ccm-bg-menu-color', cd(style.background.menuColor)),\n v('--ccm-bg-center-color', cd(style.background.centerColor)),\n center?.title?.color ? v('--ccm-center-title-color', cd(center.title.color)) : '',\n center?.subtitle?.color ? v('--ccm-center-subtitle-color', cd(center.subtitle.color)) : '',\n center?.style?.color ? v('--ccm-center-border-color', cd(center.style.color)) : '',\n v('--ccm-menu-color', cd(style.menu.color)),\n center?.style?.color ? v('color', cd(center.style.color)) : '',\n `}`,\n style.showAnimation.menu.durationPerItem ? `--ccm-menu-show-duration: ${style.showAnimation.menu.durationPerItem}ms` : '',\n style.showAnimation.center.duration ? `--ccm-center-show-duration: ${style.showAnimation.center.duration}ms` : '',\n ].filter(Boolean).join('\\n');\n head.appendChild(styleEle);\n\n // 引入打包 CSS?\n }\n // TODO 把 render 和 注册类 拆分出去……\n\n /**\n * 渲染菜单项\n */\n\n renderMenuItems(): void {\n // throw new Error('Not implemented yet');\n const items = this.items;\n const groupedItems = [\n items.filter(item => item.direction === 'up'),\n items.filter(item => item.direction === 'right'),\n items.filter(item => item.direction === 'down'),\n items.filter(item => item.direction === 'left'),\n ];\n const delegateFunctions: Record<string, () => void | Promise<void>> = {}\n items.forEach(item => {\n if (typeof item.action === 'function') {\n const funcHash = Math.random().toString(36).substr(2, 9);\n delegateFunctions[funcHash] = item.action;\n // @ts-expect-error bad type\n item.action = `__ccm_dispatch_func('${funcHash}')`;\n }\n });\n // var __ccm_dispatch_func = function (funcHash:string) {\n //@ts-expect-error windows has no attr\n window.__ccm_dispatch_func = function (funcHash: string) {\n // console.log(`Dispatching function for hash: ${funcHash}`);\n const func = delegateFunctions[funcHash];\n if (func) {\n func();\n } else {\n console.warn(`No function found for hash: ${funcHash}`);\n }\n };\n\n\n (async () => {\n const _delay = this.config.style.showAnimation.menu.durationPerItem ?? 0;\n for (const group of groupedItems) {\n if (group.length === 0) continue;\n await new Promise<void>(r => setTimeout(r, _delay));\n if (group.length === 1) {\n this._createMenuItem(group[0]!);\n } else if (group.length === 2) {\n this._createMenuItem(group[0]!, -20, 6);\n await new Promise<void>(r => setTimeout(r, _delay));\n this._createMenuItem(group[1]!, 20, -6);\n }\n }\n })();\n\n }\n // menuItemTemplate = pug.compileFile('/templates/menuItem.pug')\n _createMenuItem(item: MenuItem, rotateOffset = 0, x = 0) {\n const degMap = {\n up: 0,\n right: 90,\n down: 0,\n left: 270,\n };\n // 此时确实是 string……\n const menuItemHTML = menuItemTemplate({ ...item, action: item.action as unknown as string, rotate: `${degMap[item.direction] + rotateOffset}deg`, x: `${x}px` });\n this.container.insertAdjacentHTML('beforeend', menuItemHTML);\n }\n\n /**\n * 渲染中心元素\n */\n renderCenter(): void {\n // throw new Error('Not implemented yet');\n if (this.config.style.center.render && typeof this.config.style.center.render === 'function') {\n const customCenterEle = this.config.style.center.render()\n if (customCenterEle instanceof HTMLElement) {\n this.container.appendChild(customCenterEle);\n } else {\n throw new Error('Custom center render function must return an HTMLElement');\n }\n return;\n }\n const centerHtml = centerTemplate({ title: this.config.style.center.title!.content, subtitle: this.config.style.center.subtitle?.content, icon: this.config.style.center.icon?.url });\n const container = document.querySelector(this.config.container);\n if (!container) {\n throw new Error(`Container element not found for selector: ${this.config.container}`);\n }\n // if (container.hasChildNodes()) {\n // console.warn(`Container element for selector: ${this.config.container} is not empty. Existing content will be preserved.`);\n // }\n container.insertAdjacentHTML('beforeend', centerHtml);\n }\n\n /**\n * 视差效果注册\n */\n registerParallaxEffect(): void {\n const init = async () => {\n const parallaxCon = document.body;\n // const this.container = this.container;\n await retry(() => Promise.resolve(this.container), 10, 500);\n if (!parallaxCon || !this.container) {\n throw new Error('Parallax container or CCM container not found');\n };\n\n const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n const canHover = window.matchMedia('(hover: hover) and (pointer: fine)').matches;\n if (reduceMotion || !canHover) return;\n\n let latestPointerEvent: PointerEvent | null = null;\n let ticking = false;\n\n const update = () => {\n if (!latestPointerEvent) return;\n\n const rect = parallaxCon.getBoundingClientRect();\n const nx = ((latestPointerEvent.clientX - rect.left) / rect.width - 0.5) * 2;\n const ny = ((latestPointerEvent.clientY - rect.top) / rect.height - 0.5) * 2;\n\n this.container.style.setProperty('--ccm-parallax-x', `${(nx * 80).toFixed(2)}px`);\n this.container.style.setProperty('--ccm-parallax-y', `${(ny * 80).toFixed(2)}px`);\n this.container.style.setProperty('--ccm-tilt-x', `${(nx * 25).toFixed(2)}deg`);\n this.container.style.setProperty('--ccm-tilt-y', `${(-ny * 20).toFixed(2)}deg`);\n };\n\n const reset = () => {\n this.container.style.setProperty('--ccm-parallax-x', '0px');\n this.container.style.setProperty('--ccm-parallax-y', '0px');\n this.container.style.setProperty('--ccm-tilt-x', '0deg');\n this.container.style.setProperty('--ccm-tilt-y', '0deg');\n };\n\n const onPointerMove = (event: PointerEvent) => {\n latestPointerEvent = event;\n if (ticking) return;\n\n ticking = true;\n requestAnimationFrame(() => {\n update();\n ticking = false;\n });\n };\n\n const onPointerLeave = () => {\n latestPointerEvent = null;\n requestAnimationFrame(reset);\n };\n\n // 据说如果只是简单变量读写浏览器已经做了优化可以不加 requestAnimationFrame\n // 还是得加,不加动画抽搐\n parallaxCon.addEventListener('pointermove', onPointerMove, { passive: true });\n parallaxCon.addEventListener('pointerleave', onPointerLeave, { passive: true });\n };\n // 哦哦是喔已经加载完了哈哈\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init);\n } else {\n init();\n }\n }\n\n /**\n * 注册键盘事件\n */\n selectAwaitingDirection: MenuDirection | null = null;\n selectAwaitingTimer: ReturnType<typeof setTimeout> | null = null;\n selectAwaitDelayMS = 5000;\n registerKeyboardEvents(): void {\n document.addEventListener('keydown', (event) => {\n const key = event.key.toLowerCase();\n // const keyMap: { [key: string]: MenuDirection } = {\n // w: 'up',\n // arrowup: 'up',\n // d: 'right',\n // arrowright: 'right',\n // s: 'down',\n // arrowdown: 'down',\n // a: 'left',\n // arrowleft: 'left'\n // };\n\n const direction = this.config.keyBindings[key];\n if (direction) {\n event.preventDefault();\n this._ccmHandlePress(direction);\n }\n\n });\n\n }\n /**\n * 通用清理选择状态工具函数\n */\n private _clear() {\n const selectingItems = document.querySelectorAll(`.ccm-items.selecting`);\n this.selectAwaitingDirection = null;\n\n selectingItems.forEach(item => {\n item.classList.remove('selecting');\n })\n }\n private _ccmClearSelecting(delay: boolean | number = false) {\n if (this.selectAwaitingTimer) {\n clearTimeout(this.selectAwaitingTimer);\n this.selectAwaitingTimer = null;\n }\n if (delay) {\n this.selectAwaitingTimer = setTimeout(() => this._clear(), delay === true ? this.selectAwaitDelayMS : delay);\n } else {\n this._clear();\n }\n }\n /**\n * 按下事件通用处理\n */\n private _ccmHandlePress(direction: MenuDirection) {\n if (!direction) throw new Error('direction is required');\n\n // 重复点击重置选择 timer\n if (this.selectAwaitingDirection !== null) {\n if (direction === this.selectAwaitingDirection) {\n this._ccmClearSelecting(true);\n return;\n }\n\n\n if (this._ccmTriggerAwaitingSelection(direction)) {\n return;\n }\n\n this._ccmClearSelecting();\n }\n\n\n const menuItems = document.querySelectorAll(`.ccm-items.${direction}`);\n if (!menuItems) {\n return\n }\n menuItems.forEach(item => {\n item.classList.add('selecting');\n });\n // 如果有多个 up 菜单项,进入选择状态,等待用户再次点击确认选择哪个菜单项\n if (menuItems.length > 1) {\n this.selectAwaitingDirection = direction;\n this._ccmClearSelecting(true);\n } else if (menuItems.length === 1) {\n // 只有一个 up 菜单项,直接触发点击\n const item = menuItems[0]!;\n // ~~是的,有 hasChildNodes 的……\n // hasChildNodes 在有纯文本时也为 true……\n if (item.firstElementChild) {\n (item.children[0] as HTMLElement).click();\n } else {\n (item as HTMLElement).click();\n }\n\n this._ccmClearSelecting(500);\n }\n }\n private _ccmIsVertical(direction: MenuDirection | null) {\n if (!direction) return false;\n return ['up', 'down'].includes(direction);\n }\n private _ccmIsHorizontal(direction: MenuDirection | null) {\n if (!direction) return false;\n return ['left', 'right'].includes(direction);\n }\n\n private _ccmTriggerAwaitingSelection(direction: MenuDirection) {\n // if (!ccmSelectAwaitingDirection) {\n // return false;\n // }\n\n const selectingItems = document.querySelectorAll(`.ccm-items.${this.selectAwaitingDirection}`);\n if (selectingItems.length === 0) {\n return false;\n }\n\n const awaitingIsVertical = this._ccmIsVertical(this.selectAwaitingDirection);\n const canResolveByCrossAxis = awaitingIsVertical\n ? this._ccmIsHorizontal(direction)\n : this._ccmIsVertical(direction);\n\n // 点击了选择方向的对向,清除选择\n if (!canResolveByCrossAxis) {\n this._ccmClearSelecting();\n return true;\n }\n\n // 对不起不能直接借用……\n // const orderedItems = [].sort.call(selectingItems, (leftItem, rightItem) => {\n const orderedItems = Array.from(selectingItems).sort((leftItem, rightItem) => {\n const leftRect = leftItem.getBoundingClientRect();\n const rightRect = rightItem.getBoundingClientRect();\n\n return awaitingIsVertical\n ? leftRect.left - rightRect.left\n : leftRect.top - rightRect.top;\n });\n\n const targetItem = awaitingIsVertical\n ? (direction === 'left' ? orderedItems[0] : orderedItems[orderedItems.length - 1])\n : (direction === 'up' ? orderedItems[0] : orderedItems[orderedItems.length - 1]);\n\n if (!targetItem) {\n return false;\n }\n\n ((targetItem.firstElementChild ?? targetItem) as HTMLElement)?.click();\n this._ccmClearSelecting(1000);\n return true;\n }\n\n /**\n * 销毁菜单\n */\n destroy(): void {\n const container = document.querySelector(this.config.container);\n if (!container) return;\n container.innerHTML = '';\n\n this.selectAwaitingDirection = null;\n\n if (this.selectAwaitingTimer) {\n clearTimeout(this.selectAwaitingTimer);\n this.selectAwaitingTimer = null;\n }\n\n this.items = [];\n\n // document.removeEventListener('keydown', this._ccmHandlePress as any);\n // 这里没有保存事件监听函数的引用,所以无法正确移除事件监听,暂时不处理了,反正页面刷新了就没了\n // if (this.crossMenu) {\n // this.crossMenu.destroy();\n // this.crossMenu = null;\n // }\n }\n}\n\nexport type { CCMConfig };\n"],"names":["root","factory","exports","module","define","amd","globalThis","pug","locals","pug_interp","pug_html","locals_for_with","direction","icon","render","subtitle","title","attr","classes","escape","call","this","undefined","Boolean","action","bgColor","hoverOffset","label","offset","rotate","size","url","x","styleParts","filter","join","style","pug_has_own_property","Object","prototype","hasOwnProperty","pug_classes","val","escaping","Array","isArray","className","classString","padding","escapeEnabled","i","length","pug_escape","pug_classes_array","key","pug_classes_object","pug_style","out","pug_attr","escaped","terse","type","toJSON","JSON","stringify","indexOf","replace","merge","pug_merge","a","b","arguments","attrs","valA","concat","valB","obj","pug_match_html","_html","html","regexResult","exec","lastIndex","result","index","charCodeAt","substring","rethrow","pug_rethrow","err","filename","lineno","str","Error","window","message","context","lines","start","end","encoding","split","Math","max","min","ex","slice","map","line","curr","path","e","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","__esModule","d","definition","o","defineProperty","enumerable","get","prop","r","Symbol","toStringTag","value","defaultConfig","container","startingDirections","width","radius","background","menuColor","light","dark","centerColor","opacity","blur","center","content","color","menu","showAnimation","duration","durationPerItem","keyBindings","mergeConfig","config","origin","base","dc","uc","async","retry","fn","retries","delay","Promise","setTimeout","CCM","_config","_containerEle","document","querySelector","isSameNode","constructor","useDefaultKeyBindings","items","initialized","selectAwaitingDirection","selectAwaitingTimer","selectAwaitDelayMS","merged","w","s","arrowup","arrowright","arrowdown","arrowleft","CCMConfigBuilder","destroy","console","error","updateCSS","registerParallaxEffect","registerKeyboardEvents","renderCenter","renderMenuItems","classList","add","head","styleEle","createElement","cl","c","cd","numOrStr","v","innerHTML","borderSize","appendChild","groupedItems","item","delegateFunctions","forEach","funcHash","random","toString","substr","__ccm_dispatch_func","func","warn","_delay","group","_createMenuItem","rotateOffset","menuItemHTML","up","right","down","left","insertAdjacentHTML","customCenterEle","HTMLElement","centerHtml","init","parallaxCon","body","resolve","reduceMotion","matchMedia","matches","canHover","latestPointerEvent","ticking","update","rect","getBoundingClientRect","nx","clientX","ny","clientY","top","height","setProperty","toFixed","reset","addEventListener","event","requestAnimationFrame","passive","readyState","toLowerCase","preventDefault","_ccmHandlePress","_clear","selectingItems","querySelectorAll","remove","_ccmClearSelecting","clearTimeout","_ccmTriggerAwaitingSelection","menuItems","firstElementChild","children","click","_ccmIsVertical","includes","_ccmIsHorizontal","awaitingIsVertical","orderedItems","from","sort","leftItem","rightItem","leftRect","rightRect","targetItem"],"sourceRoot":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type CCMConfig, type MenuDirection, type MenuItem } from "./config";
|
|
2
|
+
/**
|
|
3
|
+
* 配置化十字菜单库
|
|
4
|
+
*
|
|
5
|
+
* 使用方式:
|
|
6
|
+
* const ccm = new CCM({ ... });
|
|
7
|
+
* ccm.render();
|
|
8
|
+
*/
|
|
9
|
+
export declare class CCM {
|
|
10
|
+
private _config;
|
|
11
|
+
private items;
|
|
12
|
+
private initialized;
|
|
13
|
+
private _containerEle;
|
|
14
|
+
get config(): CCMConfig;
|
|
15
|
+
set config(config: Partial<CCMConfig>);
|
|
16
|
+
get container(): HTMLElement;
|
|
17
|
+
constructor(config: Partial<CCMConfig>, useDefaultKeyBindings?: boolean);
|
|
18
|
+
/**
|
|
19
|
+
* 渲染菜单
|
|
20
|
+
* container 参数有点多余了但显式放出来又有必要
|
|
21
|
+
*/
|
|
22
|
+
render(items?: MenuItem[], container?: string, config?: Partial<CCMConfig>): void;
|
|
23
|
+
/**
|
|
24
|
+
* 更新 CSS
|
|
25
|
+
*/
|
|
26
|
+
updateCSS(): void;
|
|
27
|
+
/**
|
|
28
|
+
* 渲染菜单项
|
|
29
|
+
*/
|
|
30
|
+
renderMenuItems(): void;
|
|
31
|
+
_createMenuItem(item: MenuItem, rotateOffset?: number, x?: number): void;
|
|
32
|
+
/**
|
|
33
|
+
* 渲染中心元素
|
|
34
|
+
*/
|
|
35
|
+
renderCenter(): void;
|
|
36
|
+
/**
|
|
37
|
+
* 视差效果注册
|
|
38
|
+
*/
|
|
39
|
+
registerParallaxEffect(): void;
|
|
40
|
+
/**
|
|
41
|
+
* 注册键盘事件
|
|
42
|
+
*/
|
|
43
|
+
selectAwaitingDirection: MenuDirection | null;
|
|
44
|
+
selectAwaitingTimer: ReturnType<typeof setTimeout> | null;
|
|
45
|
+
selectAwaitDelayMS: number;
|
|
46
|
+
registerKeyboardEvents(): void;
|
|
47
|
+
/**
|
|
48
|
+
* 通用清理选择状态工具函数
|
|
49
|
+
*/
|
|
50
|
+
private _clear;
|
|
51
|
+
private _ccmClearSelecting;
|
|
52
|
+
/**
|
|
53
|
+
* 按下事件通用处理
|
|
54
|
+
*/
|
|
55
|
+
private _ccmHandlePress;
|
|
56
|
+
private _ccmIsVertical;
|
|
57
|
+
private _ccmIsHorizontal;
|
|
58
|
+
private _ccmTriggerAwaitingSelection;
|
|
59
|
+
/**
|
|
60
|
+
* 销毁菜单
|
|
61
|
+
*/
|
|
62
|
+
destroy(): void;
|
|
63
|
+
}
|
|
64
|
+
export type { CCMConfig };
|
|
65
|
+
//# sourceMappingURL=crossMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crossMenu.d.ts","sourceRoot":"","sources":["../src/crossMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAM5G;;;;;;GAMG;AACH,qBAAa,GAAG;IACd,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,KAAK,CAAkB;IAE/B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAA4B;IAEjD,IAAI,MAAM,IAAI,SAAS,CAEtB;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,EAEpC;IAED,IAAI,SAAS,gBAMZ;gBAGC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,EAC1B,qBAAqB,GAAE,OAAc;IAKvC;;;OAGG;IACH,MAAM,CAAC,KAAK,GAAE,QAAQ,EAAO,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI;IAiCrF;;OAEG;IACH,SAAS,IAAI,IAAI;IAqDjB;;OAEG;IAEH,eAAe,IAAI,IAAI;IAgDvB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,SAAI,EAAE,CAAC,SAAI;IAYvD;;OAEG;IACH,YAAY,IAAI,IAAI;IAsBpB;;OAEG;IACH,sBAAsB,IAAI,IAAI;IAiE9B;;OAEG;IACH,uBAAuB,EAAE,aAAa,GAAG,IAAI,CAAQ;IACrD,mBAAmB,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAQ;IACjE,kBAAkB,SAAQ;IAC1B,sBAAsB,IAAI,IAAI;IAuB9B;;MAEE;IACF,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACH,OAAO,CAAC,eAAe;IA4CvB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,4BAA4B;IA6CpC;;OAEG;IACH,OAAO,IAAI,IAAI;CAqBhB;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CCMConfig } from './config';
|
|
2
|
+
import './styles/index.scss';
|
|
3
|
+
/**
|
|
4
|
+
* 配置化十字菜单库
|
|
5
|
+
*
|
|
6
|
+
* 使用方式:
|
|
7
|
+
* const ccm = new CCM({ ... });
|
|
8
|
+
* ccm.render();
|
|
9
|
+
*/
|
|
10
|
+
export { CCM } from './crossMenu';
|
|
11
|
+
export type { CCMConfig };
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAiB,MAAM,UAAU,CAAC;AAC3E,OAAO,qBAAqB,CAAA;AAC5B;;;;;;GAMG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,YAAY,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AAAA,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,SAAI,EACX,KAAK,SAAO,GACX,OAAO,CAAC,CAAC,CAAC,CAQZ"}
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@woisol-g/configurable-cross-menu",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A highly configurable animated cross menu for web pages.",
|
|
5
|
+
"main": "dist/configurable-cross-menu.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"style": "dist/configurable-cross-menu.css",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/configurable-cross-menu.js"
|
|
12
|
+
},
|
|
13
|
+
"./styles.css": "./dist/configurable-cross-menu.css",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"README.en.md",
|
|
20
|
+
"ENGINEERING_REVIEW.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "pnpm run build:types && pnpm run build:bundle",
|
|
25
|
+
"build:bundle": "webpack",
|
|
26
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"dev": "webpack --watch",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"storybook": "storybook dev -p 6006",
|
|
32
|
+
"build-storybook": "storybook build",
|
|
33
|
+
"ci": "pnpm typecheck && pnpm build && pnpm test && pnpm build-storybook && npm pack --dry-run --cache .npm-cache",
|
|
34
|
+
"prepublishOnly": "pnpm build",
|
|
35
|
+
"prepare": "husky"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"animation",
|
|
39
|
+
"context-menu",
|
|
40
|
+
"cross-menu",
|
|
41
|
+
"menu",
|
|
42
|
+
"typescript",
|
|
43
|
+
"ui",
|
|
44
|
+
"umd"
|
|
45
|
+
],
|
|
46
|
+
"author": "Woisol",
|
|
47
|
+
"license": "ISC",
|
|
48
|
+
"homepage": "https://github.com/Woisol/ConfigurableCrossMenu#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/Woisol/ConfigurableCrossMenu/issues"
|
|
51
|
+
},
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/Woisol/ConfigurableCrossMenu.git"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"registry": "https://registry.npmjs.org/"
|
|
59
|
+
},
|
|
60
|
+
"packageManager": "pnpm@10.7.0",
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@storybook/addon-essentials": "8.6.14",
|
|
63
|
+
"@storybook/html": "8.6.14",
|
|
64
|
+
"@storybook/html-webpack5": "8.6.14",
|
|
65
|
+
"@types/pug": "^2.0.10",
|
|
66
|
+
"css-loader": "^7.1.4",
|
|
67
|
+
"jsdom": "^29.0.0",
|
|
68
|
+
"mini-css-extract-plugin": "^2.10.1",
|
|
69
|
+
"pug-loader": "^2.4.0",
|
|
70
|
+
"sass": "^1.98.0",
|
|
71
|
+
"sass-loader": "^16.0.7",
|
|
72
|
+
"storybook": "8.6.14",
|
|
73
|
+
"style-loader": "^4.0.0",
|
|
74
|
+
"ts-loader": "^9.5.4",
|
|
75
|
+
"typescript": "^5.9.3",
|
|
76
|
+
"vitest": "^4.1.0",
|
|
77
|
+
"webpack": "^5.105.4",
|
|
78
|
+
"webpack-cli": "^7.0.0"
|
|
79
|
+
},
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"husky": "^9.1.7",
|
|
82
|
+
"pug": "^3.0.4"
|
|
83
|
+
}
|
|
84
|
+
}
|