@zerodep-dev/vue-tsx-pro-mobile 0.0.0-stage → 0.4.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 +29 -2
- package/dist/area/data.d.ts +10 -0
- package/dist/area/data.js +3899 -0
- package/dist/area/data.js.map +1 -0
- package/dist/area/index.d.ts +22 -0
- package/dist/area/index.js +41 -0
- package/dist/area/index.js.map +1 -0
- package/dist/base/Button.d.ts +170 -0
- package/dist/base/Button.js +537 -0
- package/dist/base/Button.js.map +1 -0
- package/dist/base/Icon.d.ts +38 -0
- package/dist/base/Icon.js +65 -0
- package/dist/base/Icon.js.map +1 -0
- package/dist/capability/PrivacyPrompt.d.ts +1 -0
- package/dist/capability/PrivacyPrompt.js +73 -0
- package/dist/capability/PrivacyPrompt.js.map +1 -0
- package/dist/capability/types.d.ts +92 -0
- package/dist/capability/usePermission.d.ts +23 -0
- package/dist/capability/usePermission.js +52 -0
- package/dist/capability/usePermission.js.map +1 -0
- package/dist/capability/useStorage.d.ts +8 -0
- package/dist/capability/useStorage.js +19 -0
- package/dist/capability/useStorage.js.map +1 -0
- package/dist/capability/web.d.ts +18 -0
- package/dist/capability/web.js +109 -0
- package/dist/capability/web.js.map +1 -0
- package/dist/capacitor/index.d.ts +9 -0
- package/dist/capacitor/index.js +143 -0
- package/dist/capacitor/index.js.map +1 -0
- package/dist/config/ConfigProvider.d.ts +37 -0
- package/dist/config/ConfigProvider.js +64 -0
- package/dist/config/ConfigProvider.js.map +1 -0
- package/dist/config/context.d.ts +16 -0
- package/dist/config/context.js +20 -0
- package/dist/config/context.js.map +1 -0
- package/dist/config/lang.d.ts +85 -0
- package/dist/config/lang.js +138 -0
- package/dist/config/lang.js.map +1 -0
- package/dist/display/Badge.d.ts +39 -0
- package/dist/display/Badge.js +108 -0
- package/dist/display/Badge.js.map +1 -0
- package/dist/display/Cell.d.ts +92 -0
- package/dist/display/Cell.js +204 -0
- package/dist/display/Cell.js.map +1 -0
- package/dist/display/Collapse.d.ts +47 -0
- package/dist/display/Collapse.js +173 -0
- package/dist/display/Collapse.js.map +1 -0
- package/dist/display/Empty.d.ts +42 -0
- package/dist/display/Empty.js +79 -0
- package/dist/display/Empty.js.map +1 -0
- package/dist/display/Grid.d.ts +49 -0
- package/dist/display/Grid.js +136 -0
- package/dist/display/Grid.js.map +1 -0
- package/dist/display/Image.d.ts +70 -0
- package/dist/display/Image.js +138 -0
- package/dist/display/Image.js.map +1 -0
- package/dist/display/IndexBar.d.ts +34 -0
- package/dist/display/IndexBar.js +158 -0
- package/dist/display/IndexBar.js.map +1 -0
- package/dist/display/List.d.ts +34 -0
- package/dist/display/List.js +105 -0
- package/dist/display/List.js.map +1 -0
- package/dist/display/NoticeBar.d.ts +57 -0
- package/dist/display/NoticeBar.js +172 -0
- package/dist/display/NoticeBar.js.map +1 -0
- package/dist/display/Steps.d.ts +59 -0
- package/dist/display/Steps.js +158 -0
- package/dist/display/Steps.js.map +1 -0
- package/dist/display/SwipeCell.d.ts +37 -0
- package/dist/display/SwipeCell.js +147 -0
- package/dist/display/SwipeCell.js.map +1 -0
- package/dist/display/Swiper.d.ts +47 -0
- package/dist/display/Swiper.js +173 -0
- package/dist/display/Swiper.js.map +1 -0
- package/dist/display/Tag.d.ts +99 -0
- package/dist/display/Tag.js +320 -0
- package/dist/display/Tag.js.map +1 -0
- package/dist/feedback/Dialog.d.ts +79 -0
- package/dist/feedback/Dialog.js +186 -0
- package/dist/feedback/Dialog.js.map +1 -0
- package/dist/feedback/FeedbackOutlet.d.ts +2 -0
- package/dist/feedback/FeedbackOutlet.js +31 -0
- package/dist/feedback/FeedbackOutlet.js.map +1 -0
- package/dist/feedback/Loading.d.ts +26 -0
- package/dist/feedback/Loading.js +68 -0
- package/dist/feedback/Loading.js.map +1 -0
- package/dist/feedback/Notify.d.ts +48 -0
- package/dist/feedback/Notify.js +116 -0
- package/dist/feedback/Notify.js.map +1 -0
- package/dist/feedback/PullRefresh.d.ts +58 -0
- package/dist/feedback/PullRefresh.js +178 -0
- package/dist/feedback/PullRefresh.js.map +1 -0
- package/dist/feedback/Skeleton.d.ts +56 -0
- package/dist/feedback/Skeleton.js +99 -0
- package/dist/feedback/Skeleton.js.map +1 -0
- package/dist/feedback/Toast.d.ts +89 -0
- package/dist/feedback/Toast.js +186 -0
- package/dist/feedback/Toast.js.map +1 -0
- package/dist/form/Checkbox.d.ts +52 -0
- package/dist/form/Checkbox.js +107 -0
- package/dist/form/Checkbox.js.map +1 -0
- package/dist/form/Field.d.ts +91 -0
- package/dist/form/Field.js +296 -0
- package/dist/form/Field.js.map +1 -0
- package/dist/form/Form.d.ts +50 -0
- package/dist/form/Form.js +96 -0
- package/dist/form/Form.js.map +1 -0
- package/dist/form/FormItem.d.ts +57 -0
- package/dist/form/FormItem.js +142 -0
- package/dist/form/FormItem.js.map +1 -0
- package/dist/form/Radio.d.ts +41 -0
- package/dist/form/Radio.js +85 -0
- package/dist/form/Radio.js.map +1 -0
- package/dist/form/Rate.d.ts +45 -0
- package/dist/form/Rate.js +162 -0
- package/dist/form/Rate.js.map +1 -0
- package/dist/form/Search.d.ts +50 -0
- package/dist/form/Search.js +152 -0
- package/dist/form/Search.js.map +1 -0
- package/dist/form/Slider.d.ts +43 -0
- package/dist/form/Slider.js +213 -0
- package/dist/form/Slider.js.map +1 -0
- package/dist/form/Stepper.d.ts +61 -0
- package/dist/form/Stepper.js +222 -0
- package/dist/form/Stepper.js.map +1 -0
- package/dist/form/Switch.d.ts +33 -0
- package/dist/form/Switch.js +116 -0
- package/dist/form/Switch.js.map +1 -0
- package/dist/form/Uploader.d.ts +67 -0
- package/dist/form/Uploader.js +207 -0
- package/dist/form/Uploader.js.map +1 -0
- package/dist/form/choice.d.ts +42 -0
- package/dist/form/choice.js +109 -0
- package/dist/form/choice.js.map +1 -0
- package/dist/form/context.d.ts +83 -0
- package/dist/form/context.js +89 -0
- package/dist/form/context.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +59 -0
- package/dist/layout/Divider.d.ts +34 -0
- package/dist/layout/Divider.js +55 -0
- package/dist/layout/Divider.js.map +1 -0
- package/dist/layout/Space.d.ts +66 -0
- package/dist/layout/Space.js +65 -0
- package/dist/layout/Space.js.map +1 -0
- package/dist/navigation/BackTop.d.ts +29 -0
- package/dist/navigation/BackTop.js +89 -0
- package/dist/navigation/BackTop.js.map +1 -0
- package/dist/navigation/NavBar.d.ts +46 -0
- package/dist/navigation/NavBar.js +141 -0
- package/dist/navigation/NavBar.js.map +1 -0
- package/dist/navigation/Sticky.d.ts +19 -0
- package/dist/navigation/Sticky.js +28 -0
- package/dist/navigation/Sticky.js.map +1 -0
- package/dist/navigation/TabBar.d.ts +54 -0
- package/dist/navigation/TabBar.js +138 -0
- package/dist/navigation/TabBar.js.map +1 -0
- package/dist/navigation/Tabs.d.ts +45 -0
- package/dist/navigation/Tabs.js +206 -0
- package/dist/navigation/Tabs.js.map +1 -0
- package/dist/overlay/ActionSheet.d.ts +80 -0
- package/dist/overlay/ActionSheet.js +164 -0
- package/dist/overlay/ActionSheet.js.map +1 -0
- package/dist/overlay/Overlay.d.ts +35 -0
- package/dist/overlay/Overlay.js +85 -0
- package/dist/overlay/Overlay.js.map +1 -0
- package/dist/overlay/Popup.d.ts +76 -0
- package/dist/overlay/Popup.js +191 -0
- package/dist/overlay/Popup.js.map +1 -0
- package/dist/picker/Calendar.d.ts +69 -0
- package/dist/picker/Calendar.js +213 -0
- package/dist/picker/Calendar.js.map +1 -0
- package/dist/picker/Cascader.d.ts +50 -0
- package/dist/picker/Cascader.js +199 -0
- package/dist/picker/Cascader.js.map +1 -0
- package/dist/picker/DatePicker.d.ts +52 -0
- package/dist/picker/DatePicker.js +135 -0
- package/dist/picker/DatePicker.js.map +1 -0
- package/dist/picker/DropdownMenu.d.ts +39 -0
- package/dist/picker/DropdownMenu.js +242 -0
- package/dist/picker/DropdownMenu.js.map +1 -0
- package/dist/picker/Picker.d.ts +67 -0
- package/dist/picker/Picker.js +185 -0
- package/dist/picker/Picker.js.map +1 -0
- package/dist/picker/PickerColumn.d.ts +36 -0
- package/dist/picker/PickerColumn.js +175 -0
- package/dist/picker/PickerColumn.js.map +1 -0
- package/dist/theme/dark.d.ts +121 -0
- package/dist/theme/dark.js +77 -0
- package/dist/theme/dark.js.map +1 -0
- package/dist/theme/divider.d.ts +6 -0
- package/dist/theme/divider.js +18 -0
- package/dist/theme/divider.js.map +1 -0
- package/dist/theme/token-names.d.ts +1389 -0
- package/dist/theme/tokens.d.ts +549 -0
- package/dist/theme/tokens.js +312 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/utils/host.d.ts +97 -0
- package/dist/utils/host.js +98 -0
- package/dist/utils/host.js.map +1 -0
- package/dist/utils/presence.d.ts +11 -0
- package/dist/utils/presence.js +34 -0
- package/dist/utils/presence.js.map +1 -0
- package/dist/utils/props.d.ts +38 -0
- package/dist/utils/props.js +115 -0
- package/dist/utils/props.js.map +1 -0
- package/dist/utils/scales.d.ts +48 -0
- package/package.json +106 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Slider.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_createVNode","_mergeProps"],"sources":["../../src/form/Slider.tsx"],"sourcesContent":["/**\n * 滑块:在一段范围里拖动选一个数(音量、价格上限),给两个数时是区间(价格区间)。点轨道直接跳过去。\n *\n * <Slider _value={volume} />\n * <Slider _value={price} min={0} max={1000} step={50} /> price 是 [200, 600] 时两个滑块\n *\n * 拖动时值跟着变,松手时 onChange;横滑才接管手势,竖滑照常滚动页面。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useHost, type HostRect } from '../utils/host';\nimport { dataAttrs, mergeAttrs, type PartProps } from '../utils/props';\nimport { useForm } from './context';\n\n/** 一个数,或区间的两端 */\nexport type SliderValue = number | [number, number];\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface SliderProps extends Omit<PartProps<'div'>, 'onChange'> {\n /** 当前的值;双向绑定写 `_value={x}`。数组时是区间 */\n _value?: SliderValue;\n /**\n * 最小值\n * @default 0\n */\n min?: number;\n /**\n * 最大值\n * @default 100\n */\n max?: number;\n /**\n * 步长\n * @default 1\n */\n step?: number;\n /** 禁用 */\n disabled?: boolean;\n /** 只读 */\n readonly?: boolean;\n /** 松手(或点了轨道、按了方向键)之后 */\n onChange?: (value: SliderValue) => void;\n /** 轨道的部件属性 */\n propsTrack?: PartProps<'div'>;\n /** 滑块(两个时都用)的部件属性 */\n propsThumb?: PartProps<'div'>;\n}\n\n/** Slider 的样式:root、track、fill、thumb */\nexport const sliderStyles = css.create({\n root: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n height: '2em',\n // 两端留出半个滑块,滑块不出界\n paddingInline: '0.75em',\n touchAction: 'pan-y',\n userSelect: 'none',\n opacity: { default: 1, '[data-disabled]': '_disabled' },\n },\n track: {\n position: 'relative',\n flex: '1 1 auto',\n height: '0.25em',\n borderRadius: '_full',\n backgroundColor: '_fillActive',\n },\n fill: { position: 'absolute', insetBlock: 0, borderRadius: '_full', backgroundColor: '_accent' },\n thumb: {\n boxSizing: 'border-box',\n position: 'absolute',\n insetBlockStart: '50%',\n width: '1.5em',\n height: '1.5em',\n borderRadius: '_full',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n // 滑块在亮暗两套主题里都是白的(同 Switch 的圆点)\n backgroundColor: '#fff',\n boxShadow: '_sm',\n transform: 'translate(-50%, -50%)',\n cursor: { default: 'grab', '[data-disabled]': 'not-allowed' },\n },\n});\n\n// 判定横竖的移动距离(px)\nconst LOCK_DISTANCE = 6;\n\n/** 滑块(div > 轨道:已选的一段 + 滑块) */\nexport const Slider = _component(\n ({\n _value: value = _bindable<SliderValue>(0),\n min = 0,\n max = 100,\n step = 1,\n disabled,\n readonly = false,\n onChange,\n propsTrack,\n propsThumb,\n ...rest\n }: SliderProps) => {\n const host = useHost();\n const form = useForm();\n let track: unknown = null;\n let rect: HostRect | null = null;\n let horizontal = _state(false);\n let start = { x: 0, y: 0 };\n let direction: 'h' | 'v' | null = null;\n // 拖的是区间的哪一端\n let active = 0;\n\n const interactive = () => !(disabled ?? form?.disabled) && !readonly && !form?.readonly;\n const ends = (): [number, number] => (Array.isArray(value) ? value : [min, value]);\n const percent = (number: number) => ((number - min) / (max - min || 1)) * 100;\n const snap = (number: number) => {\n const stepped = Math.round((number - min) / step) * step + min;\n return Number(Math.min(max, Math.max(min, stepped)).toFixed(10));\n };\n const valueAt = (x: number) =>\n rect ? snap(min + ((x - rect.left) / (rect.width || 1)) * (max - min)) : null;\n /** 把一端改成 next:区间两端交叉时换成拖另一端 */\n const setEnd = (index: number, next: number) => {\n if (!Array.isArray(value)) return void (value = next);\n const pair: [number, number] = [...value];\n pair[index] = next;\n if (pair[0] > pair[1]) {\n pair.reverse();\n active = 1 - index;\n }\n value = pair;\n };\n const nearest = (number: number) => {\n if (!Array.isArray(value)) return 0;\n return Math.abs(number - value[0]) <= Math.abs(number - value[1]) ? 0 : 1;\n };\n const pointX = (event: Event) =>\n (event as TouchEvent).changedTouches?.[0]?.clientX ?? (event as MouseEvent).clientX;\n\n const onTouchstart = (event: TouchEvent) => {\n if (!interactive()) return;\n const touch = event.touches[0]!;\n start = { x: touch.clientX, y: touch.clientY };\n direction = null;\n void host.measure(track).then((measured) => (rect = measured));\n };\n const onTouchmove = (event: TouchEvent) => {\n if (!interactive() || !rect) return;\n const touch = event.touches[0]!;\n if (!direction) {\n const dx = touch.clientX - start.x;\n const dy = touch.clientY - start.y;\n if (Math.abs(dx) < LOCK_DISTANCE && Math.abs(dy) < LOCK_DISTANCE) return;\n direction = Math.abs(dx) > Math.abs(dy) ? 'h' : 'v';\n horizontal = direction === 'h';\n if (horizontal) active = nearest(valueAt(start.x) ?? min);\n }\n if (direction !== 'h') return;\n const next = valueAt(touch.clientX);\n if (next !== null) setEnd(active, next);\n };\n const onTouchend = () => {\n if (horizontal) onChange?.(value);\n horizontal = false;\n };\n // 点轨道:跳到点的位置(滑动不触发点击)\n const onClick = (event: Event) => {\n if (!interactive()) return;\n const x = pointX(event);\n void host.measure(track).then((measured) => {\n rect = measured;\n const next = valueAt(x);\n if (next === null) return;\n active = nearest(next);\n setEnd(active, next);\n onChange?.(value);\n });\n };\n const onKeydown = (index: number, event: KeyboardEvent) => {\n if (!interactive()) return;\n const delta = { ArrowRight: step, ArrowUp: step, ArrowLeft: -step, ArrowDown: -step }[\n event.key\n ];\n if (delta === undefined) return;\n event.preventDefault();\n setEnd(index, snap(ends()[index]! + delta));\n onChange?.(value);\n };\n\n return () => {\n const off = !interactive();\n const [low, high] = ends();\n const range = Array.isArray(value);\n const thumbs = range ? [low, high] : [high];\n return (\n <div\n {...mergeAttrs<'div'>({ sx: sliderStyles.root }, rest, {\n onTouchstart,\n onTouchmove,\n onTouchend,\n onTouchcancel: onTouchend,\n onClick,\n ...dataAttrs({\n // 小程序:收 touchmove;横滑期间 catch,页面不跟着滚\n 'data-touch-move': horizontal ? 'catch' : true,\n 'data-disabled': disabled ?? form?.disabled,\n }),\n })}\n >\n <div\n {...mergeAttrs<'div'>({ sx: sliderStyles.track }, propsTrack)}\n ref={(node: unknown) => (track = node)}\n >\n <div\n {...mergeAttrs<'div'>({\n sx: sliderStyles.fill,\n style: {\n insetInlineStart: `${range ? percent(low) : 0}%`,\n width: `${percent(high) - (range ? percent(low) : 0)}%`,\n },\n })}\n />\n {thumbs.map((number, index) => (\n <div\n {...mergeAttrs<'div'>(\n { sx: sliderStyles.thumb, style: { insetInlineStart: `${percent(number)}%` } },\n propsThumb,\n {\n role: 'slider',\n tabindex: off ? undefined : 0,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuenow': number,\n 'aria-disabled': off ? 'true' : undefined,\n onKeydown: (event: KeyboardEvent) => onKeydown(range ? index : 1, event),\n ...dataAttrs({ 'data-disabled': off }),\n },\n )}\n />\n ))}\n </div>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type PartProps } from '../utils/props';
|
|
3
|
+
/** 外观 */
|
|
4
|
+
export interface StepperThemes {
|
|
5
|
+
/** 方块按钮,浅灰底(默认) */
|
|
6
|
+
default: unknown;
|
|
7
|
+
/** 圆按钮:减号描边、加号实底 */
|
|
8
|
+
round: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
11
|
+
export interface StepperProps extends Omit<PartProps<'div', 'role'>, 'onChange'> {
|
|
12
|
+
/** 当前的数;双向绑定写 `_value={x}` */
|
|
13
|
+
_value?: number;
|
|
14
|
+
/**
|
|
15
|
+
* 最小值
|
|
16
|
+
* @default 1
|
|
17
|
+
*/
|
|
18
|
+
min?: number;
|
|
19
|
+
/** 最大值 */
|
|
20
|
+
max?: number;
|
|
21
|
+
/**
|
|
22
|
+
* 每次加减多少
|
|
23
|
+
* @default 1
|
|
24
|
+
*/
|
|
25
|
+
step?: number;
|
|
26
|
+
/** 保留几位小数;不给时按 step 的小数位 */
|
|
27
|
+
decimalLength?: number;
|
|
28
|
+
/**
|
|
29
|
+
* 外观
|
|
30
|
+
* @default 'default'
|
|
31
|
+
*/
|
|
32
|
+
theme?: keyof StepperThemes;
|
|
33
|
+
/** 禁用 */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/** 中间的数不能直接输入(只能点加减) */
|
|
36
|
+
disableInput?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 按住加减号时连续变化
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
longPress?: boolean;
|
|
42
|
+
/** 数变了 */
|
|
43
|
+
onChange?: (value: number) => void;
|
|
44
|
+
/** 减号按钮的部件属性 */
|
|
45
|
+
propsMinus?: PartProps<'button'>;
|
|
46
|
+
/** 加号按钮的部件属性 */
|
|
47
|
+
propsPlus?: PartProps<'button'>;
|
|
48
|
+
/** 输入框的部件属性 */
|
|
49
|
+
propsInput?: PartProps<'input', 'value' | 'disabled' | 'readonly' | 'inputmode'>;
|
|
50
|
+
}
|
|
51
|
+
/** Stepper 的样式:root、button、round、plusRound、input */
|
|
52
|
+
export declare const stepperStyles: Readonly<{
|
|
53
|
+
button: css.CompiledStyles;
|
|
54
|
+
input: css.CompiledStyles;
|
|
55
|
+
inputRound: css.CompiledStyles;
|
|
56
|
+
plusRound: css.CompiledStyles;
|
|
57
|
+
root: css.CompiledStyles;
|
|
58
|
+
round: css.CompiledStyles;
|
|
59
|
+
}>;
|
|
60
|
+
/** 步进器(div > 减号 + 输入框 + 加号) */
|
|
61
|
+
export declare const Stepper: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<StepperProps>;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { useConfig } from "../config/context.js";
|
|
2
|
+
import { dataAttrs, mergeAttrs } from "../utils/props.js";
|
|
3
|
+
import { Icon } from "../base/Icon.js";
|
|
4
|
+
import { useForm } from "./context.js";
|
|
5
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
6
|
+
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
7
|
+
import { createVNode, onBeforeUnmount, ref } from "vue";
|
|
8
|
+
import { Minus, Plus } from "@lucide/icons";
|
|
9
|
+
//#region src/form/Stepper.tsx
|
|
10
|
+
/**
|
|
11
|
+
* 步进器:减号、数字、加号,调整数量(购物车的件数、人数)。按住加减号连续变化;中间的数字也可以直接输入,失焦时按范围修正。
|
|
12
|
+
*
|
|
13
|
+
* <Stepper _value={count} min={1} max={99} />
|
|
14
|
+
* <Stepper _value={weight} step={0.5} decimalLength={1} theme="round" />
|
|
15
|
+
*/
|
|
16
|
+
/** 外观 */
|
|
17
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
18
|
+
/** Stepper 的样式:root、button、round、plusRound、input */
|
|
19
|
+
var stepperStyles = css.create({
|
|
20
|
+
root: {
|
|
21
|
+
display: "inline-flex",
|
|
22
|
+
alignItems: "center",
|
|
23
|
+
gap: "0.15em",
|
|
24
|
+
verticalAlign: "middle"
|
|
25
|
+
},
|
|
26
|
+
button: {
|
|
27
|
+
boxSizing: "border-box",
|
|
28
|
+
display: "inline-flex",
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
justifyContent: "center",
|
|
31
|
+
width: "2em",
|
|
32
|
+
height: "2em",
|
|
33
|
+
margin: 0,
|
|
34
|
+
padding: 0,
|
|
35
|
+
borderWidth: 0,
|
|
36
|
+
borderRadius: "_sm",
|
|
37
|
+
backgroundColor: {
|
|
38
|
+
default: "_fill",
|
|
39
|
+
":active:not([disabled])": "_fillActive"
|
|
40
|
+
},
|
|
41
|
+
color: {
|
|
42
|
+
default: "_text",
|
|
43
|
+
"[disabled]": "_textDisabled"
|
|
44
|
+
},
|
|
45
|
+
fontSize: "1em",
|
|
46
|
+
cursor: {
|
|
47
|
+
default: "pointer",
|
|
48
|
+
"[disabled]": "not-allowed"
|
|
49
|
+
},
|
|
50
|
+
userSelect: "none"
|
|
51
|
+
},
|
|
52
|
+
round: {
|
|
53
|
+
borderRadius: "_full",
|
|
54
|
+
borderWidth: "_xs",
|
|
55
|
+
borderStyle: "solid",
|
|
56
|
+
borderColor: {
|
|
57
|
+
default: "_accent",
|
|
58
|
+
"[disabled]": "_border"
|
|
59
|
+
},
|
|
60
|
+
backgroundColor: {
|
|
61
|
+
default: "_surface",
|
|
62
|
+
":active:not([disabled])": "_accentSoft"
|
|
63
|
+
},
|
|
64
|
+
color: {
|
|
65
|
+
default: "_accentText",
|
|
66
|
+
"[disabled]": "_textDisabled"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
plusRound: {
|
|
70
|
+
backgroundColor: {
|
|
71
|
+
default: "_accent",
|
|
72
|
+
":active:not([disabled])": "_accentActive",
|
|
73
|
+
"[disabled]": "_fill"
|
|
74
|
+
},
|
|
75
|
+
color: {
|
|
76
|
+
default: "_accentContrast",
|
|
77
|
+
"[disabled]": "_textDisabled"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
input: {
|
|
81
|
+
boxSizing: "border-box",
|
|
82
|
+
width: "2.6em",
|
|
83
|
+
height: "2em",
|
|
84
|
+
margin: 0,
|
|
85
|
+
padding: 0,
|
|
86
|
+
borderWidth: 0,
|
|
87
|
+
borderRadius: "_sm",
|
|
88
|
+
backgroundColor: "_fill",
|
|
89
|
+
color: {
|
|
90
|
+
default: "_text",
|
|
91
|
+
"[disabled]": "_textDisabled"
|
|
92
|
+
},
|
|
93
|
+
fontFamily: "inherit",
|
|
94
|
+
fontSize: "inherit",
|
|
95
|
+
textAlign: "center",
|
|
96
|
+
outlineStyle: "none"
|
|
97
|
+
},
|
|
98
|
+
inputRound: { backgroundColor: "transparent" }
|
|
99
|
+
});
|
|
100
|
+
/** 按住多久开始连续变化、之后每隔多久变一次(ms) */
|
|
101
|
+
var HOLD_DELAY = 500;
|
|
102
|
+
var REPEAT_INTERVAL = 150;
|
|
103
|
+
/** 小数位:step 是 0.5 时是 1 */
|
|
104
|
+
var decimalsOf = (step) => (String(step).split(".")[1] ?? "").length;
|
|
105
|
+
/** 步进器(div > 减号 + 输入框 + 加号) */
|
|
106
|
+
var Stepper = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
107
|
+
const value = bindable(_props, "_value", () => 1, _ctx.emit);
|
|
108
|
+
const min = readonlyProp(_props, "min", 1);
|
|
109
|
+
const max = readonlyProp(_props, "max", () => Number.POSITIVE_INFINITY);
|
|
110
|
+
const step = readonlyProp(_props, "step", 1);
|
|
111
|
+
const decimalLength = readonlyProp(_props, "decimalLength");
|
|
112
|
+
const theme = readonlyProp(_props, "theme", "default");
|
|
113
|
+
const disabled = readonlyProp(_props, "disabled");
|
|
114
|
+
const disableInput = readonlyProp(_props, "disableInput", false);
|
|
115
|
+
const longPress = readonlyProp(_props, "longPress", true);
|
|
116
|
+
const onChange = readonlyProp(_props, "onChange");
|
|
117
|
+
const propsMinus = readonlyProp(_props, "propsMinus");
|
|
118
|
+
const propsPlus = readonlyProp(_props, "propsPlus");
|
|
119
|
+
const propsInput = readonlyProp(_props, "propsInput");
|
|
120
|
+
const rest = restProps(_props, _ctx, true);
|
|
121
|
+
const config = useConfig();
|
|
122
|
+
const form = useForm();
|
|
123
|
+
let draft = ref(null);
|
|
124
|
+
const digits = () => decimalLength.value ?? decimalsOf(step.value);
|
|
125
|
+
const clamp = (next) => {
|
|
126
|
+
const bounded = Math.min(max.value, Math.max(min.value, next));
|
|
127
|
+
return Number(bounded.toFixed(digits()));
|
|
128
|
+
};
|
|
129
|
+
const set = (next) => {
|
|
130
|
+
const fixed = clamp(next);
|
|
131
|
+
if (fixed === value.value) return;
|
|
132
|
+
value.value = fixed;
|
|
133
|
+
onChange.value?.(fixed);
|
|
134
|
+
};
|
|
135
|
+
const isDisabled = () => disabled.value ?? form?.disabled ?? false;
|
|
136
|
+
let holdTimer;
|
|
137
|
+
let repeatTimer;
|
|
138
|
+
let held = false;
|
|
139
|
+
const stopHold = () => {
|
|
140
|
+
clearTimeout(holdTimer);
|
|
141
|
+
clearInterval(repeatTimer);
|
|
142
|
+
};
|
|
143
|
+
onBeforeUnmount(stopHold);
|
|
144
|
+
const startHold = (direction) => {
|
|
145
|
+
held = false;
|
|
146
|
+
if (!longPress.value) return;
|
|
147
|
+
stopHold();
|
|
148
|
+
holdTimer = setTimeout(() => {
|
|
149
|
+
held = true;
|
|
150
|
+
repeatTimer = setInterval(() => set(value.value + direction * step.value), REPEAT_INTERVAL);
|
|
151
|
+
}, HOLD_DELAY);
|
|
152
|
+
};
|
|
153
|
+
const button = (direction) => {
|
|
154
|
+
const off = isDisabled() || form?.readonly || (direction < 0 ? value.value <= min.value : value.value >= max.value) || false;
|
|
155
|
+
return createVNode("button", mergeAttrs({ sx: [
|
|
156
|
+
stepperStyles.button,
|
|
157
|
+
theme.value === "round" && stepperStyles.round,
|
|
158
|
+
theme.value === "round" && direction > 0 && stepperStyles.plusRound
|
|
159
|
+
] }, direction > 0 ? propsPlus.value : propsMinus.value, {
|
|
160
|
+
type: "button",
|
|
161
|
+
disabled: off,
|
|
162
|
+
"aria-label": direction > 0 ? config.lang.increase : config.lang.decrease,
|
|
163
|
+
onTouchstart: () => startHold(direction),
|
|
164
|
+
onTouchend: stopHold,
|
|
165
|
+
onTouchcancel: stopHold,
|
|
166
|
+
onClick: () => {
|
|
167
|
+
if (held) return void (held = false);
|
|
168
|
+
set(value.value + direction * step.value);
|
|
169
|
+
}
|
|
170
|
+
}), [createVNode(Icon, { "icon": direction > 0 ? Plus : Minus }, null)]);
|
|
171
|
+
};
|
|
172
|
+
return () => {
|
|
173
|
+
const off = isDisabled();
|
|
174
|
+
return createVNode("div", mergeAttrs({ sx: stepperStyles.root }, rest, {
|
|
175
|
+
role: "group",
|
|
176
|
+
...dataAttrs({ "data-disabled": off })
|
|
177
|
+
}), [
|
|
178
|
+
button(-1),
|
|
179
|
+
createVNode("input", mergeAttrs({ sx: [stepperStyles.input, theme.value === "round" && stepperStyles.inputRound] }, propsInput.value, {
|
|
180
|
+
value: draft.value ?? value.value.toFixed(digits()),
|
|
181
|
+
type: "text",
|
|
182
|
+
inputmode: digits() > 0 || min.value < 0 ? "decimal" : "numeric",
|
|
183
|
+
disabled: off,
|
|
184
|
+
readonly: disableInput.value || form?.readonly,
|
|
185
|
+
"aria-valuemin": min.value,
|
|
186
|
+
"aria-valuemax": Number.isFinite(max.value) ? max.value : void 0,
|
|
187
|
+
"aria-valuenow": value.value,
|
|
188
|
+
role: "spinbutton",
|
|
189
|
+
onInput: (event) => draft.value = event.target.value,
|
|
190
|
+
onBlur: () => {
|
|
191
|
+
if (draft.value === null) return;
|
|
192
|
+
const parsed = Number.parseFloat(draft.value);
|
|
193
|
+
draft.value = null;
|
|
194
|
+
if (!Number.isNaN(parsed)) set(parsed);
|
|
195
|
+
}
|
|
196
|
+
}), null),
|
|
197
|
+
button(1)
|
|
198
|
+
]);
|
|
199
|
+
};
|
|
200
|
+
}, {
|
|
201
|
+
name: "Stepper",
|
|
202
|
+
props: [
|
|
203
|
+
"_value",
|
|
204
|
+
"onUpdate:_value",
|
|
205
|
+
"min",
|
|
206
|
+
"max",
|
|
207
|
+
"step",
|
|
208
|
+
"decimalLength",
|
|
209
|
+
"theme",
|
|
210
|
+
"disabled",
|
|
211
|
+
"disableInput",
|
|
212
|
+
"longPress",
|
|
213
|
+
"onChange",
|
|
214
|
+
"propsMinus",
|
|
215
|
+
"propsPlus",
|
|
216
|
+
"propsInput"
|
|
217
|
+
]
|
|
218
|
+
});
|
|
219
|
+
//#endregion
|
|
220
|
+
export { Stepper, stepperStyles };
|
|
221
|
+
|
|
222
|
+
//# sourceMappingURL=Stepper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Stepper.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_createVNode"],"sources":["../../src/form/Stepper.tsx"],"sourcesContent":["/**\n * 步进器:减号、数字、加号,调整数量(购物车的件数、人数)。按住加减号连续变化;中间的数字也可以直接输入,失焦时按范围修正。\n *\n * <Stepper _value={count} min={1} max={99} />\n * <Stepper _value={weight} step={0.5} decimalLength={1} theme=\"round\" />\n */\nimport { Minus, Plus } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { onBeforeUnmount } from 'vue';\nimport { Icon } from '../base/Icon';\nimport { useConfig } from '../config/context';\nimport { dataAttrs, mergeAttrs, type PartProps } from '../utils/props';\nimport { useForm } from './context';\n\n/** 外观 */\nexport interface StepperThemes {\n /** 方块按钮,浅灰底(默认) */\n default: unknown;\n /** 圆按钮:减号描边、加号实底 */\n round: unknown;\n}\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface StepperProps extends Omit<PartProps<'div', 'role'>, 'onChange'> {\n /** 当前的数;双向绑定写 `_value={x}` */\n _value?: number;\n /**\n * 最小值\n * @default 1\n */\n min?: number;\n /** 最大值 */\n max?: number;\n /**\n * 每次加减多少\n * @default 1\n */\n step?: number;\n /** 保留几位小数;不给时按 step 的小数位 */\n decimalLength?: number;\n /**\n * 外观\n * @default 'default'\n */\n theme?: keyof StepperThemes;\n /** 禁用 */\n disabled?: boolean;\n /** 中间的数不能直接输入(只能点加减) */\n disableInput?: boolean;\n /**\n * 按住加减号时连续变化\n * @default true\n */\n longPress?: boolean;\n /** 数变了 */\n onChange?: (value: number) => void;\n /** 减号按钮的部件属性 */\n propsMinus?: PartProps<'button'>;\n /** 加号按钮的部件属性 */\n propsPlus?: PartProps<'button'>;\n /** 输入框的部件属性 */\n propsInput?: PartProps<'input', 'value' | 'disabled' | 'readonly' | 'inputmode'>;\n}\n\n/** Stepper 的样式:root、button、round、plusRound、input */\nexport const stepperStyles = css.create({\n root: { display: 'inline-flex', alignItems: 'center', gap: '0.15em', verticalAlign: 'middle' },\n button: {\n boxSizing: 'border-box',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '2em',\n height: '2em',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n borderRadius: '_sm',\n backgroundColor: { default: '_fill', ':active:not([disabled])': '_fillActive' },\n color: { default: '_text', '[disabled]': '_textDisabled' },\n fontSize: '1em',\n cursor: { default: 'pointer', '[disabled]': 'not-allowed' },\n userSelect: 'none',\n },\n round: {\n borderRadius: '_full',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: { default: '_accent', '[disabled]': '_border' },\n backgroundColor: { default: '_surface', ':active:not([disabled])': '_accentSoft' },\n color: { default: '_accentText', '[disabled]': '_textDisabled' },\n },\n plusRound: {\n backgroundColor: {\n default: '_accent',\n ':active:not([disabled])': '_accentActive',\n '[disabled]': '_fill',\n },\n color: { default: '_accentContrast', '[disabled]': '_textDisabled' },\n },\n input: {\n boxSizing: 'border-box',\n width: '2.6em',\n height: '2em',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n borderRadius: '_sm',\n backgroundColor: '_fill',\n color: { default: '_text', '[disabled]': '_textDisabled' },\n fontFamily: 'inherit',\n fontSize: 'inherit',\n textAlign: 'center',\n outlineStyle: 'none',\n },\n inputRound: { backgroundColor: 'transparent' },\n});\n\n/** 按住多久开始连续变化、之后每隔多久变一次(ms) */\nconst HOLD_DELAY = 500;\nconst REPEAT_INTERVAL = 150;\n\n/** 小数位:step 是 0.5 时是 1 */\nconst decimalsOf = (step: number) => (String(step).split('.')[1] ?? '').length;\n\n/** 步进器(div > 减号 + 输入框 + 加号) */\nexport const Stepper = _component(\n ({\n _value: value = _bindable(1),\n min = 1,\n max = Number.POSITIVE_INFINITY,\n step = 1,\n decimalLength,\n theme = 'default',\n disabled,\n disableInput = false,\n longPress = true,\n onChange,\n propsMinus,\n propsPlus,\n propsInput,\n ...rest\n }: StepperProps) => {\n const config = useConfig();\n const form = useForm();\n // 正在输入的文字:失焦时才换成数\n let draft = _state<string | null>(null);\n const digits = () => decimalLength ?? decimalsOf(step);\n const clamp = (next: number) => {\n const bounded = Math.min(max, Math.max(min, next));\n return Number(bounded.toFixed(digits()));\n };\n const set = (next: number) => {\n const fixed = clamp(next);\n if (fixed === value) return;\n value = fixed;\n onChange?.(fixed);\n };\n const isDisabled = () => disabled ?? form?.disabled ?? false;\n\n // 按住连续加减;连续变化过之后松手,那一次点击不再算\n let holdTimer: ReturnType<typeof setTimeout> | undefined;\n let repeatTimer: ReturnType<typeof setInterval> | undefined;\n let held = false;\n const stopHold = () => {\n clearTimeout(holdTimer);\n clearInterval(repeatTimer);\n };\n onBeforeUnmount(stopHold);\n const startHold = (direction: 1 | -1) => {\n held = false;\n if (!longPress) return;\n stopHold();\n holdTimer = setTimeout(() => {\n held = true;\n repeatTimer = setInterval(() => set(value + direction * step), REPEAT_INTERVAL);\n }, HOLD_DELAY);\n };\n\n const button = (direction: 1 | -1) => {\n const off =\n isDisabled() || form?.readonly || (direction < 0 ? value <= min : value >= max) || false;\n return (\n <button\n {...mergeAttrs<'button'>(\n {\n sx: [\n stepperStyles.button,\n theme === 'round' && stepperStyles.round,\n theme === 'round' && direction > 0 && stepperStyles.plusRound,\n ],\n },\n direction > 0 ? propsPlus : propsMinus,\n {\n type: 'button',\n disabled: off,\n 'aria-label': direction > 0 ? config.lang.increase : config.lang.decrease,\n onTouchstart: () => startHold(direction),\n onTouchend: stopHold,\n onTouchcancel: stopHold,\n onClick: () => {\n if (held) return void (held = false);\n set(value + direction * step);\n },\n },\n )}\n >\n <Icon icon={direction > 0 ? Plus : Minus} />\n </button>\n );\n };\n\n return () => {\n const off = isDisabled();\n return (\n <div\n {...mergeAttrs<'div'>({ sx: stepperStyles.root }, rest, {\n role: 'group',\n ...dataAttrs({ 'data-disabled': off }),\n })}\n >\n {button(-1)}\n <input\n {...mergeAttrs<'input'>(\n { sx: [stepperStyles.input, theme === 'round' && stepperStyles.inputRound] },\n propsInput,\n {\n value: draft ?? value.toFixed(digits()),\n type: 'text',\n inputmode: digits() > 0 || min < 0 ? 'decimal' : 'numeric',\n disabled: off,\n readonly: disableInput || form?.readonly,\n 'aria-valuemin': min,\n 'aria-valuemax': Number.isFinite(max) ? max : undefined,\n 'aria-valuenow': value,\n role: 'spinbutton',\n onInput: (event: Event) => (draft = (event.target as HTMLInputElement).value),\n onBlur: () => {\n if (draft === null) return;\n const parsed = Number.parseFloat(draft);\n draft = null;\n if (!Number.isNaN(parsed)) set(parsed);\n },\n },\n )}\n />\n {button(1)}\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type PartProps } from '../utils/props';
|
|
3
|
+
/** 角色与状态由组件管理 */
|
|
4
|
+
export type SwitchManaged = 'role' | 'aria-checked' | 'type' | 'disabled';
|
|
5
|
+
/** 未声明的属性落到按钮上 */
|
|
6
|
+
export interface SwitchProps extends Omit<PartProps<'button', SwitchManaged>, 'onChange'> {
|
|
7
|
+
/** 是否打开;双向绑定写 `_checked={x}` */
|
|
8
|
+
_checked?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 打开时的色调
|
|
11
|
+
* @default 'accent'
|
|
12
|
+
*/
|
|
13
|
+
tone?: 'accent' | 'success' | 'warning' | 'danger';
|
|
14
|
+
/** 加载中:圆点里转圈,不能切换 */
|
|
15
|
+
loading?: boolean;
|
|
16
|
+
/** 禁用;在 Form 里时随 Form */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** 切换了 */
|
|
19
|
+
onChange?: (checked: boolean) => void;
|
|
20
|
+
/** 圆点的部件属性 */
|
|
21
|
+
propsThumb?: PartProps<'span'>;
|
|
22
|
+
}
|
|
23
|
+
/** Switch 的样式:root、各色调、thumb、spinner */
|
|
24
|
+
export declare const switchStyles: Readonly<{
|
|
25
|
+
accent: css.CompiledStyles;
|
|
26
|
+
danger: css.CompiledStyles;
|
|
27
|
+
root: css.CompiledStyles;
|
|
28
|
+
success: css.CompiledStyles;
|
|
29
|
+
thumb: css.CompiledStyles;
|
|
30
|
+
warning: css.CompiledStyles;
|
|
31
|
+
}>;
|
|
32
|
+
/** 开关(button[role=switch] > 圆点) */
|
|
33
|
+
export declare const Switch: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<SwitchProps>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { dataAttrs, mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { Loading } from "../feedback/Loading.js";
|
|
3
|
+
import { useForm } from "./context.js";
|
|
4
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
5
|
+
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
6
|
+
import { createVNode } from "vue";
|
|
7
|
+
//#region src/form/Switch.tsx
|
|
8
|
+
/** 角色与状态由组件管理 */
|
|
9
|
+
/** 未声明的属性落到按钮上 */
|
|
10
|
+
/** Switch 的样式:root、各色调、thumb、spinner */
|
|
11
|
+
var switchStyles = css.create({
|
|
12
|
+
root: {
|
|
13
|
+
boxSizing: "border-box",
|
|
14
|
+
position: "relative",
|
|
15
|
+
display: "inline-block",
|
|
16
|
+
flex: "none",
|
|
17
|
+
width: "3.1em",
|
|
18
|
+
height: "1.85em",
|
|
19
|
+
margin: 0,
|
|
20
|
+
padding: 0,
|
|
21
|
+
borderWidth: 0,
|
|
22
|
+
borderRadius: "_full",
|
|
23
|
+
verticalAlign: "middle",
|
|
24
|
+
cursor: {
|
|
25
|
+
default: "pointer",
|
|
26
|
+
"[data-disabled]": "not-allowed"
|
|
27
|
+
},
|
|
28
|
+
opacity: {
|
|
29
|
+
default: 1,
|
|
30
|
+
"[data-disabled]": "_disabled"
|
|
31
|
+
},
|
|
32
|
+
transitionProperty: "background-color",
|
|
33
|
+
transitionDuration: "_md"
|
|
34
|
+
},
|
|
35
|
+
accent: { backgroundColor: {
|
|
36
|
+
default: "_fillActive",
|
|
37
|
+
"[aria-checked=\"true\"]": "_accent"
|
|
38
|
+
} },
|
|
39
|
+
success: { backgroundColor: {
|
|
40
|
+
default: "_fillActive",
|
|
41
|
+
"[aria-checked=\"true\"]": "_success"
|
|
42
|
+
} },
|
|
43
|
+
warning: { backgroundColor: {
|
|
44
|
+
default: "_fillActive",
|
|
45
|
+
"[aria-checked=\"true\"]": "_warning"
|
|
46
|
+
} },
|
|
47
|
+
danger: { backgroundColor: {
|
|
48
|
+
default: "_fillActive",
|
|
49
|
+
"[aria-checked=\"true\"]": "_danger"
|
|
50
|
+
} },
|
|
51
|
+
thumb: {
|
|
52
|
+
boxSizing: "border-box",
|
|
53
|
+
position: "absolute",
|
|
54
|
+
insetBlockStart: "0.15em",
|
|
55
|
+
insetInlineStart: "0.15em",
|
|
56
|
+
display: "flex",
|
|
57
|
+
alignItems: "center",
|
|
58
|
+
justifyContent: "center",
|
|
59
|
+
width: "1.55em",
|
|
60
|
+
height: "1.55em",
|
|
61
|
+
borderRadius: "_full",
|
|
62
|
+
backgroundColor: "#fff",
|
|
63
|
+
boxShadow: "_sm",
|
|
64
|
+
color: "_accent",
|
|
65
|
+
fontSize: "1em",
|
|
66
|
+
transform: {
|
|
67
|
+
default: "none",
|
|
68
|
+
"[data-checked]": "translateX(1.25em)"
|
|
69
|
+
},
|
|
70
|
+
transitionProperty: "transform",
|
|
71
|
+
transitionDuration: "_md",
|
|
72
|
+
transitionTimingFunction: "_standard"
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
/** 开关(button[role=switch] > 圆点) */
|
|
76
|
+
var Switch = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
77
|
+
const checked = bindable(_props, "_checked", () => false, _ctx.emit);
|
|
78
|
+
const tone = readonlyProp(_props, "tone", "accent");
|
|
79
|
+
const loading = readonlyProp(_props, "loading", false);
|
|
80
|
+
const disabled = readonlyProp(_props, "disabled");
|
|
81
|
+
const onChange = readonlyProp(_props, "onChange");
|
|
82
|
+
const propsThumb = readonlyProp(_props, "propsThumb");
|
|
83
|
+
const rest = restProps(_props, _ctx, true);
|
|
84
|
+
const form = useForm();
|
|
85
|
+
return () => {
|
|
86
|
+
const off = disabled.value ?? form?.disabled ?? false;
|
|
87
|
+
return createVNode("button", mergeAttrs({ sx: [switchStyles.root, switchStyles[tone.value]] }, rest, {
|
|
88
|
+
type: "button",
|
|
89
|
+
role: "switch",
|
|
90
|
+
"aria-checked": checked.value ? "true" : "false",
|
|
91
|
+
"aria-busy": loading.value ? "true" : void 0,
|
|
92
|
+
disabled: off,
|
|
93
|
+
onClick: () => {
|
|
94
|
+
if (loading.value || form?.readonly) return;
|
|
95
|
+
checked.value = !checked.value;
|
|
96
|
+
onChange.value?.(checked.value);
|
|
97
|
+
},
|
|
98
|
+
...dataAttrs({ "data-disabled": off })
|
|
99
|
+
}), [createVNode("span", mergeAttrs({ sx: switchStyles.thumb }, propsThumb.value, dataAttrs({ "data-checked": checked.value })), [loading.value && createVNode(Loading, { "size": 14 }, null)])]);
|
|
100
|
+
};
|
|
101
|
+
}, {
|
|
102
|
+
name: "Switch",
|
|
103
|
+
props: [
|
|
104
|
+
"_checked",
|
|
105
|
+
"onUpdate:_checked",
|
|
106
|
+
"tone",
|
|
107
|
+
"loading",
|
|
108
|
+
"disabled",
|
|
109
|
+
"onChange",
|
|
110
|
+
"propsThumb"
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
//#endregion
|
|
114
|
+
export { Switch, switchStyles };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=Switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Switch.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_restProps","_createVNode"],"sources":["../../src/form/Switch.tsx"],"sourcesContent":["/**\n * 开关:两种状态之间切换,立刻生效(消息通知、深色模式)。要提交了才生效的选项用 Checkbox。\n *\n * <Switch _checked={notify} aria-label=\"消息通知\" />\n * <Switch _checked={autoRenew} loading={saving} onChange={(on) => save(on)} />\n *\n * 大小随所在文字(em),整体缩放写 sx 的 fontSize。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { Loading } from '../feedback/Loading';\nimport { useForm } from './context';\nimport { dataAttrs, mergeAttrs, type PartProps } from '../utils/props';\n\n/** 角色与状态由组件管理 */\nexport type SwitchManaged = 'role' | 'aria-checked' | 'type' | 'disabled';\n\n/** 未声明的属性落到按钮上 */\nexport interface SwitchProps extends Omit<PartProps<'button', SwitchManaged>, 'onChange'> {\n /** 是否打开;双向绑定写 `_checked={x}` */\n _checked?: boolean;\n /**\n * 打开时的色调\n * @default 'accent'\n */\n tone?: 'accent' | 'success' | 'warning' | 'danger';\n /** 加载中:圆点里转圈,不能切换 */\n loading?: boolean;\n /** 禁用;在 Form 里时随 Form */\n disabled?: boolean;\n /** 切换了 */\n onChange?: (checked: boolean) => void;\n /** 圆点的部件属性 */\n propsThumb?: PartProps<'span'>;\n}\n\n/** Switch 的样式:root、各色调、thumb、spinner */\nexport const switchStyles = css.create({\n root: {\n boxSizing: 'border-box',\n position: 'relative',\n display: 'inline-block',\n flex: 'none',\n width: '3.1em',\n height: '1.85em',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n borderRadius: '_full',\n verticalAlign: 'middle',\n cursor: { default: 'pointer', '[data-disabled]': 'not-allowed' },\n opacity: { default: 1, '[data-disabled]': '_disabled' },\n transitionProperty: 'background-color',\n transitionDuration: '_md',\n },\n accent: { backgroundColor: { default: '_fillActive', '[aria-checked=\"true\"]': '_accent' } },\n success: { backgroundColor: { default: '_fillActive', '[aria-checked=\"true\"]': '_success' } },\n warning: { backgroundColor: { default: '_fillActive', '[aria-checked=\"true\"]': '_warning' } },\n danger: { backgroundColor: { default: '_fillActive', '[aria-checked=\"true\"]': '_danger' } },\n thumb: {\n boxSizing: 'border-box',\n position: 'absolute',\n insetBlockStart: '0.15em',\n insetInlineStart: '0.15em',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1.55em',\n height: '1.55em',\n borderRadius: '_full',\n // 圆点在亮暗两套主题里都是白的(同系统的开关)\n backgroundColor: '#fff',\n boxShadow: '_sm',\n color: '_accent',\n fontSize: '1em',\n transform: { default: 'none', '[data-checked]': 'translateX(1.25em)' },\n transitionProperty: 'transform',\n transitionDuration: '_md',\n transitionTimingFunction: '_standard',\n },\n});\n\n/** 开关(button[role=switch] > 圆点) */\nexport const Switch = _component(\n ({\n _checked: checked = _bindable(false),\n tone = 'accent',\n loading = false,\n disabled,\n onChange,\n propsThumb,\n ...rest\n }: SwitchProps) => {\n const form = useForm();\n return () => {\n const off = disabled ?? form?.disabled ?? false;\n return (\n <button\n {...mergeAttrs<'button'>({ sx: [switchStyles.root, switchStyles[tone]] }, rest, {\n type: 'button',\n role: 'switch',\n 'aria-checked': checked ? 'true' : 'false',\n 'aria-busy': loading ? 'true' : undefined,\n disabled: off,\n onClick: () => {\n if (loading || form?.readonly) return;\n checked = !checked;\n onChange?.(checked);\n },\n ...dataAttrs({ 'data-disabled': off }),\n })}\n >\n <span\n {...mergeAttrs<'span'>(\n { sx: switchStyles.thumb },\n propsThumb,\n dataAttrs({ 'data-checked': checked }),\n )}\n >\n {loading && <Loading size={14} />}\n </span>\n </button>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;AAqCA,IAAa,eAAe,IAAI,OAAO;CACrC,MAAM;EACJ,WAAW;EACX,UAAU;EACV,SAAS;EACT,MAAM;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,aAAa;EACb,cAAc;EACd,eAAe;EACf,QAAQ;GAAE,SAAS;GAAW,mBAAmB;EAAc;EAC/D,SAAS;GAAE,SAAS;GAAG,mBAAmB;EAAY;EACtD,oBAAoB;EACpB,oBAAoB;CACtB;CACA,QAAQ,EAAE,iBAAiB;EAAE,SAAS;EAAe,2BAAyB;CAAU,EAAE;CAC1F,SAAS,EAAE,iBAAiB;EAAE,SAAS;EAAe,2BAAyB;CAAW,EAAE;CAC5F,SAAS,EAAE,iBAAiB;EAAE,SAAS;EAAe,2BAAyB;CAAW,EAAE;CAC5F,QAAQ,EAAE,iBAAiB;EAAE,SAAS;EAAe,2BAAyB;CAAU,EAAE;CAC1F,OAAO;EACL,WAAW;EACX,UAAU;EACV,iBAAiB;EACjB,kBAAkB;EAClB,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,OAAO;EACP,QAAQ;EACR,cAAc;EAEd,iBAAiB;EACjB,WAAW;EACX,OAAO;EACP,UAAU;EACV,WAAW;GAAE,SAAS;GAAQ,kBAAkB;EAAqB;EACrE,oBAAoB;EACpB,oBAAoB;EACpB,0BAA0B;CAC5B;AACF,CAAC;;AAGD,IAAa,SAAS,0BACpB,QAAA,SAQmB;CAAA,MAAA,UAAAA,SAAA,QAAA,kBAPa,OAAK,KAAA,IAAA;CAAA,MAAA,OAAAC,aAAA,QAAA,QAC5B,QAAQ;CAAA,MAAA,UAAAA,aAAA,QAAA,WACL,KAAK;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAIZ,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAO,QAAQ;CACrB,aAAa;EACX,MAAM,MAAM,SAAA,SAAY,MAAM,YAAY;EAC1C,OAAAC,YAAA,UAEQ,WAAqB,EAAE,IAAI,CAAC,aAAa,MAAM,aAAY,KAAA,MAAM,EAAE,GAAG,MAAM;GAC9E,MAAM;GACN,MAAM;GACN,gBAAgB,QAAA,QAAU,SAAS;GACnC,aAAa,QAAA,QAAU,SAAS,KAAA;GAChC,UAAU;GACV,eAAe;IACb,IAAI,QAAA,SAAW,MAAM,UAAU;IAC/B,QAAA,QAAU,CAAA,QAAA;IACV,SAAA,QAAA,QAAA,KAAkB;GACpB;GACA,GAAG,UAAU,EAAE,iBAAiB,IAAI,CAAC;EACvC,CAAC,GAAC,CAAAA,YAAA,QAGI,WACF,EAAE,IAAI,aAAa,MAAM,GAAC,WAAA,OAE1B,UAAU,EAAE,gBAAc,QAAA,MAAU,CAAC,CACvC,GAAC,CAEA,QAAA,SAAAA,YAAA,SAAA,EAAA,QAA0B,GAAE,GAAA,IAAA,CAAI,CAAA,CAAA,CAAA;CAIzC;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type VNodeChild } from 'vue';
|
|
3
|
+
import { type PartProps } from '../utils/props';
|
|
4
|
+
/** 一个文件 */
|
|
5
|
+
export interface UploaderFile {
|
|
6
|
+
/** 预览(及上传完)的地址 */
|
|
7
|
+
url: string;
|
|
8
|
+
/** H5 里选到的 File,上传时用;小程序里没有(用 url 这个临时路径上传) */
|
|
9
|
+
file?: File;
|
|
10
|
+
/** 字节数 */
|
|
11
|
+
size?: number;
|
|
12
|
+
/** 上传的状态;不给时是已完成 */
|
|
13
|
+
status?: 'uploading' | 'failed' | 'done';
|
|
14
|
+
/** 状态下面的文字(“上传中”“上传失败”) */
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
18
|
+
export interface UploaderProps extends Omit<PartProps<'div'>, 'onChange'> {
|
|
19
|
+
/** 已选的文件;双向绑定写 `_value={x}` */
|
|
20
|
+
_value?: UploaderFile[];
|
|
21
|
+
/** 最多几张 */
|
|
22
|
+
maxCount?: number;
|
|
23
|
+
/** 单张最大字节数;超过的不加进来,交给 onOversize */
|
|
24
|
+
maxSize?: number;
|
|
25
|
+
/**
|
|
26
|
+
* 一次可以选多张
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
multiple?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 能删除
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
deletable?: boolean;
|
|
35
|
+
/** 禁用 */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 方格的边长(CSS 长度)
|
|
39
|
+
* @default '5em'
|
|
40
|
+
*/
|
|
41
|
+
previewSize?: string;
|
|
42
|
+
/** 选图那一格的文字;不给时用语言包的“上传图片” */
|
|
43
|
+
uploadText?: VNodeChild;
|
|
44
|
+
/** 选好了:新加的文件(已经在 _value 里,是响应式的,改 status 会刷新) */
|
|
45
|
+
onRead?: (files: UploaderFile[]) => void;
|
|
46
|
+
/** 有文件超过 maxSize */
|
|
47
|
+
onOversize?: (files: UploaderFile[]) => void;
|
|
48
|
+
/** 删除了一张 */
|
|
49
|
+
onDelete?: (file: UploaderFile, index: number) => void;
|
|
50
|
+
/** 点了一张预览 */
|
|
51
|
+
onPreview?: (file: UploaderFile, index: number) => void;
|
|
52
|
+
/** 每一格预览的部件属性 */
|
|
53
|
+
propsItem?: PartProps<'div'>;
|
|
54
|
+
/** 选图那一格的部件属性 */
|
|
55
|
+
propsUpload?: PartProps<'button'>;
|
|
56
|
+
}
|
|
57
|
+
/** Uploader 的样式:root、item、mask、remove、upload */
|
|
58
|
+
export declare const uploaderStyles: Readonly<{
|
|
59
|
+
item: css.CompiledStyles;
|
|
60
|
+
mask: css.CompiledStyles;
|
|
61
|
+
remove: css.CompiledStyles;
|
|
62
|
+
root: css.CompiledStyles;
|
|
63
|
+
upload: css.CompiledStyles;
|
|
64
|
+
uploadIcon: css.CompiledStyles;
|
|
65
|
+
}>;
|
|
66
|
+
/** 图片上传(div > 预览的方格 + 选图的一格) */
|
|
67
|
+
export declare const Uploader: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<UploaderProps>;
|