@linkdesk/ui 0.1.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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/button/Button.d.ts +19 -0
- package/dist/color-picker/ColorPicker.d.ts +51 -0
- package/dist/combobox/Combobox.d.ts +30 -0
- package/dist/context-menu/ContextMenu.d.ts +37 -0
- package/dist/file-path-input/FilePathInput.d.ts +15 -0
- package/dist/font-family-select/FontFamilySelect.d.ts +19 -0
- package/dist/form-row/FormRow.d.ts +7 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1629 -0
- package/dist/inline-input/InlineInput.d.ts +38 -0
- package/dist/number-input/NumberInput.d.ts +18 -0
- package/dist/overlay-portal/OverlayPortal.d.ts +27 -0
- package/dist/plugin-icon/PluginIcon.d.ts +24 -0
- package/dist/plugin-icon/iconUtils.d.ts +38 -0
- package/dist/segmented-radio/SegmentedRadio.d.ts +30 -0
- package/dist/select-box/DynamicSelect.d.ts +25 -0
- package/dist/select-box/SelectBox.d.ts +29 -0
- package/dist/select-box/SelectBoxDropdown.d.ts +35 -0
- package/dist/slider/Slider.d.ts +22 -0
- package/dist/slider/sliderStep.d.ts +10 -0
- package/dist/theme-picker/ThemePicker.d.ts +17 -0
- package/dist/toggle/Toggle.d.ts +7 -0
- package/package.json +50 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineInput —— 归一化行内编辑组件。
|
|
3
|
+
* E5#18a:全项目所有行内编辑用一个组件——FileTree rename / 串口会话 / 设置 / 快捷键。
|
|
4
|
+
*
|
|
5
|
+
* 对标 E4V#27 两阶段聚焦模式 + isActive guard 防 Enter/Blur 竞态。
|
|
6
|
+
* 对标 VS Code input — compact 22px / normal 32px 两套尺寸走 CSS 变量。
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
export interface InlineInputProps {
|
|
10
|
+
/** 尺寸——compact=22px 文件树行内 / normal=32px 设置/串口 */
|
|
11
|
+
size: "compact" | "normal";
|
|
12
|
+
/** 当前值 */
|
|
13
|
+
value: string;
|
|
14
|
+
/** 确认回调——Enter 或 blur 时调用 */
|
|
15
|
+
onConfirm: (value: string) => void;
|
|
16
|
+
/** 取消回调——Esc 时调用 */
|
|
17
|
+
onCancel: () => void;
|
|
18
|
+
/** 即时回调——每次按键都通知(搜索框等实时过滤场景) */
|
|
19
|
+
onChange?: (value: string) => void;
|
|
20
|
+
/** 选中模式——all=全选 / nameOnly=只选文件名(去扩展名) */
|
|
21
|
+
selectMode?: "all" | "nameOnly";
|
|
22
|
+
/** 宽度——默认 fill(撑满父容器) */
|
|
23
|
+
width?: number;
|
|
24
|
+
/** 自动 focus + 选文本 */
|
|
25
|
+
autoFocus?: boolean;
|
|
26
|
+
/** placeholder */
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
/** 输入类型 */
|
|
29
|
+
type?: "text" | "number";
|
|
30
|
+
/** 数字类型的 min/max */
|
|
31
|
+
min?: number;
|
|
32
|
+
max?: number;
|
|
33
|
+
}
|
|
34
|
+
/** 暴露给父组件的 imperative handle——外部按钮读当前值 */
|
|
35
|
+
export interface InlineInputHandle {
|
|
36
|
+
getValue(): string;
|
|
37
|
+
}
|
|
38
|
+
export declare const InlineInput: React.ForwardRefExoticComponent<InlineInputProps & React.RefAttributes<InlineInputHandle>>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NumberInput——带 +/- 步进按钮的数字输入。
|
|
3
|
+
* E5#57: 替代原生 <input type="number"> spinner——暗色/亮色主题统一外观。
|
|
4
|
+
*
|
|
5
|
+
* 壳侧实现——uiHint: "fontSize" 与普通 number 配置项共用此组件。
|
|
6
|
+
*/
|
|
7
|
+
interface NumberInputProps {
|
|
8
|
+
value: number;
|
|
9
|
+
onChange: (v: number) => void;
|
|
10
|
+
min?: number;
|
|
11
|
+
max?: number;
|
|
12
|
+
step?: number;
|
|
13
|
+
/** E5.8 Phase 12 #161:数值单位后缀(如 "%")——schema unit 声明走 i18n(F10);空 = 裸数值 */
|
|
14
|
+
unit?: string;
|
|
15
|
+
style?: React.CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
export default function NumberInput({ value, onChange, min, max, step, unit, style }: NumberInputProps): import("react").JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** E5.8#107:遮罩 portal 目标——scrim-plane 存在(池内浮层权威)用层平面;壳侧无此平面回退 body。
|
|
2
|
+
* 遮罩满屏无磨砂,与 surface 平面分离后结构隔离地板(:not(#ld-scrim-plane))天然不碰它。 */
|
|
3
|
+
export declare function getScrimTarget(): HTMLElement;
|
|
4
|
+
interface OverlayPortalProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
/** E5.7#100:活跃守卫——false 时所有窗口级监听器挂起(防"挂载但隐藏"态回调泄漏,
|
|
7
|
+
* 硬约束 14)。条件挂载({open && <OverlayPortal/>})的消费方可省略——默认 true */
|
|
8
|
+
open?: boolean;
|
|
9
|
+
/** 外部点击 / Escape → 关闭回调。不传则无外部关闭行为(如 ToastContainer) */
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
/** 点击此 ref 指向的元素不算"外部"(如触发按钮)。每次事件回调内实时读,不缓存 */
|
|
12
|
+
triggerRef?: React.RefObject<HTMLElement>;
|
|
13
|
+
/** Tab/Shift+Tab 在 overlay 内循环。首次渲染自动 focus 第一个可聚焦元素 */
|
|
14
|
+
trapFocus?: boolean;
|
|
15
|
+
/** 叠层 token——透传到 wrapper 的 z-index。
|
|
16
|
+
* 🔥 契约(E5.8#111):z-index/filter/backdrop-filter/transform 会给包装盒建 stacking context,
|
|
17
|
+
* 内部 fixed 内容的自身 z-index 被困其中、对外只算包装盒层级。scrim 遮罩(--z-overlay-backdrop 500)
|
|
18
|
+
* 在同一层上下文竞争 → 包装盒 z-index 必须 > 500(ContextMenu→Z_INDEX.contextMenu 即此契约),
|
|
19
|
+
* 否则面板内容被遮罩盖住 = 点一下自己退。 */
|
|
20
|
+
zIndex?: string;
|
|
21
|
+
/** E5.7#14:portal 目标 root id——归一化到 FloatingLayerHost 的 portal root。
|
|
22
|
+
* E5.8#107 默认改 #overlay-root(浮层权威通用 surface 根);root 不存在(壳 DOM 场景)
|
|
23
|
+
* 自动回退 body。显式传 rootId(ContextMenu→context-menu-root)不受影响。 */
|
|
24
|
+
rootId?: string;
|
|
25
|
+
}
|
|
26
|
+
export default function OverlayPortal({ children, onClose, triggerRef, trapFocus, zIndex, rootId, open }: OverlayPortalProps): import("react").ReactPortal;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginIcon — 统一插件图标渲染组件(@linkdesk/ui)。
|
|
3
|
+
* E2c #19j:图标栏 / 标签栏 / 欢迎页 / [+] 菜单 共用同一个图标来源(plugin.json icon 字段)。
|
|
4
|
+
* E5#100:Lucide 优先 → codicon → img → emoji(@deprecated)。
|
|
5
|
+
*
|
|
6
|
+
* E6#54b 解耦(审计 §三 项 1):manifest 数据注入取代 viewRegistry 特权查询——
|
|
7
|
+
* 原实现 import getViewPlugin(壳 viewRegistry 查询 = 特权假设,池内空 loader → 恒 emoji)+
|
|
8
|
+
* resolvePluginIcon(core)。现在纯 props 契约:图标只由 `manifest` 声明字段裁决
|
|
9
|
+
* (硬约束 11 插件身份 = manifest 声明字段),谁有 manifest 谁传,无 manifest → emoji 兜底。
|
|
10
|
+
*
|
|
11
|
+
* 消费端用法(作者持 manifest 时传 manifest,如插件列表/详情条目):
|
|
12
|
+
* <PluginIcon pluginId="terminal" manifest={plugin.manifest} className="my-icon-class" />
|
|
13
|
+
* manifest 缺省 → emoji 兜底(池内无 manifest 的历史条目沿用现状)。
|
|
14
|
+
*/
|
|
15
|
+
import { type ManifestIconShape } from "./iconUtils";
|
|
16
|
+
interface PluginIconProps {
|
|
17
|
+
pluginId: string;
|
|
18
|
+
/** 插件 manifest(或其含 icon/iconSource 的子集)——图标唯一裁决来源;缺省 → emoji 兜底 */
|
|
19
|
+
manifest?: ManifestIconShape;
|
|
20
|
+
className?: string;
|
|
21
|
+
alt?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function PluginIcon({ pluginId, manifest, className, alt }: PluginIconProps): import("react").JSX.Element;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件图标解析 —— 纯函数(manifest → ResolvedIcon),@linkdesk/ui 图标渲染唯一真相源。
|
|
3
|
+
* E6#54b:自 src/core/utils/plugin/iconUtils.ts 搬入 shared(随包分发)——解耦审计 §三 项 1:
|
|
4
|
+
* 原居 core 时是 PluginIcon(共享组件)的 @src/core import 违规点;函数本体纯(只读 manifest
|
|
5
|
+
* 的 icon/iconSource,零运行时依赖、零 window.linkdesk 调用),搬包零行为变化。
|
|
6
|
+
*
|
|
7
|
+
* 消费方:
|
|
8
|
+
* - shared PluginIcon.tsx(组件内 resolvePluginIcon(pluginId, manifest))
|
|
9
|
+
* - 壳 usePoolSync(iconbar/windowLayout 序列化进 pool 布局前先解析)——两处壳消费维持同源,
|
|
10
|
+
* pool/PluginIcon 不 import pluginLoader/viewRegistry(特权假设已除)。
|
|
11
|
+
*
|
|
12
|
+
* 设计依据:[[phase4-design-decisions]] §16 + VS Code extension icon 解析(manifest.icon + galleryBanner)
|
|
13
|
+
*/
|
|
14
|
+
import type { PluginManifest } from "@linkdesk/contracts";
|
|
15
|
+
export interface ResolvedIcon {
|
|
16
|
+
/** Lucide 图标名——iconSource: "lucide" 时返回 "Package" / "Folder" 等 */
|
|
17
|
+
lucide?: string;
|
|
18
|
+
/** img src——非 codicon 图标时返回 linkdesk:// 协议路径 */
|
|
19
|
+
src?: string;
|
|
20
|
+
/** codicon CSS class——codicon 图标时返回 "codicon-xxx" */
|
|
21
|
+
codicon?: string;
|
|
22
|
+
/** emoji fallback——无 lucide/src/codicon 时用 */
|
|
23
|
+
emoji?: string;
|
|
24
|
+
}
|
|
25
|
+
/** manifest 中图标相关的最小结构——函数只消费这两字段,任何 manifest 形状(完整 PluginManifest /
|
|
26
|
+
* IPC 序列化子集 / 插件列表条目)结构兼容均可直接传入,零跨包类型漂移。 */
|
|
27
|
+
export type ManifestIconShape = Pick<PluginManifest, "icon" | "iconSource">;
|
|
28
|
+
/**
|
|
29
|
+
* 从 manifest 解析图标。
|
|
30
|
+
* - iconSource: "lucide" → { lucide: icon }
|
|
31
|
+
* - iconSource: "codicon" → { codicon: "codicon-{icon}" }
|
|
32
|
+
* - iconSource: "svg" | "url" → { src: icon(直接当 URL 用)}
|
|
33
|
+
* - 无 iconSource → { src: "linkdesk://{pluginId}/{icon}" }
|
|
34
|
+
* - icon 含 "." → 当完整文件名(如 "icon.svg")
|
|
35
|
+
* - icon 不含 "." → 自动加 .png(如 "icon" → "icon.png")
|
|
36
|
+
* - 全无 → { emoji: "📄" }
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolvePluginIcon(pluginId: string, manifest: ManifestIconShape): ResolvedIcon;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SegmentedRadio——分段单选(ghost 单选按钮组)。E5.8#99 归一化:fontTone(#91)+ accentSource(#98)
|
|
3
|
+
* 两处重复的分段控件收敛为壳共享控件(对标 Slider #50.9 先例——多消费方控件进 shared/)。
|
|
4
|
+
*
|
|
5
|
+
* 按钮双轨制(E5.8#99 声明化,#2):选择/分组 = 低调 ghost 分段(本组件);动作/提交 = 实心 accent
|
|
6
|
+
* (壳 Button 组件——原 .settings-action-btn 收编)——对标 VS Code:radio group 低调、动作按钮显眼。
|
|
7
|
+
*
|
|
8
|
+
* 形态:transparent 底 + 1px separator 边框 + var(--radius-sm) 圆角;hover = border-muted + hover-overlay;
|
|
9
|
+
* 选中 = accent 描边 + color-mix accent 12% 微着色(Navigation/Active State——选中档一目了然)。
|
|
10
|
+
* 无线电语义:role radiogroup/radio + aria-checked + roving tabindex(←→↑↓ 换档,对标 #91)。
|
|
11
|
+
* preview:可选段内预览(fontTone 双字系方块 / accentSource 色块)——内容与几何消费方自持。
|
|
12
|
+
*/
|
|
13
|
+
export interface SegmentedRadioOption {
|
|
14
|
+
value: string;
|
|
15
|
+
/** 段显示标签(消费方已截短,如 enumDescription "—" 前段) */
|
|
16
|
+
label: string;
|
|
17
|
+
/** tooltip(完整解释,缺省 = label) */
|
|
18
|
+
title?: string;
|
|
19
|
+
/** 可选段内预览(swatch/图标)——内容消费方自持 */
|
|
20
|
+
preview?: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
interface SegmentedRadioProps {
|
|
23
|
+
options: SegmentedRadioOption[];
|
|
24
|
+
value: string;
|
|
25
|
+
onChange: (v: string) => void;
|
|
26
|
+
/** 无障碍 radiogroup 标签(读屏) */
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
}
|
|
29
|
+
declare function SegmentedRadio({ options, value, onChange, ariaLabel }: SegmentedRadioProps): import("react").JSX.Element;
|
|
30
|
+
export default SegmentedRadio;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicSelect——动态选项下拉(E5.8#50.23)。
|
|
3
|
+
* uiHint "select" + optionsFrom:选项不在配置注册表静态 enum,渲染时调 window.linkdesk.theme.listRecipes() 动态取。
|
|
4
|
+
* - optionsFrom "theme.colorways":活动配方(app.theme)的配色变体,选项带预览色块(ColorwayMeta.preview.accent);
|
|
5
|
+
* 随 app.theme 变化重取(configuration.onChange 订阅)+ 下拉打开时刷新(SelectBox onOpen)。
|
|
6
|
+
* - optionsFrom "theme.sources":混搭来源——「跟随主题」置顶 + 按 optionsFromDomain 过滤 RecipeMeta.domains 的配方。
|
|
7
|
+
* - E5.8#82 双语义(app.themeColor):schema 静态声明 optionsFrom="theme.colorways"+optionsFromDomain="colors",
|
|
8
|
+
* 运行时按 app.appearanceMode 切换——跟随主题模式 = 配方内配色变体;自定义模式 = colors 域来源(sources+colors)。
|
|
9
|
+
* 仅此处自解析(renderControl 是纯函数不能用 hook、SettingRow 线程化会造条件 hook)——壳设置 UI 零改动复用。
|
|
10
|
+
* 视觉沿用 SelectBox(含色块 swatch);零 @src/core import(共享控件白名单)——数据全走 window.linkdesk.*。
|
|
11
|
+
*/
|
|
12
|
+
interface DynamicSelectProps {
|
|
13
|
+
/** 当前配置值(受控) */
|
|
14
|
+
value: string;
|
|
15
|
+
onChange: (v: string) => void;
|
|
16
|
+
/** 动态数据源:"theme.colorways" | "theme.sources" */
|
|
17
|
+
optionsFrom: string;
|
|
18
|
+
/** 混搭来源域过滤——optionsFrom "theme.sources" 时按此域过滤(colors/font/radius/glass/background/surface) */
|
|
19
|
+
domain?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
}
|
|
24
|
+
declare function DynamicSelect({ value, onChange, optionsFrom, domain, disabled, placeholder, title }: DynamicSelectProps): import("react").JSX.Element | null;
|
|
25
|
+
export default DynamicSelect;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelectBox——归一化下拉组件,替代所有原生 <select>。
|
|
3
|
+
*
|
|
4
|
+
* 对标 VS Code SelectBox(src/vs/base/browser/ui/selectBox/)。
|
|
5
|
+
* 原生 <select> 在 Electron 里走独立 OS 渲染通道——跟 Chromium 合成器不同步,
|
|
6
|
+
* 异步 onApply 时态时序错乱。自定义组件走 React 状态,完全可控。
|
|
7
|
+
*
|
|
8
|
+
* 归一化:全局一个 SelectBox——不同页面/插件传不同的 options/value/onChange。
|
|
9
|
+
* 新插件用下拉 → import SelectBox from "@src/components/shared/select-box/SelectBox"。
|
|
10
|
+
*/
|
|
11
|
+
interface SelectBoxOption {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
/** E5.8#50.23:选项预览色块(如配色变体的 accent)——触发器与下拉项显示前置色点 */
|
|
15
|
+
preview?: string;
|
|
16
|
+
}
|
|
17
|
+
interface SelectBoxProps {
|
|
18
|
+
value: string;
|
|
19
|
+
options: string[] | SelectBoxOption[];
|
|
20
|
+
onChange: (v: string) => void;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
/** 下拉面板打开时回调——用于刷新动态选项列表(如串口热插拔) */
|
|
26
|
+
onOpen?: () => void;
|
|
27
|
+
}
|
|
28
|
+
declare function SelectBox({ value, options, onChange, disabled, placeholder, title, className, onOpen }: SelectBoxProps): import("react").JSX.Element;
|
|
29
|
+
export default SelectBox;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelectBoxDropdown——SelectBox/Combobox 共用下拉面板。E5.8#30.17。
|
|
3
|
+
*
|
|
4
|
+
* 定位 + Portal + 列表骨架一处写(jscpd 0 克隆门禁:两组件不再各写一份 dropdown 结构)。
|
|
5
|
+
* 视觉走 SelectBox.css 的 .selectbox-dropdown/list 类——一个视觉语言一处写。
|
|
6
|
+
* 选项渲染(.selectbox-item)由宿主提供——SelectBox 与 Combobox 的交互模型不同
|
|
7
|
+
* (点击 select vs 输入过滤),候选渲染留在各自组件。
|
|
8
|
+
*
|
|
9
|
+
* E5.8#69:定位改 effect 量测(post-paint)+ resize/scroll 重算,非渲染期同步读 rect——
|
|
10
|
+
* 渲染期读是快照:① 滚动后面板钉旧坐标脱附(悬浮在触发器外);② 任意 re-render
|
|
11
|
+
* (悬停 focusIdx / 异步选项到达 / 搜索输入)重读 rect → 面板跳变 → mousedown/mouseup
|
|
12
|
+
* 目标错位 → 点击落空「有时选不了 / 点不了」(bug 8/10 实机复现根因)。改后:
|
|
13
|
+
* 面板跟随触发器滚动(scroll 捕获阶段收任意滚动容器),re-render 用稳定 position 状态不重读。
|
|
14
|
+
*
|
|
15
|
+
* 量测时序:layout effect 同步量测(paint 前,无闪烁)。但 React commit 是叶子先序——
|
|
16
|
+
* 若触发器与下拉**同一 commit** 挂载(如测试 harness 一次性 render),祖先 host 的 ref
|
|
17
|
+
* 在子组件 layout effect 之后才 attach → 当时读到 null。生产 SelectBox 是 {open && <Dropdown/>}
|
|
18
|
+
* 条件后挂载(单独 commit,ref 早已就绪)不受影响;这里再补一个 passive effect 兜底
|
|
19
|
+
* (全 commit 后 ref 已 attach),挂载时序无关化——消费方无需知道"必须先挂触发器"契约。
|
|
20
|
+
*/
|
|
21
|
+
import type { KeyboardEvent as ReactKeyboardEvent, ReactNode, RefObject } from "react";
|
|
22
|
+
interface SelectBoxDropdownProps {
|
|
23
|
+
/** 触发容器——定位锚点(面板对齐其下缘) */
|
|
24
|
+
containerRef: RefObject<HTMLElement>;
|
|
25
|
+
/** 外部点击/Escape 关闭(OverlayPortal 统一处理) */
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
/** 键盘导航(Enter/方向键)——宿主组件实现(选项模型不同) */
|
|
28
|
+
onKeyDown?: (e: ReactKeyboardEvent) => void;
|
|
29
|
+
listRef?: RefObject<HTMLUListElement>;
|
|
30
|
+
/** 可选搜索区(SelectBox >8 选项时)——渲染在列表上方 */
|
|
31
|
+
search?: ReactNode;
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}
|
|
34
|
+
export default function SelectBoxDropdown({ containerRef, onClose, onKeyDown, listRef, search, children }: SelectBoxDropdownProps): import("react").JSX.Element;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slider——通用滑杆控件(E5.8#50.9)。
|
|
3
|
+
* 外观设置第一个消费者(#50.10 玻璃五配置滑杆),插件以后也要用——壳先行建设通用 API 面。
|
|
4
|
+
* 视觉对标 mockup 帧 3(01-图片玻璃态-mockup.html .slider:4px 圆角轨道 + accent 填充 + 12px 圆点 thumb)。
|
|
5
|
+
* 实现:原生 <input type="range"> 样式化——Chromium 免费给拖拽/键盘(方向键/Home/End/PageUp/PageDown)/
|
|
6
|
+
* 无障碍(原生 range = role="slider" + aria-valuemin/max/now),不手写 pointer 拖拽状态机(B 类 bug 高发区)。
|
|
7
|
+
* 填充 = inline `--slider-pct` CSS 变量 → linear-gradient(mockup fill 语义,见 Slider.css)。
|
|
8
|
+
* prefers-reduced-motion:原生 range 无动画,天然满足(无 transition 可禁)。
|
|
9
|
+
*/
|
|
10
|
+
interface SliderProps {
|
|
11
|
+
value: number;
|
|
12
|
+
onChange: (v: number) => void;
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
step?: number;
|
|
16
|
+
/** 无障碍标签(设置行 label 由 SettingRow 显示,此处供读屏) */
|
|
17
|
+
ariaLabel?: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
}
|
|
21
|
+
declare function Slider({ value, onChange, min, max, step, ariaLabel, disabled, style }: SliderProps): import("react").JSX.Element;
|
|
22
|
+
export default Slider;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E5.8#65:滑杆 step 推导——renderControl slider 分支选择 step。
|
|
3
|
+
* 背景:bug 2/3——glassOpacity(0-1)/surfaceRadius(0.5-2) 滑杆只出端点值,
|
|
4
|
+
* 根因 = renderControl 不传 step → Slider 默认 step=1 → 浮点区间中间值不可达。
|
|
5
|
+
* 规则:端点非整数(浮点区间)或窄跨度(max-min ≤ 2)→ 0.01 连续可调;
|
|
6
|
+
* 整数宽区间 → 1(现状整数步,零回归)。
|
|
7
|
+
* 端点全整数但跨度小也必须连续(玻璃不透明度 0-1 即此类)——span 判据兜住。
|
|
8
|
+
* 第三方可在 plugin.json contributes.configuration 显式声明 step 覆盖本推导(ConfigProperty.step)。
|
|
9
|
+
*/
|
|
10
|
+
export declare function inferSliderStep(min: number, max: number): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThemePicker——主题配方卡片选择器(E5.8#50.22)。
|
|
3
|
+
* 卡片网格:配方名称 + 配色徽标(单配色 = 预览条 / 多配色 = 配色圆点 + 计数)+ 选中态 + 键盘可达。
|
|
4
|
+
* 数据源 = window.linkdesk.theme.listRecipes()(06 §2 RecipeMeta——id/名称/明暗/配色预览色)。
|
|
5
|
+
* 视觉对标 mockup 01-设置页-主题区(.theme-picker 四列网格 + .theme-card 边框/悬停上浮/选中 accent 描边)。
|
|
6
|
+
* 受控组件:value = 当前 app.theme 值;点卡片 → onChange(recipeId)(上层写配置 → onApply 应用配方)。
|
|
7
|
+
* 键盘:方向键在卡片间移动焦点(roving tabindex)+ Enter/Space 激活(原生 button)。
|
|
8
|
+
* 预览区/圆点色 = 配方数据(inline style)——非样式硬编码(同 renderControl 色块先例);
|
|
9
|
+
* 结构样式全走 CSS 变量(硬约束 1)。
|
|
10
|
+
*/
|
|
11
|
+
interface ThemePickerProps {
|
|
12
|
+
/** 当前选中配方 id(app.theme 配置值) */
|
|
13
|
+
value: string;
|
|
14
|
+
onChange: (v: string) => void;
|
|
15
|
+
}
|
|
16
|
+
declare function ThemePicker({ value, onChange }: ThemePickerProps): import("react").JSX.Element;
|
|
17
|
+
export default ThemePicker;
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@linkdesk/ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "@linkdesk/ui——LinkDesk 共享 UI 组件(右键/下拉/开关/取色器等)。壳 src/components/shared 单一源码经构建编译,插件作者 npm i 后与内置插件同款、自动跟随主题/玻璃的界面。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./index.css": "./dist/index.css",
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": [
|
|
21
|
+
"**/*.css"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "node scripts/build.mjs",
|
|
28
|
+
"typecheck": "tsc -p tsconfig.decl.json --noEmit --emitDeclarationOnly false"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": "^18.0.0",
|
|
32
|
+
"react-dom": "^18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@linkdesk/contracts": "^0.1.0",
|
|
36
|
+
"i18next": "^26.3.6",
|
|
37
|
+
"react-i18next": "^17.0.10"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"lucide-react": "^1.29.0",
|
|
41
|
+
"react": "^18.3.1",
|
|
42
|
+
"react-dom": "^18.3.1"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"linkdesk",
|
|
46
|
+
"ui",
|
|
47
|
+
"components"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT"
|
|
50
|
+
}
|