iter(g181): 控件高度统一三档
This commit is contained in:
+21
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T09:17:08.488Z",
|
"generatedAt": "2026-10-26T09:29:34.397Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -343,6 +343,11 @@
|
|||||||
"t": "() => void",
|
"t": "() => void",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "点击回调"
|
"d": "点击回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })",
|
"example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })",
|
||||||
@@ -399,6 +404,11 @@
|
|||||||
"n": "maxCount",
|
"n": "maxCount",
|
||||||
"t": "number",
|
"t": "number",
|
||||||
"d": "字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限"
|
"d": "字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
||||||
@@ -462,6 +472,11 @@
|
|||||||
"n": "suffix",
|
"n": "suffix",
|
||||||
"t": "string | HTMLElement",
|
"t": "string | HTMLElement",
|
||||||
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
||||||
@@ -736,6 +751,11 @@
|
|||||||
"t": "(value: string) => void",
|
"t": "(value: string) => void",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "选中回调"
|
"d": "选中回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], value: city, placeholder: '选择城市…' })",
|
"example": "const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], value: city, placeholder: '选择城市…' })",
|
||||||
|
|||||||
@@ -456,6 +456,17 @@ const content = h(
|
|||||||
inputNumber({ value: qty, disabled: locked }),
|
inputNumber({ value: qty, disabled: locked }),
|
||||||
inputNumber({ value: amount, min: 0, prefix: '¥', suffix: '件' }),
|
inputNumber({ value: amount, min: 0, prefix: '¥', suffix: '件' }),
|
||||||
),
|
),
|
||||||
|
h('div', { class: 'out' }, '↓ 控件高度三档并排(sm / md / lg):button / textField / select / inputNumber 统一走 --ua-control-h-*'),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{ class: 'row' },
|
||||||
|
button({ label: '小号', size: 'sm' }), button({ label: '默认' }), button({ label: '大号', size: 'lg' }),
|
||||||
|
textField({ placeholder: 'sm', size: 'sm' }), textField({ placeholder: 'md' }), textField({ placeholder: 'lg', size: 'lg' }),
|
||||||
|
select({ options: [{ value: 'sm', label: 'sm' }, { value: 'md', label: 'md' }, { value: 'lg', label: 'lg' }], value: signal('md'), size: 'sm' }),
|
||||||
|
select({ options: [{ value: 'a', label: '选项 A' }, { value: 'b', label: '选项 B' }], value: signal('a') }),
|
||||||
|
select({ options: [{ value: 'a', label: '选项 A' }, { value: 'b', label: '选项 B' }], value: signal('a'), size: 'lg' }),
|
||||||
|
inputNumber({ value: qty, size: 'sm' }), inputNumber({ value: qty }), inputNumber({ value: qty, size: 'lg' }),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
section(
|
section(
|
||||||
'sec-feedback',
|
'sec-feedback',
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T09:17:08.488Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T09:29:34.397Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -87,6 +87,7 @@ image({ src: '/a.png', alt: '架构图', width: 160 })
|
|||||||
- type:'button' | 'submit' | 'reset' —— 原生类型;缺省 button,表单内不会意外提交
|
- type:'button' | 'submit' | 'reset' —— 原生类型;缺省 button,表单内不会意外提交
|
||||||
- disabled:boolean | Signal<boolean> —— 禁用态:布尔直接置位,传信号则动态同步
|
- disabled:boolean | Signal<boolean> —— 禁用态:布尔直接置位,传信号则动态同步
|
||||||
- onClick(回调):() => void —— 点击回调
|
- onClick(回调):() => void —— 点击回调
|
||||||
|
- size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -115,6 +116,7 @@ button({ label: '提交', variant: 'primary', onClick: () => console.log('clicke
|
|||||||
- suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染
|
- suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染
|
||||||
- clearable(默认 false):boolean —— 开启后有值即显示一键清空按钮;缺省 false
|
- clearable(默认 false):boolean —— 开启后有值即显示一键清空按钮;缺省 false
|
||||||
- maxCount:number —— 字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限
|
- maxCount:number —— 字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限
|
||||||
|
- size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -145,6 +147,7 @@ const name = signal(''); const el = textField({ value: name, placeholder: '输
|
|||||||
- onChange(回调):(value: number) => void —— 值变化回调(已夹取到合法区间)
|
- onChange(回调):(value: number) => void —— 值变化回调(已夹取到合法区间)
|
||||||
- prefix:string | HTMLElement —— 前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染
|
- prefix:string | HTMLElement —— 前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染
|
||||||
- suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染
|
- suffix:string | HTMLElement —— 后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染
|
||||||
|
- size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -286,6 +289,7 @@ const night = signal(true); switchButton({ label: '夜间模式', checked: night
|
|||||||
- clearable:boolean —— 有值时触发器右缘出现清空按钮(收起态按 Delete / Backspace 也能清);缺省 false,用于「可选筛选项」
|
- clearable:boolean —— 有值时触发器右缘出现清空按钮(收起态按 Delete / Backspace 也能清);缺省 false,用于「可选筛选项」
|
||||||
- disabled:boolean | Signal<boolean> —— 禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层
|
- disabled:boolean | Signal<boolean> —— 禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层
|
||||||
- onChange(回调):(value: string) => void —— 选中回调
|
- onChange(回调):(value: string) => void —— 选中回调
|
||||||
|
- size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { ButtonProps } from './types.js';
|
|||||||
export function button(props: ButtonProps): HTMLElement {
|
export function button(props: ButtonProps): HTMLElement {
|
||||||
return h(
|
return h(
|
||||||
'button',
|
'button',
|
||||||
{ class: `ua-btn ua-btn-${props.variant ?? 'primary'}`, type: props.type ?? 'button', disabled: props.disabled, onClick: props.onClick }, // 缺省 button:表单内不隐式提交
|
{ class: `ua-btn ua-btn-${props.variant ?? 'primary'}${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, type: props.type ?? 'button', disabled: props.disabled, onClick: props.onClick }, // 缺省 button:表单内不隐式提交
|
||||||
props.label,
|
props.label,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function inputNumber(props: InputNumberProps): HTMLElement {
|
|||||||
const clamp = (v: number): number => Math.min(max, Math.max(min, v)); // 任何来源的值都夹回合法区间
|
const clamp = (v: number): number => Math.min(max, Math.max(min, v)); // 任何来源的值都夹回合法区间
|
||||||
const quantize = (n: number): number => (props.precision === undefined ? n : Number(n.toFixed(props.precision))); // precision 归一(顺带消除步进浮点尾差)
|
const quantize = (n: number): number => (props.precision === undefined ? n : Number(n.toFixed(props.precision))); // precision 归一(顺带消除步进浮点尾差)
|
||||||
const emit = (v: number): void => { const n = quantize(clamp(v)); props.value(n); if (props.onChange) props.onChange(n); }; // 写回信号并回调
|
const emit = (v: number): void => { const n = quantize(clamp(v)); props.value(n); if (props.onChange) props.onChange(n); }; // 写回信号并回调
|
||||||
const input = h('input', { class: 'ua-input ua-input-number-field', inputmode: 'decimal', disabled: props.disabled, 'aria-label': '数字输入' }) as HTMLInputElement;
|
const input = h('input', { class: `ua-input ua-input-number-field${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, inputmode: 'decimal', disabled: props.disabled, 'aria-label': '数字输入' }) as HTMLInputElement;
|
||||||
const sync = (): void => { const text = props.precision === undefined ? String(props.value()) : props.value().toFixed(props.precision); if (input.value !== text) input.value = text; }; // 外部变化写回输入框(precision 定长显示)
|
const sync = (): void => { const text = props.precision === undefined ? String(props.value()) : props.value().toFixed(props.precision); if (input.value !== text) input.value = text; }; // 外部变化写回输入框(precision 定长显示)
|
||||||
effect(sync);
|
effect(sync);
|
||||||
input.addEventListener('change', () => { const n = parseFloat(input.value); if (Number.isNaN(n)) sync(); else emit(n); }); // 非法输入回滚当前值
|
input.addEventListener('change', () => { const n = parseFloat(input.value); if (Number.isNaN(n)) sync(); else emit(n); }); // 非法输入回滚当前值
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function select(props: SelectProps): HTMLElement {
|
|||||||
'button',
|
'button',
|
||||||
{
|
{
|
||||||
type: 'button',
|
type: 'button',
|
||||||
class: 'ua-select-trigger',
|
class: `ua-select-trigger${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`,
|
||||||
'aria-haspopup': 'listbox',
|
'aria-haspopup': 'listbox',
|
||||||
'aria-expanded': () => String(open()),
|
'aria-expanded': () => String(open()),
|
||||||
disabled: props.disabled, // 布尔或信号禁用,h() 直接透传(原生 button 拦截点击)
|
disabled: props.disabled, // 布尔或信号禁用,h() 直接透传(原生 button 拦截点击)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { TextFieldProps } from './types.js';
|
|||||||
|
|
||||||
/*** 文本输入:value 信号双向绑定,disabled 支持布尔或信号禁用,onInput 为输入回调;prefix/suffix 包附加组,clearable 一键清空,maxCount 显示字数统计 ***/
|
/*** 文本输入:value 信号双向绑定,disabled 支持布尔或信号禁用,onInput 为输入回调;prefix/suffix 包附加组,clearable 一键清空,maxCount 显示字数统计 ***/
|
||||||
export function textField(props: TextFieldProps): HTMLElement {
|
export function textField(props: TextFieldProps): HTMLElement {
|
||||||
const el = h('input', { class: 'ua-input', placeholder: props.placeholder ?? '', disabled: props.disabled }) as HTMLInputElement; // false/undefined 移除属性,Signal 走响应式
|
const el = h('input', { class: `ua-input${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, placeholder: props.placeholder ?? '', disabled: props.disabled }) as HTMLInputElement; // false/undefined 移除属性,Signal 走响应式
|
||||||
if (props.value) {
|
if (props.value) {
|
||||||
const signal = props.value;
|
const signal = props.value;
|
||||||
el.addEventListener('input', () => signal(el.value)); // 用户输入写回信号
|
el.addEventListener('input', () => signal(el.value)); // 用户输入写回信号
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export interface PopoverProps {
|
|||||||
|
|
||||||
/*** 按钮入参 ***/
|
/*** 按钮入参 ***/
|
||||||
export interface ButtonProps {
|
export interface ButtonProps {
|
||||||
|
size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md
|
||||||
label: string; // 按钮文案
|
label: string; // 按钮文案
|
||||||
variant?: 'primary' | 'ghost' | 'danger'; // 视觉强度;缺省 primary
|
variant?: 'primary' | 'ghost' | 'danger'; // 视觉强度;缺省 primary
|
||||||
type?: 'button' | 'submit' | 'reset'; // 原生类型;缺省 button(表单内不会意外触发提交)
|
type?: 'button' | 'submit' | 'reset'; // 原生类型;缺省 button(表单内不会意外触发提交)
|
||||||
@@ -30,6 +31,7 @@ export interface ButtonProps {
|
|||||||
|
|
||||||
/*** 文本输入入参 ***/
|
/*** 文本输入入参 ***/
|
||||||
export interface TextFieldProps {
|
export interface TextFieldProps {
|
||||||
|
size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md
|
||||||
placeholder?: string; // 占位文案
|
placeholder?: string; // 占位文案
|
||||||
value?: Signal<string>; // 双向绑定的信号
|
value?: Signal<string>; // 双向绑定的信号
|
||||||
disabled?: boolean | Signal<boolean>; // 禁用态:布尔直接置位,传信号则动态同步
|
disabled?: boolean | Signal<boolean>; // 禁用态:布尔直接置位,传信号则动态同步
|
||||||
@@ -108,6 +110,7 @@ export interface SwitchProps {
|
|||||||
|
|
||||||
/*** 下拉选择入参 ***/
|
/*** 下拉选择入参 ***/
|
||||||
export interface SelectProps {
|
export interface SelectProps {
|
||||||
|
size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md
|
||||||
options: OptionItem[]; // 选项列表
|
options: OptionItem[]; // 选项列表
|
||||||
value?: Signal<string>; // 选中值信号,双向绑定
|
value?: Signal<string>; // 选中值信号,双向绑定
|
||||||
placeholder?: string; // 未选中时的占位项文案
|
placeholder?: string; // 未选中时的占位项文案
|
||||||
@@ -203,6 +206,7 @@ export interface TextareaProps {
|
|||||||
|
|
||||||
/*** 数字输入入参 ***/
|
/*** 数字输入入参 ***/
|
||||||
export interface InputNumberProps {
|
export interface InputNumberProps {
|
||||||
|
size?: 'sm' | 'md' | 'lg'; // 高度档位:走 --ua-control-h-* 三档;缺省 md
|
||||||
value: Signal<number>; // 当前值信号,双向绑定(按钮/键盘写回、外部赋值同步)
|
value: Signal<number>; // 当前值信号,双向绑定(按钮/键盘写回、外部赋值同步)
|
||||||
min?: number; // 最小值;缺省无下限
|
min?: number; // 最小值;缺省无下限
|
||||||
max?: number; // 最大值;缺省无上限
|
max?: number; // 最大值;缺省无上限
|
||||||
|
|||||||
@@ -214,6 +214,11 @@
|
|||||||
"t": "() => void",
|
"t": "() => void",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "点击回调"
|
"d": "点击回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })",
|
"example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })",
|
||||||
@@ -270,6 +275,11 @@
|
|||||||
"n": "maxCount",
|
"n": "maxCount",
|
||||||
"t": "number",
|
"t": "number",
|
||||||
"d": "字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限"
|
"d": "字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
||||||
@@ -333,6 +343,11 @@
|
|||||||
"n": "suffix",
|
"n": "suffix",
|
||||||
"t": "string | HTMLElement",
|
"t": "string | HTMLElement",
|
||||||
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
||||||
@@ -607,6 +622,11 @@
|
|||||||
"t": "(value: string) => void",
|
"t": "(value: string) => void",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "选中回调"
|
"d": "选中回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "size",
|
||||||
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], value: city, placeholder: '选择城市…' })",
|
"example": "const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], value: city, placeholder: '选择城市…' })",
|
||||||
|
|||||||
+29
-7
@@ -6,6 +6,27 @@
|
|||||||
outline: 2px solid var(--ua-color-accent);
|
outline: 2px solid var(--ua-color-accent);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
/* —— 控件高度三档:所有可交互控件统一走 --ua-control-h-*,size 档只改高度 —— */
|
||||||
|
.ua-btn,
|
||||||
|
.ua-input,
|
||||||
|
.ua-select-trigger,
|
||||||
|
.ua-input-number-field {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: var(--ua-control-h-md);
|
||||||
|
}
|
||||||
|
.ua-btn.is-sm,
|
||||||
|
.ua-input.is-sm,
|
||||||
|
.ua-select-trigger.is-sm,
|
||||||
|
.ua-input-number-field.is-sm {
|
||||||
|
height: var(--ua-control-h-sm);
|
||||||
|
}
|
||||||
|
.ua-btn.is-lg,
|
||||||
|
.ua-input.is-lg,
|
||||||
|
.ua-select-trigger.is-lg,
|
||||||
|
.ua-input-number-field.is-lg {
|
||||||
|
height: var(--ua-control-h-lg);
|
||||||
|
}
|
||||||
|
|
||||||
.ua-btn {
|
.ua-btn {
|
||||||
padding: var(--ua-space-15) var(--ua-space-35);
|
padding: var(--ua-space-15) var(--ua-space-35);
|
||||||
border: 1px solid var(--ua-color-accent);
|
border: 1px solid var(--ua-color-accent);
|
||||||
@@ -913,6 +934,7 @@ body.ua-col-resizing {
|
|||||||
gap: var(--ua-space-1);
|
gap: var(--ua-space-1);
|
||||||
}
|
}
|
||||||
.ua-input-number .ua-btn {
|
.ua-input-number .ua-btn {
|
||||||
|
height: auto; /* 步进器是图标按钮:不吃控件统一高度 */
|
||||||
padding: 5px var(--ua-space-3); /* 收窄为步进按钮 */
|
padding: 5px var(--ua-space-3); /* 收窄为步进按钮 */
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
@@ -1311,7 +1333,7 @@ button.ua-step-dot:hover {
|
|||||||
}
|
}
|
||||||
.ua-page-btn {
|
.ua-page-btn {
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
height: 28px;
|
height: var(--ua-control-h-sm);
|
||||||
padding: 0 var(--ua-space-2);
|
padding: 0 var(--ua-space-2);
|
||||||
border: 1px solid var(--ua-color-border);
|
border: 1px solid var(--ua-color-border);
|
||||||
border-radius: var(--ua-radius-sm);
|
border-radius: var(--ua-radius-sm);
|
||||||
@@ -1380,7 +1402,7 @@ button.ua-step-dot:hover {
|
|||||||
}
|
}
|
||||||
.ua-skeleton-circle {
|
.ua-skeleton-circle {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: var(--ua-control-h-lg);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -2554,7 +2576,7 @@ button.ua-step-dot:hover {
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: var(--ua-control-h-sm);
|
||||||
border: 1px solid var(--ua-color-border);
|
border: 1px solid var(--ua-color-border);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--ua-color-bg);
|
background: var(--ua-color-bg);
|
||||||
@@ -2737,7 +2759,7 @@ button.ua-step-dot:hover {
|
|||||||
}
|
}
|
||||||
.ua-upload-thumb {
|
.ua-upload-thumb {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: var(--ua-control-h-sm);
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 1px solid var(--ua-color-border);
|
border: 1px solid var(--ua-color-border);
|
||||||
border-radius: var(--ua-radius-sm);
|
border-radius: var(--ua-radius-sm);
|
||||||
@@ -2869,7 +2891,7 @@ button.ua-step-dot:hover {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: var(--ua-control-h-lg);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
font-size: var(--ua-font-size-lg);
|
font-size: var(--ua-font-size-lg);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -3364,10 +3386,10 @@ button.ua-step-dot:hover {
|
|||||||
margin-top: var(--ua-space-05);
|
margin-top: var(--ua-space-05);
|
||||||
}
|
}
|
||||||
.ua-datepicker-blank {
|
.ua-datepicker-blank {
|
||||||
height: 28px;
|
height: var(--ua-control-h-sm);
|
||||||
}
|
}
|
||||||
.ua-datepicker-day {
|
.ua-datepicker-day {
|
||||||
height: 28px;
|
height: var(--ua-control-h-sm);
|
||||||
font-size: var(--ua-font-size-sm);
|
font-size: var(--ua-font-size-sm);
|
||||||
color: var(--ua-color-text);
|
color: var(--ua-color-text);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ export const KIND_META = [
|
|||||||
label: '内距字面值',
|
label: '内距字面值',
|
||||||
hint: 'padding/gap/margin 写死 px,应改用 --ua-space-*',
|
hint: 'padding/gap/margin 写死 px,应改用 --ua-space-*',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'control-h-literal',
|
||||||
|
label: '控件高度字面值',
|
||||||
|
hint: 'height 写死控件档位值(28/34/40px),应改用 --ua-control-h-*',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/*** 作用:需要扫描的 CSS 属性白名单(其余属性本轮不动;用白名单顺带避开伪类选择器误判) ***/
|
/*** 作用:需要扫描的 CSS 属性白名单(其余属性本轮不动;用白名单顺带避开伪类选择器误判) ***/
|
||||||
@@ -66,6 +71,9 @@ const SCAN_PROPS = new Set([
|
|||||||
'gap',
|
'gap',
|
||||||
'row-gap',
|
'row-gap',
|
||||||
'column-gap',
|
'column-gap',
|
||||||
|
'min-height',
|
||||||
|
'max-height',
|
||||||
|
'height',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/*** 作用:内距类属性集合(迁移与统计共用同一判定) ***/
|
/*** 作用:内距类属性集合(迁移与统计共用同一判定) ***/
|
||||||
@@ -136,6 +144,13 @@ const DURATION_STEPS = [
|
|||||||
{ max: 0.2501, token: 'var(--ua-duration-slow)' },
|
{ max: 0.2501, token: 'var(--ua-duration-slow)' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/*** 作用:控件高度三档的字面值 → 令牌(28/34/40px 三个值保留给控件高度档位,其余尺寸不入此口径) ***/
|
||||||
|
const CONTROL_H_TOKENS = new Map([
|
||||||
|
['28px', 'var(--ua-control-h-sm)'],
|
||||||
|
['34px', 'var(--ua-control-h-md)'],
|
||||||
|
['40px', 'var(--ua-control-h-lg)'],
|
||||||
|
]);
|
||||||
|
|
||||||
/*** 作用:压缩空白,便于用稳定字符串比对(不改语义) ***/
|
/*** 作用:压缩空白,便于用稳定字符串比对(不改语义) ***/
|
||||||
export function squash(value) {
|
export function squash(value) {
|
||||||
return String(value).replace(/\s+/g, ' ').trim();
|
return String(value).replace(/\s+/g, ' ').trim();
|
||||||
@@ -161,7 +176,7 @@ export function blankComments(src) {
|
|||||||
|
|
||||||
/*** 作用:逐条产出「属性 + 值」及其在原文中的偏移;只认白名单属性且必须以分号收尾(避开伪类选择器) ***/
|
/*** 作用:逐条产出「属性 + 值」及其在原文中的偏移;只认白名单属性且必须以分号收尾(避开伪类选择器) ***/
|
||||||
export function eachDeclaration(code) {
|
export function eachDeclaration(code) {
|
||||||
const re = /(padding-top|padding-right|padding-bottom|padding-left|padding|margin-top|margin-right|margin-bottom|margin-left|margin|row-gap|column-gap|gap|transition-duration|animation-duration|transition|animation|font-size|box-shadow|border-radius|outline)\s*:\s*([^;{}]*);/gi;
|
const re = /(padding-top|padding-right|padding-bottom|padding-left|padding|margin-top|margin-right|margin-bottom|margin-left|margin|row-gap|column-gap|gap|min-height|max-height|height|transition-duration|animation-duration|transition|animation|font-size|box-shadow|border-radius|outline)\s*:\s*([^;{}]*);/gi;
|
||||||
const list = [];
|
const list = [];
|
||||||
for (const m of code.matchAll(re)) {
|
for (const m of code.matchAll(re)) {
|
||||||
const prop = m[1].toLowerCase();
|
const prop = m[1].toLowerCase();
|
||||||
@@ -266,6 +281,13 @@ function migrateSpace(core) {
|
|||||||
return { kind: 'space-literal', value: next };
|
return { kind: 'space-literal', value: next };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 作用:迁移单条控件高度:整值命中三档即换令牌(其余 height 值不动),返回 null 表示无需改动 ***/
|
||||||
|
function migrateControlH(core) {
|
||||||
|
const token = CONTROL_H_TOKENS.get(squash(core));
|
||||||
|
if (!token) return null;
|
||||||
|
return { kind: 'control-h-literal', value: token };
|
||||||
|
}
|
||||||
|
|
||||||
/*** 作用:判定单条声明是否需要迁移,返回 { kind, value } 或 null(幂等:已是令牌就返回 null) ***/
|
/*** 作用:判定单条声明是否需要迁移,返回 { kind, value } 或 null(幂等:已是令牌就返回 null) ***/
|
||||||
export function migrateDeclaration(prop, rawValue) {
|
export function migrateDeclaration(prop, rawValue) {
|
||||||
const { core, tail } = splitImportant(rawValue);
|
const { core, tail } = splitImportant(rawValue);
|
||||||
@@ -282,6 +304,8 @@ export function migrateDeclaration(prop, rawValue) {
|
|||||||
result = migrateShadow(core);
|
result = migrateShadow(core);
|
||||||
} else if (SPACE_PROPS.has(prop)) {
|
} else if (SPACE_PROPS.has(prop)) {
|
||||||
result = migrateSpace(core);
|
result = migrateSpace(core);
|
||||||
|
} else if (prop === 'height') {
|
||||||
|
result = migrateControlH(core);
|
||||||
}
|
}
|
||||||
if (!result) return null;
|
if (!result) return null;
|
||||||
return { kind: result.kind, value: result.value + tail };
|
return { kind: result.kind, value: result.value + tail };
|
||||||
@@ -328,6 +352,10 @@ function detect(prop, rawValue, key) {
|
|||||||
const tokens = String(value).match(/(^|[\s(,])(\d+(?:\.\d+)?)px\b/gi) ?? [];
|
const tokens = String(value).match(/(^|[\s(,])(\d+(?:\.\d+)?)px\b/gi) ?? [];
|
||||||
return tokens.filter((t) => !/(^|[\s(,])1px$/i.test(t)).length;
|
return tokens.filter((t) => !/(^|[\s(,])1px$/i.test(t)).length;
|
||||||
}
|
}
|
||||||
|
if (key === 'control-h-literal') {
|
||||||
|
if (prop !== 'height') return 0; // min/max-height 是约束不是控件档位,不入此口径
|
||||||
|
return CONTROL_H_TOKENS.has(squash(core)) ? 1 : 0;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user