@zerodep-dev/vue-tsx-pro-mobile 0.0.0-stage → 0.4.1
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,185 @@
|
|
|
1
|
+
import { t } from "../theme/tokens.js";
|
|
2
|
+
import { useConfig } from "../config/context.js";
|
|
3
|
+
import { hasContent, mergeAttrs } from "../utils/props.js";
|
|
4
|
+
import { PICKER_ITEM_HEIGHT, PickerColumn } from "./PickerColumn.js";
|
|
5
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
6
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
7
|
+
import { computed, createVNode } from "vue";
|
|
8
|
+
//#region src/picker/Picker.tsx
|
|
9
|
+
/** 选项的值 */
|
|
10
|
+
/** 确认、变化时给出的 */
|
|
11
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
12
|
+
/** Picker 的样式:root、toolbar、title、action、confirm、columns、mask、indicator */
|
|
13
|
+
var pickerStyles = css.create({
|
|
14
|
+
root: {
|
|
15
|
+
position: "relative",
|
|
16
|
+
backgroundColor: "_surface",
|
|
17
|
+
userSelect: "none"
|
|
18
|
+
},
|
|
19
|
+
toolbar: {
|
|
20
|
+
display: "flex",
|
|
21
|
+
alignItems: "center",
|
|
22
|
+
justifyContent: "space-between",
|
|
23
|
+
height: "3em"
|
|
24
|
+
},
|
|
25
|
+
title: {
|
|
26
|
+
flex: "1 1 auto",
|
|
27
|
+
textAlign: "center",
|
|
28
|
+
fontSize: "_lg",
|
|
29
|
+
fontWeight: "_semibold",
|
|
30
|
+
color: "_text"
|
|
31
|
+
},
|
|
32
|
+
action: {
|
|
33
|
+
flex: "none",
|
|
34
|
+
display: "inline-flex",
|
|
35
|
+
alignItems: "center",
|
|
36
|
+
height: "100%",
|
|
37
|
+
margin: 0,
|
|
38
|
+
paddingBlock: 0,
|
|
39
|
+
paddingInline: "_lg",
|
|
40
|
+
borderWidth: 0,
|
|
41
|
+
backgroundColor: "transparent",
|
|
42
|
+
color: {
|
|
43
|
+
default: "_textMuted",
|
|
44
|
+
":active": "_text"
|
|
45
|
+
},
|
|
46
|
+
fontFamily: "inherit",
|
|
47
|
+
fontSize: "_md",
|
|
48
|
+
cursor: "pointer"
|
|
49
|
+
},
|
|
50
|
+
confirm: { color: {
|
|
51
|
+
default: "_accentText",
|
|
52
|
+
":active": "_accentActive"
|
|
53
|
+
} },
|
|
54
|
+
columns: {
|
|
55
|
+
position: "relative",
|
|
56
|
+
display: "flex",
|
|
57
|
+
fontSize: "_md"
|
|
58
|
+
},
|
|
59
|
+
mask: {
|
|
60
|
+
position: "absolute",
|
|
61
|
+
inset: 0,
|
|
62
|
+
zIndex: 1,
|
|
63
|
+
pointerEvents: "none",
|
|
64
|
+
backgroundImage: `linear-gradient(180deg, ${t.colors.surface}, transparent), linear-gradient(0deg, ${t.colors.surface}, transparent)`,
|
|
65
|
+
backgroundPosition: "top, bottom",
|
|
66
|
+
backgroundRepeat: "no-repeat"
|
|
67
|
+
},
|
|
68
|
+
indicator: {
|
|
69
|
+
position: "absolute",
|
|
70
|
+
insetInline: "_lg",
|
|
71
|
+
zIndex: 2,
|
|
72
|
+
height: `${PICKER_ITEM_HEIGHT}em`,
|
|
73
|
+
pointerEvents: "none",
|
|
74
|
+
borderBlockWidth: "_hairline",
|
|
75
|
+
borderBlockStyle: "solid",
|
|
76
|
+
borderBlockColor: "_border"
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
var isMulti = (columns) => Array.isArray(columns[0]);
|
|
80
|
+
/** 按选中的值算出各列(联动时跟着上一列的选择往下展开)与各列选中的序号 */
|
|
81
|
+
function resolveColumns(columns, values) {
|
|
82
|
+
const lists = [];
|
|
83
|
+
if (isMulti(columns)) lists.push(...columns);
|
|
84
|
+
else {
|
|
85
|
+
let list = columns;
|
|
86
|
+
for (let depth = 0; list?.length; depth++) {
|
|
87
|
+
lists.push(list);
|
|
88
|
+
list = (list.find((option) => option.value === values[depth] && !option.disabled) ?? list.find((option) => !option.disabled))?.children;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const indexes = lists.map((list, depth) => {
|
|
92
|
+
const found = list.findIndex((option) => option.value === values[depth] && !option.disabled);
|
|
93
|
+
return found >= 0 ? found : Math.max(0, list.findIndex((option) => !option.disabled));
|
|
94
|
+
});
|
|
95
|
+
const options = lists.map((list, depth) => list[indexes[depth]]).filter(Boolean);
|
|
96
|
+
return {
|
|
97
|
+
lists,
|
|
98
|
+
indexes,
|
|
99
|
+
options,
|
|
100
|
+
values: options.map((option) => option.value)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** 选择器(div > 标题栏 + 各列滚轮) */
|
|
104
|
+
var Picker = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
105
|
+
const _cache = [];
|
|
106
|
+
const columns = readonlyProp(_props, "columns");
|
|
107
|
+
const value = bindable(_props, "_value", () => [], _ctx.emit);
|
|
108
|
+
const title = readonlyProp(_props, "title");
|
|
109
|
+
const showToolbar = readonlyProp(_props, "showToolbar", true);
|
|
110
|
+
const confirmText = readonlyProp(_props, "confirmText");
|
|
111
|
+
const cancelText = readonlyProp(_props, "cancelText");
|
|
112
|
+
const visibleItemCount = readonlyProp(_props, "visibleItemCount", 5);
|
|
113
|
+
const onConfirm = readonlyProp(_props, "onConfirm");
|
|
114
|
+
const onCancel = readonlyProp(_props, "onCancel");
|
|
115
|
+
const onChange = readonlyProp(_props, "onChange");
|
|
116
|
+
const columnLabels = readonlyProp(_props, "columnLabels");
|
|
117
|
+
const rest = restProps(_props, _ctx, true);
|
|
118
|
+
const config = useConfig();
|
|
119
|
+
const resolved = computed(() => resolveColumns(columns.value, value.value));
|
|
120
|
+
const select = (columnIndex, index) => {
|
|
121
|
+
const next = [...resolved.value.values];
|
|
122
|
+
next[columnIndex] = resolved.value.lists[columnIndex][index].value;
|
|
123
|
+
const settled = resolveColumns(columns.value, isMulti(columns.value) ? next : next.slice(0, columnIndex + 1));
|
|
124
|
+
value.value = settled.values;
|
|
125
|
+
onChange.value?.({
|
|
126
|
+
values: settled.values,
|
|
127
|
+
options: settled.options,
|
|
128
|
+
columnIndex
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
return () => {
|
|
132
|
+
const visible = visibleItemCount.value % 2 ? visibleItemCount.value : visibleItemCount.value + 1;
|
|
133
|
+
const fade = `100% ${(visible - 1) / 2 * PICKER_ITEM_HEIGHT}em`;
|
|
134
|
+
return createVNode("div", mergeAttrs({ sx: pickerStyles.root }, rest), [showToolbar.value && createVNode("div", mergeAttrs({ sx: pickerStyles.toolbar }), [
|
|
135
|
+
createVNode("button", mergeJsxProps(mergeAttrs({ sx: pickerStyles.action }), {
|
|
136
|
+
"type": "button",
|
|
137
|
+
"onClick": _cache[0] || (_cache[0] = () => onCancel.value?.())
|
|
138
|
+
}), [cancelText.value ?? config.lang.cancel]),
|
|
139
|
+
hasContent(title.value) && createVNode("div", mergeAttrs({ sx: pickerStyles.title }), [title.value]),
|
|
140
|
+
createVNode("button", mergeJsxProps(mergeAttrs({ sx: [pickerStyles.action, pickerStyles.confirm] }), {
|
|
141
|
+
"type": "button",
|
|
142
|
+
"onClick": _cache[1] || (_cache[1] = () => onConfirm.value?.({
|
|
143
|
+
values: resolved.value.values,
|
|
144
|
+
options: resolved.value.options
|
|
145
|
+
}))
|
|
146
|
+
}), [confirmText.value ?? config.lang.confirm])
|
|
147
|
+
]), createVNode("div", mergeAttrs({ sx: pickerStyles.columns }), [
|
|
148
|
+
resolved.value.lists.map((list, columnIndex) => createVNode(PickerColumn, {
|
|
149
|
+
"options": list,
|
|
150
|
+
"index": resolved.value.indexes[columnIndex],
|
|
151
|
+
"visible": visible,
|
|
152
|
+
"label": columnLabels.value?.[columnIndex],
|
|
153
|
+
"onSelect": (index) => select(columnIndex, index)
|
|
154
|
+
}, null)),
|
|
155
|
+
createVNode("div", mergeAttrs({
|
|
156
|
+
sx: pickerStyles.mask,
|
|
157
|
+
style: { backgroundSize: `${fade}, ${fade}` }
|
|
158
|
+
}), null),
|
|
159
|
+
createVNode("div", mergeAttrs({
|
|
160
|
+
sx: pickerStyles.indicator,
|
|
161
|
+
style: { insetBlockStart: `${(visible - 1) / 2 * PICKER_ITEM_HEIGHT}em` }
|
|
162
|
+
}), null)
|
|
163
|
+
])]);
|
|
164
|
+
};
|
|
165
|
+
}, {
|
|
166
|
+
name: "Picker",
|
|
167
|
+
props: [
|
|
168
|
+
"columns",
|
|
169
|
+
"_value",
|
|
170
|
+
"onUpdate:_value",
|
|
171
|
+
"title",
|
|
172
|
+
"showToolbar",
|
|
173
|
+
"confirmText",
|
|
174
|
+
"cancelText",
|
|
175
|
+
"visibleItemCount",
|
|
176
|
+
"onConfirm",
|
|
177
|
+
"onCancel",
|
|
178
|
+
"onChange",
|
|
179
|
+
"columnLabels"
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
//#endregion
|
|
183
|
+
export { Picker, pickerStyles, resolveColumns };
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=Picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Picker.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/picker/Picker.tsx"],"sourcesContent":["/**\n * 选择器:一列或几列滚轮,上面一条标题栏(取消、标题、确认)。常放在底部弹出层里。\n *\n * <Popup _open={show} position=\"bottom\" round>\n * <Picker title=\"城市\" columns={cities} _value={city} onConfirm={({ values }) => …} onCancel={() => (show = false)} />\n * </Popup>\n *\n * columns 三种写法:一列(选项数组)、几列各自独立(选项数组的数组)、联动(选项带 children:省 → 市 → 区)。\n * _value 是各列选中的值;没给或对不上时选每列第一个可选的。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { useConfig } from '../config/context';\nimport { t } from '../theme/tokens';\nimport { hasContent, mergeAttrs, type PartProps } from '../utils/props';\nimport { PICKER_ITEM_HEIGHT, PickerColumn, type PickerOption } from './PickerColumn';\n\nexport type { PickerOption } from './PickerColumn';\n\n/** 选项的值 */\nexport type PickerValue = string | number;\n\n/** 确认、变化时给出的 */\nexport interface PickerResult {\n /** 各列选中的值 */\n values: PickerValue[];\n /** 各列选中的选项 */\n options: PickerOption[];\n}\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface PickerProps extends Omit<PartProps<'div', 'title'>, 'onChange'> {\n /** 选项:一列、几列(数组的数组)、联动(选项带 children) */\n columns: PickerOption[] | PickerOption[][];\n /** 各列选中的值;双向绑定写 `_value={x}`(滚动时就写回,确认时 onConfirm) */\n _value?: PickerValue[];\n /** 标题 */\n title?: VNodeChild;\n /**\n * 显示标题栏\n * @default true\n */\n showToolbar?: boolean;\n /** 确认的文字;不给时用语言包 */\n confirmText?: VNodeChild;\n /** 取消的文字;不给时用语言包 */\n cancelText?: VNodeChild;\n /**\n * 可见的行数(奇数)\n * @default 5\n */\n visibleItemCount?: number;\n /** 点了确认 */\n onConfirm?: (result: PickerResult) => void;\n /** 点了取消 */\n onCancel?: () => void;\n /** 滚到另一项 */\n onChange?: (result: PickerResult & { columnIndex: number }) => void;\n /** 各列的读屏名称 */\n columnLabels?: string[];\n}\n\n/** Picker 的样式:root、toolbar、title、action、confirm、columns、mask、indicator */\nexport const pickerStyles = css.create({\n root: { position: 'relative', backgroundColor: '_surface', userSelect: 'none' },\n toolbar: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: '3em',\n },\n title: {\n flex: '1 1 auto',\n textAlign: 'center',\n fontSize: '_lg',\n fontWeight: '_semibold',\n color: '_text',\n },\n action: {\n flex: 'none',\n // 小程序里按钮是 view:文字要自己居中\n display: 'inline-flex',\n alignItems: 'center',\n height: '100%',\n margin: 0,\n paddingBlock: 0,\n paddingInline: '_lg',\n borderWidth: 0,\n backgroundColor: 'transparent',\n color: { default: '_textMuted', ':active': '_text' },\n fontFamily: 'inherit',\n fontSize: '_md',\n cursor: 'pointer',\n },\n confirm: { color: { default: '_accentText', ':active': '_accentActive' } },\n columns: { position: 'relative', display: 'flex', fontSize: '_md' },\n // 上下渐隐到底色,中间一行留白;选中的那一行上下各一条细线\n mask: {\n position: 'absolute',\n inset: 0,\n zIndex: 1,\n pointerEvents: 'none',\n backgroundImage: `linear-gradient(180deg, ${t.colors.surface}, transparent), linear-gradient(0deg, ${t.colors.surface}, transparent)`,\n backgroundPosition: 'top, bottom',\n backgroundRepeat: 'no-repeat',\n },\n indicator: {\n position: 'absolute',\n insetInline: '_lg',\n zIndex: 2,\n height: `${PICKER_ITEM_HEIGHT}em`,\n pointerEvents: 'none',\n borderBlockWidth: '_hairline',\n borderBlockStyle: 'solid',\n borderBlockColor: '_border',\n },\n});\n\nconst isMulti = (columns: PickerOption[] | PickerOption[][]): columns is PickerOption[][] =>\n Array.isArray(columns[0]);\n\n/** 按选中的值算出各列(联动时跟着上一列的选择往下展开)与各列选中的序号 */\nexport function resolveColumns(columns: PickerOption[] | PickerOption[][], values: PickerValue[]) {\n const lists: PickerOption[][] = [];\n if (isMulti(columns)) lists.push(...columns);\n else {\n let list: PickerOption[] | undefined = columns;\n for (let depth = 0; list?.length; depth++) {\n lists.push(list);\n const chosen: PickerOption | undefined =\n list.find((option) => option.value === values[depth] && !option.disabled) ??\n list.find((option) => !option.disabled);\n list = chosen?.children;\n }\n }\n const indexes = lists.map((list, depth) => {\n const found = list.findIndex((option) => option.value === values[depth] && !option.disabled);\n return found >= 0\n ? found\n : Math.max(\n 0,\n list.findIndex((option) => !option.disabled),\n );\n });\n const options = lists.map((list, depth) => list[indexes[depth]!]!).filter(Boolean);\n return { lists, indexes, options, values: options.map((option) => option.value) };\n}\n\n/** 选择器(div > 标题栏 + 各列滚轮) */\nexport const Picker = _component(\n ({\n columns,\n _value: value = _bindable<PickerValue[]>([]),\n title,\n showToolbar = true,\n confirmText,\n cancelText,\n visibleItemCount = 5,\n onConfirm,\n onCancel,\n onChange,\n columnLabels,\n ...rest\n }: PickerProps) => {\n const config = useConfig();\n const resolved = _derived(resolveColumns(columns, value));\n const select = (columnIndex: number, index: number) => {\n const next = [...resolved.values];\n next[columnIndex] = resolved.lists[columnIndex]![index]!.value;\n // 联动:后面的列重新按第一个可选的算\n const settled = resolveColumns(\n columns,\n isMulti(columns) ? next : next.slice(0, columnIndex + 1),\n );\n value = settled.values;\n onChange?.({ values: settled.values, options: settled.options, columnIndex });\n };\n return () => {\n const visible = visibleItemCount % 2 ? visibleItemCount : visibleItemCount + 1;\n const fade = `100% ${((visible - 1) / 2) * PICKER_ITEM_HEIGHT}em`;\n return (\n <div {...mergeAttrs<'div'>({ sx: pickerStyles.root }, rest)}>\n {showToolbar && (\n <div {...mergeAttrs<'div'>({ sx: pickerStyles.toolbar })}>\n <button\n {...mergeAttrs<'button'>({ sx: pickerStyles.action })}\n type=\"button\"\n onClick={() => onCancel?.()}\n >\n {cancelText ?? config.lang.cancel}\n </button>\n {hasContent(title) && (\n <div {...mergeAttrs<'div'>({ sx: pickerStyles.title })}>{title}</div>\n )}\n <button\n {...mergeAttrs<'button'>({ sx: [pickerStyles.action, pickerStyles.confirm] })}\n type=\"button\"\n onClick={() => onConfirm?.({ values: resolved.values, options: resolved.options })}\n >\n {confirmText ?? config.lang.confirm}\n </button>\n </div>\n )}\n <div {...mergeAttrs<'div'>({ sx: pickerStyles.columns })}>\n {resolved.lists.map((list, columnIndex) => (\n <PickerColumn\n options={list}\n index={resolved.indexes[columnIndex]!}\n visible={visible}\n label={columnLabels?.[columnIndex]}\n onSelect={(index) => select(columnIndex, index)}\n />\n ))}\n <div\n {...mergeAttrs<'div'>({\n sx: pickerStyles.mask,\n style: { backgroundSize: `${fade}, ${fade}` },\n })}\n />\n <div\n {...mergeAttrs<'div'>({\n sx: pickerStyles.indicator,\n style: { insetBlockStart: `${((visible - 1) / 2) * PICKER_ITEM_HEIGHT}em` },\n })}\n />\n </div>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type VNodeChild } from 'vue';
|
|
3
|
+
/** 一个选项 */
|
|
4
|
+
export interface PickerOption {
|
|
5
|
+
/** 显示的文字 */
|
|
6
|
+
text: VNodeChild;
|
|
7
|
+
/** 值 */
|
|
8
|
+
value: string | number;
|
|
9
|
+
/** 不能选 */
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
/** 联动时的下一列 */
|
|
12
|
+
children?: PickerOption[];
|
|
13
|
+
}
|
|
14
|
+
/** 一列的属性 */
|
|
15
|
+
export interface PickerColumnProps {
|
|
16
|
+
/** 选项 */
|
|
17
|
+
options: PickerOption[];
|
|
18
|
+
/** 选中的序号 */
|
|
19
|
+
index: number;
|
|
20
|
+
/** 可见的行数(奇数) */
|
|
21
|
+
visible: number;
|
|
22
|
+
/** 读屏的名称 */
|
|
23
|
+
label?: string;
|
|
24
|
+
/** 选中的项变了(拖完、点了、按了方向键) */
|
|
25
|
+
onSelect: (index: number) => void;
|
|
26
|
+
}
|
|
27
|
+
/** 一行的高度(em) */
|
|
28
|
+
export declare const PICKER_ITEM_HEIGHT = 2.75;
|
|
29
|
+
/** PickerColumn 的样式 */
|
|
30
|
+
export declare const pickerColumnStyles: Readonly<{
|
|
31
|
+
column: css.CompiledStyles;
|
|
32
|
+
item: css.CompiledStyles;
|
|
33
|
+
list: css.CompiledStyles;
|
|
34
|
+
}>;
|
|
35
|
+
/** 滚轮的一列(div[role=listbox]) */
|
|
36
|
+
export declare const PickerColumn: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<PickerColumnProps>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { dataAttrs, mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { useHost } from "../utils/host.js";
|
|
3
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
4
|
+
import { _component, mergeJsxProps, readonlyProp } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
+
import { createVNode, onMounted, ref } from "vue";
|
|
6
|
+
//#region src/picker/PickerColumn.tsx
|
|
7
|
+
/** 一个选项 */
|
|
8
|
+
/** 一列的属性 */
|
|
9
|
+
/** 一行的高度(em) */
|
|
10
|
+
var PICKER_ITEM_HEIGHT = 2.75;
|
|
11
|
+
/** PickerColumn 的样式 */
|
|
12
|
+
var pickerColumnStyles = css.create({
|
|
13
|
+
column: {
|
|
14
|
+
position: "relative",
|
|
15
|
+
flex: "1 1 0",
|
|
16
|
+
minWidth: 0,
|
|
17
|
+
overflow: "hidden",
|
|
18
|
+
touchAction: "none",
|
|
19
|
+
userSelect: "none",
|
|
20
|
+
outlineStyle: "none"
|
|
21
|
+
},
|
|
22
|
+
list: {
|
|
23
|
+
transitionProperty: "transform",
|
|
24
|
+
transitionTimingFunction: "cubic-bezier(0.23, 1, 0.68, 1)"
|
|
25
|
+
},
|
|
26
|
+
item: {
|
|
27
|
+
display: "flex",
|
|
28
|
+
alignItems: "center",
|
|
29
|
+
justifyContent: "center",
|
|
30
|
+
height: `${PICKER_ITEM_HEIGHT}em`,
|
|
31
|
+
paddingInline: "_xs",
|
|
32
|
+
overflow: "hidden",
|
|
33
|
+
whiteSpace: "nowrap",
|
|
34
|
+
textOverflow: "ellipsis",
|
|
35
|
+
fontSize: "_md",
|
|
36
|
+
color: {
|
|
37
|
+
default: "_text",
|
|
38
|
+
"[data-disabled]": "_textDisabled"
|
|
39
|
+
},
|
|
40
|
+
cursor: {
|
|
41
|
+
default: "pointer",
|
|
42
|
+
"[data-disabled]": "not-allowed"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
/** 离边缘还能多拖的阻尼:越过两端时只跟手三分之一 */
|
|
47
|
+
var RESISTANCE = 1 / 3;
|
|
48
|
+
/** 找到离 index 最近的可选项 */
|
|
49
|
+
function nearestEnabled(options, index) {
|
|
50
|
+
const clamped = Math.min(options.length - 1, Math.max(0, index));
|
|
51
|
+
for (let distance = 0; distance < options.length; distance++) for (const candidate of [clamped + distance, clamped - distance]) if (options[candidate] && !options[candidate].disabled) return candidate;
|
|
52
|
+
return clamped;
|
|
53
|
+
}
|
|
54
|
+
/** 滚轮的一列(div[role=listbox]) */
|
|
55
|
+
var PickerColumn = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
56
|
+
const _cache = [];
|
|
57
|
+
const options = readonlyProp(_props, "options");
|
|
58
|
+
const index = readonlyProp(_props, "index");
|
|
59
|
+
const visible = readonlyProp(_props, "visible");
|
|
60
|
+
const label = readonlyProp(_props, "label");
|
|
61
|
+
const onSelect = readonlyProp(_props, "onSelect");
|
|
62
|
+
const host = useHost();
|
|
63
|
+
let column = null;
|
|
64
|
+
let itemPx = 44;
|
|
65
|
+
const measure = async () => {
|
|
66
|
+
const rect = await host.measure(column);
|
|
67
|
+
if (rect?.height) itemPx = rect.height / visible.value;
|
|
68
|
+
};
|
|
69
|
+
onMounted(() => void measure());
|
|
70
|
+
let drag = ref(null);
|
|
71
|
+
let duration = ref(0);
|
|
72
|
+
let start = {
|
|
73
|
+
y: 0,
|
|
74
|
+
time: 0,
|
|
75
|
+
index: 0
|
|
76
|
+
};
|
|
77
|
+
let moves = [];
|
|
78
|
+
const select = (next, ms) => {
|
|
79
|
+
duration.value = ms;
|
|
80
|
+
const target = nearestEnabled(options.value, next);
|
|
81
|
+
if (target !== index.value) onSelect.value(target);
|
|
82
|
+
};
|
|
83
|
+
const onTouchstart = (event) => {
|
|
84
|
+
measure();
|
|
85
|
+
const y = event.touches[0].clientY;
|
|
86
|
+
start = {
|
|
87
|
+
y,
|
|
88
|
+
time: Date.now(),
|
|
89
|
+
index: index.value
|
|
90
|
+
};
|
|
91
|
+
moves = [{
|
|
92
|
+
y,
|
|
93
|
+
time: start.time
|
|
94
|
+
}];
|
|
95
|
+
drag.value = 0;
|
|
96
|
+
duration.value = 0;
|
|
97
|
+
};
|
|
98
|
+
const onTouchmove = (event) => {
|
|
99
|
+
if (drag.value === null) return;
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
const y = event.touches[0].clientY;
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
moves = [...moves.filter((move) => now - move.time < 100), {
|
|
104
|
+
y,
|
|
105
|
+
time: now
|
|
106
|
+
}];
|
|
107
|
+
let offset = y - start.y;
|
|
108
|
+
const top = start.index * itemPx;
|
|
109
|
+
const bottom = (options.value.length - 1 - start.index) * itemPx;
|
|
110
|
+
if (offset > top) offset = top + (offset - top) * RESISTANCE;
|
|
111
|
+
if (offset < -bottom) offset = -bottom + (offset + bottom) * RESISTANCE;
|
|
112
|
+
drag.value = offset;
|
|
113
|
+
};
|
|
114
|
+
const onTouchend = () => {
|
|
115
|
+
if (drag.value === null) return;
|
|
116
|
+
const offset = drag.value;
|
|
117
|
+
drag.value = null;
|
|
118
|
+
const first = moves[0];
|
|
119
|
+
const last = moves[moves.length - 1];
|
|
120
|
+
const velocity = last.time > first.time ? (last.y - first.y) / (last.time - first.time) : 0;
|
|
121
|
+
const momentum = Math.abs(velocity) > .3 ? velocity * 250 : 0;
|
|
122
|
+
const steps = Math.round((offset + momentum) / itemPx);
|
|
123
|
+
select(start.index - steps, Math.min(1e3, 200 + Math.abs(momentum) * 1.5));
|
|
124
|
+
};
|
|
125
|
+
return () => {
|
|
126
|
+
const middle = (visible.value - 1) / 2 * PICKER_ITEM_HEIGHT;
|
|
127
|
+
const transform = drag.value === null ? `translateY(${middle - index.value * PICKER_ITEM_HEIGHT}em)` : `translateY(calc(${middle - start.index * PICKER_ITEM_HEIGHT}em + ${drag.value}px))`;
|
|
128
|
+
return createVNode("div", mergeJsxProps(mergeAttrs({
|
|
129
|
+
sx: pickerColumnStyles.column,
|
|
130
|
+
style: { height: `${visible.value * PICKER_ITEM_HEIGHT}em` }
|
|
131
|
+
}), {
|
|
132
|
+
"role": "listbox",
|
|
133
|
+
"aria-label": label.value,
|
|
134
|
+
"tabindex": 0,
|
|
135
|
+
"onTouchstart": onTouchstart,
|
|
136
|
+
"onTouchmove": onTouchmove,
|
|
137
|
+
"onTouchend": onTouchend,
|
|
138
|
+
"onTouchcancel": onTouchend,
|
|
139
|
+
"onKeydown": _cache[0] || (_cache[0] = (event) => {
|
|
140
|
+
const delta = {
|
|
141
|
+
ArrowDown: 1,
|
|
142
|
+
ArrowUp: -1
|
|
143
|
+
}[event.key];
|
|
144
|
+
if (delta === void 0) return;
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
let next = index.value + delta;
|
|
147
|
+
while (options.value[next]?.disabled) next += delta;
|
|
148
|
+
if (options.value[next]) select(next, 200);
|
|
149
|
+
})
|
|
150
|
+
}, dataAttrs({ "data-touch-move": "catch" }), { "ref": _cache[1] || (_cache[1] = (node) => column = node) }), [createVNode("div", mergeAttrs({
|
|
151
|
+
sx: pickerColumnStyles.list,
|
|
152
|
+
style: {
|
|
153
|
+
transform,
|
|
154
|
+
transitionDuration: `${drag.value === null ? duration.value : 0}ms`
|
|
155
|
+
}
|
|
156
|
+
}), [options.value.map((option, position) => createVNode("div", mergeJsxProps(mergeAttrs({ sx: pickerColumnStyles.item }), {
|
|
157
|
+
"role": "option",
|
|
158
|
+
"aria-selected": position === index.value ? "true" : "false",
|
|
159
|
+
"aria-disabled": option.disabled ? "true" : void 0
|
|
160
|
+
}, dataAttrs({ "data-disabled": option.disabled }), { "onClick": () => !option.disabled && select(position, 200) }), [option.text]))])]);
|
|
161
|
+
};
|
|
162
|
+
}, {
|
|
163
|
+
name: "PickerColumn",
|
|
164
|
+
props: [
|
|
165
|
+
"options",
|
|
166
|
+
"index",
|
|
167
|
+
"visible",
|
|
168
|
+
"label",
|
|
169
|
+
"onSelect"
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
//#endregion
|
|
173
|
+
export { PICKER_ITEM_HEIGHT, PickerColumn, pickerColumnStyles };
|
|
174
|
+
|
|
175
|
+
//# sourceMappingURL=PickerColumn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PickerColumn.js","names":["_vtp_readonlyProp","_vue_ref","_createVNode","_mergeProps"],"sources":["../../src/picker/PickerColumn.tsx"],"sourcesContent":["/**\n * 滚轮的一列(Picker、DatePicker、TimePicker 共用,内部组件):上下拖动带惯性,松手对齐到最近的一项,跳过禁用的项;\n * 点一项直接滚过去;H5 里聚焦后可以用方向键。\n *\n * 项高按 em 写(随字号),拖动要换算成像素:挂上时量一次列的高度,每次按下时再量。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { onMounted, type VNodeChild } from 'vue';\nimport { useHost } from '../utils/host';\nimport { dataAttrs, mergeAttrs } from '../utils/props';\n\n/** 一个选项 */\nexport interface PickerOption {\n /** 显示的文字 */\n text: VNodeChild;\n /** 值 */\n value: string | number;\n /** 不能选 */\n disabled?: boolean;\n /** 联动时的下一列 */\n children?: PickerOption[];\n}\n\n/** 一列的属性 */\nexport interface PickerColumnProps {\n /** 选项 */\n options: PickerOption[];\n /** 选中的序号 */\n index: number;\n /** 可见的行数(奇数) */\n visible: number;\n /** 读屏的名称 */\n label?: string;\n /** 选中的项变了(拖完、点了、按了方向键) */\n onSelect: (index: number) => void;\n}\n\n/** 一行的高度(em) */\nexport const PICKER_ITEM_HEIGHT = 2.75;\n\n/** PickerColumn 的样式 */\nexport const pickerColumnStyles = css.create({\n column: {\n position: 'relative',\n flex: '1 1 0',\n minWidth: 0,\n overflow: 'hidden',\n touchAction: 'none',\n userSelect: 'none',\n outlineStyle: 'none',\n },\n list: {\n transitionProperty: 'transform',\n transitionTimingFunction: 'cubic-bezier(0.23, 1, 0.68, 1)',\n },\n item: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: `${PICKER_ITEM_HEIGHT}em`,\n paddingInline: '_xs',\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n textOverflow: 'ellipsis',\n fontSize: '_md',\n color: { default: '_text', '[data-disabled]': '_textDisabled' },\n cursor: { default: 'pointer', '[data-disabled]': 'not-allowed' },\n },\n});\n\n/** 离边缘还能多拖的阻尼:越过两端时只跟手三分之一 */\nconst RESISTANCE = 1 / 3;\n\n/** 找到离 index 最近的可选项 */\nfunction nearestEnabled(options: PickerOption[], index: number): number {\n const clamped = Math.min(options.length - 1, Math.max(0, index));\n for (let distance = 0; distance < options.length; distance++) {\n for (const candidate of [clamped + distance, clamped - distance])\n if (options[candidate] && !options[candidate].disabled) return candidate;\n }\n return clamped;\n}\n\n/** 滚轮的一列(div[role=listbox]) */\nexport const PickerColumn = _component(\n ({ options, index, visible, label, onSelect }: PickerColumnProps) => {\n const host = useHost();\n let column: unknown = null;\n // 一行的像素高度:量列的高度除以可见的行数\n let itemPx = 44;\n const measure = async () => {\n const rect = await host.measure(column);\n if (rect?.height) itemPx = rect.height / visible;\n };\n onMounted(() => void measure());\n\n // 拖动中的偏移(px,相对选中项居中的位置);null 是没在拖\n let drag = _state<number | null>(null);\n let duration = _state(0);\n let start = { y: 0, time: 0, index: 0 };\n let moves: { y: number; time: number }[] = [];\n\n const select = (next: number, ms: number) => {\n duration = ms;\n const target = nearestEnabled(options, next);\n if (target !== index) onSelect(target);\n };\n const onTouchstart = (event: TouchEvent) => {\n void measure();\n const y = event.touches[0]!.clientY;\n start = { y, time: Date.now(), index };\n moves = [{ y, time: start.time }];\n drag = 0;\n duration = 0;\n };\n const onTouchmove = (event: TouchEvent) => {\n if (drag === null) return;\n event.preventDefault();\n const y = event.touches[0]!.clientY;\n const now = Date.now();\n moves = [...moves.filter((move) => now - move.time < 100), { y, time: now }];\n let offset = y - start.y;\n // 越过第一项、最后一项时加阻尼\n const top = start.index * itemPx;\n const bottom = (options.length - 1 - start.index) * itemPx;\n if (offset > top) offset = top + (offset - top) * RESISTANCE;\n if (offset < -bottom) offset = -bottom + (offset + bottom) * RESISTANCE;\n drag = offset;\n };\n const onTouchend = () => {\n if (drag === null) return;\n const offset = drag;\n drag = null;\n // 惯性:最后 100ms 的速度(px/ms)乘一个时长,快甩滚得远\n const first = moves[0]!;\n const last = moves[moves.length - 1]!;\n const velocity = last.time > first.time ? (last.y - first.y) / (last.time - first.time) : 0;\n const momentum = Math.abs(velocity) > 0.3 ? velocity * 250 : 0;\n const steps = Math.round((offset + momentum) / itemPx);\n select(start.index - steps, Math.min(1000, 200 + Math.abs(momentum) * 1.5));\n };\n\n return () => {\n // 列表的偏移:选中的项在中间那一行\n const middle = ((visible - 1) / 2) * PICKER_ITEM_HEIGHT;\n const transform =\n drag === null\n ? `translateY(${middle - index * PICKER_ITEM_HEIGHT}em)`\n : `translateY(calc(${middle - start.index * PICKER_ITEM_HEIGHT}em + ${drag}px))`;\n return (\n <div\n {...mergeAttrs<'div'>({\n sx: pickerColumnStyles.column,\n style: { height: `${visible * PICKER_ITEM_HEIGHT}em` },\n })}\n role=\"listbox\"\n aria-label={label}\n tabindex={0}\n onTouchstart={onTouchstart}\n onTouchmove={onTouchmove}\n onTouchend={onTouchend}\n onTouchcancel={onTouchend}\n onKeydown={(event: KeyboardEvent) => {\n const delta = { ArrowDown: 1, ArrowUp: -1 }[event.key];\n if (delta === undefined) return;\n event.preventDefault();\n // 方向键越过禁用的项往同一个方向找\n let next = index + delta;\n while (options[next]?.disabled) next += delta;\n if (options[next]) select(next, 200);\n }}\n // 小程序:收 touchmove,拖动期间页面不跟着滚\n {...dataAttrs({ 'data-touch-move': 'catch' })}\n ref={(node: unknown) => (column = node)}\n >\n <div\n {...mergeAttrs<'div'>({\n sx: pickerColumnStyles.list,\n style: { transform, transitionDuration: `${drag === null ? duration : 0}ms` },\n })}\n >\n {options.map((option, position) => (\n <div\n {...mergeAttrs<'div'>({ sx: pickerColumnStyles.item })}\n role=\"option\"\n aria-selected={position === index ? 'true' : 'false'}\n aria-disabled={option.disabled ? 'true' : undefined}\n {...dataAttrs({ 'data-disabled': option.disabled })}\n onClick={() => !option.disabled && select(position, 200)}\n >\n {option.text}\n </div>\n ))}\n </div>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 暗色主题与"跟随系统":只改颜色与阴影两组变量的类,挂在 ThemeProvider 的根元素上(或任意祖先上),换主题不用重新渲染组件。
|
|
3
|
+
* 取值同 Radix Colors 的暗色色阶(与 tokens.ts 注释里写的暗色值一致)。
|
|
4
|
+
*/
|
|
5
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
6
|
+
/** 暗色主题:颜色与阴影两个类,一起挂上 */
|
|
7
|
+
export declare const darkTheme: (css.Theme<css.VarGroup<{
|
|
8
|
+
readonly background: "#f0f0f3";
|
|
9
|
+
readonly surface: "#ffffff";
|
|
10
|
+
readonly surfaceMuted: "#f9f9fb";
|
|
11
|
+
readonly fill: "#f0f0f3";
|
|
12
|
+
readonly fillActive: "#e8e8ec";
|
|
13
|
+
readonly text: "#1c2024";
|
|
14
|
+
readonly textMuted: "#60646c";
|
|
15
|
+
readonly textPlaceholder: "#80838d";
|
|
16
|
+
readonly textDisabled: "#b9bbc6";
|
|
17
|
+
readonly border: "#d9d9e0";
|
|
18
|
+
readonly borderStrong: "#cdced6";
|
|
19
|
+
readonly accent: "#3e63dd";
|
|
20
|
+
readonly accentActive: "#3358d4";
|
|
21
|
+
readonly accentContrast: "#ffffff";
|
|
22
|
+
readonly accentText: "#3a5bc7";
|
|
23
|
+
readonly accentSoft: "#edf2fe";
|
|
24
|
+
readonly accentSoftActive: "#e1e9ff";
|
|
25
|
+
readonly accentBorder: "#abbdf9";
|
|
26
|
+
readonly danger: "#ce2c31";
|
|
27
|
+
readonly dangerActive: "#b8272c";
|
|
28
|
+
readonly dangerContrast: "#ffffff";
|
|
29
|
+
readonly dangerText: "#b8272c";
|
|
30
|
+
readonly dangerSoft: "#feebec";
|
|
31
|
+
readonly dangerSoftActive: "#ffdbdc";
|
|
32
|
+
readonly dangerBorder: "#f4a9aa";
|
|
33
|
+
readonly success: "#008751";
|
|
34
|
+
readonly successActive: "#007143";
|
|
35
|
+
readonly successContrast: "#ffffff";
|
|
36
|
+
readonly successText: "#193b2d";
|
|
37
|
+
readonly successSoft: "#e6f6eb";
|
|
38
|
+
readonly successSoftActive: "#d6f1df";
|
|
39
|
+
readonly successBorder: "#8eceaa";
|
|
40
|
+
readonly warning: "#ffc53d";
|
|
41
|
+
readonly warningActive: "#ffcd5f";
|
|
42
|
+
readonly warningContrast: "#4f3422";
|
|
43
|
+
readonly warningText: "#4f3422";
|
|
44
|
+
readonly warningSoft: "#fff7c2";
|
|
45
|
+
readonly warningSoftActive: "#ffee9c";
|
|
46
|
+
readonly warningBorder: "#e9c162";
|
|
47
|
+
readonly info: "#0073e0";
|
|
48
|
+
readonly infoActive: "#0060bd";
|
|
49
|
+
readonly infoContrast: "#ffffff";
|
|
50
|
+
readonly infoText: "#113264";
|
|
51
|
+
readonly infoSoft: "#e6f4fe";
|
|
52
|
+
readonly infoSoftActive: "#d5efff";
|
|
53
|
+
readonly infoBorder: "#8ec8f6";
|
|
54
|
+
readonly inverseSurface: "#1c2024";
|
|
55
|
+
readonly inverseText: "#fcfcfd";
|
|
56
|
+
readonly overlay: "rgba(0, 0, 0, 0.5)";
|
|
57
|
+
readonly focusRing: "#3e63dd";
|
|
58
|
+
}>> | css.Theme<css.VarGroup<{
|
|
59
|
+
readonly none: "none";
|
|
60
|
+
readonly sm: "0 1px 2px rgba(0, 0, 0, 0.06), 0 2px 8px -2px rgba(0, 0, 0, 0.08)";
|
|
61
|
+
readonly md: "0 4px 16px -4px rgba(0, 0, 0, 0.12), 0 2px 6px -2px rgba(0, 0, 0, 0.08)";
|
|
62
|
+
readonly lg: "0 12px 40px -8px rgba(0, 0, 0, 0.2), 0 4px 12px -4px rgba(0, 0, 0, 0.1)";
|
|
63
|
+
}>>)[];
|
|
64
|
+
/** 跟随系统:系统是暗色时用暗色值 */
|
|
65
|
+
export declare const systemTheme: (css.Theme<css.VarGroup<{
|
|
66
|
+
readonly background: "#f0f0f3";
|
|
67
|
+
readonly surface: "#ffffff";
|
|
68
|
+
readonly surfaceMuted: "#f9f9fb";
|
|
69
|
+
readonly fill: "#f0f0f3";
|
|
70
|
+
readonly fillActive: "#e8e8ec";
|
|
71
|
+
readonly text: "#1c2024";
|
|
72
|
+
readonly textMuted: "#60646c";
|
|
73
|
+
readonly textPlaceholder: "#80838d";
|
|
74
|
+
readonly textDisabled: "#b9bbc6";
|
|
75
|
+
readonly border: "#d9d9e0";
|
|
76
|
+
readonly borderStrong: "#cdced6";
|
|
77
|
+
readonly accent: "#3e63dd";
|
|
78
|
+
readonly accentActive: "#3358d4";
|
|
79
|
+
readonly accentContrast: "#ffffff";
|
|
80
|
+
readonly accentText: "#3a5bc7";
|
|
81
|
+
readonly accentSoft: "#edf2fe";
|
|
82
|
+
readonly accentSoftActive: "#e1e9ff";
|
|
83
|
+
readonly accentBorder: "#abbdf9";
|
|
84
|
+
readonly danger: "#ce2c31";
|
|
85
|
+
readonly dangerActive: "#b8272c";
|
|
86
|
+
readonly dangerContrast: "#ffffff";
|
|
87
|
+
readonly dangerText: "#b8272c";
|
|
88
|
+
readonly dangerSoft: "#feebec";
|
|
89
|
+
readonly dangerSoftActive: "#ffdbdc";
|
|
90
|
+
readonly dangerBorder: "#f4a9aa";
|
|
91
|
+
readonly success: "#008751";
|
|
92
|
+
readonly successActive: "#007143";
|
|
93
|
+
readonly successContrast: "#ffffff";
|
|
94
|
+
readonly successText: "#193b2d";
|
|
95
|
+
readonly successSoft: "#e6f6eb";
|
|
96
|
+
readonly successSoftActive: "#d6f1df";
|
|
97
|
+
readonly successBorder: "#8eceaa";
|
|
98
|
+
readonly warning: "#ffc53d";
|
|
99
|
+
readonly warningActive: "#ffcd5f";
|
|
100
|
+
readonly warningContrast: "#4f3422";
|
|
101
|
+
readonly warningText: "#4f3422";
|
|
102
|
+
readonly warningSoft: "#fff7c2";
|
|
103
|
+
readonly warningSoftActive: "#ffee9c";
|
|
104
|
+
readonly warningBorder: "#e9c162";
|
|
105
|
+
readonly info: "#0073e0";
|
|
106
|
+
readonly infoActive: "#0060bd";
|
|
107
|
+
readonly infoContrast: "#ffffff";
|
|
108
|
+
readonly infoText: "#113264";
|
|
109
|
+
readonly infoSoft: "#e6f4fe";
|
|
110
|
+
readonly infoSoftActive: "#d5efff";
|
|
111
|
+
readonly infoBorder: "#8ec8f6";
|
|
112
|
+
readonly inverseSurface: "#1c2024";
|
|
113
|
+
readonly inverseText: "#fcfcfd";
|
|
114
|
+
readonly overlay: "rgba(0, 0, 0, 0.5)";
|
|
115
|
+
readonly focusRing: "#3e63dd";
|
|
116
|
+
}>> | css.Theme<css.VarGroup<{
|
|
117
|
+
readonly none: "none";
|
|
118
|
+
readonly sm: "0 1px 2px rgba(0, 0, 0, 0.06), 0 2px 8px -2px rgba(0, 0, 0, 0.08)";
|
|
119
|
+
readonly md: "0 4px 16px -4px rgba(0, 0, 0, 0.12), 0 2px 6px -2px rgba(0, 0, 0, 0.08)";
|
|
120
|
+
readonly lg: "0 12px 40px -8px rgba(0, 0, 0, 0.2), 0 4px 12px -4px rgba(0, 0, 0, 0.1)";
|
|
121
|
+
}>>)[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { colors, shadows } from "./tokens.js";
|
|
2
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
3
|
+
//#region src/theme/dark.ts
|
|
4
|
+
/**
|
|
5
|
+
* 暗色主题与"跟随系统":只改颜色与阴影两组变量的类,挂在 ThemeProvider 的根元素上(或任意祖先上),换主题不用重新渲染组件。
|
|
6
|
+
* 取值同 Radix Colors 的暗色色阶(与 tokens.ts 注释里写的暗色值一致)。
|
|
7
|
+
*/
|
|
8
|
+
/** 暗色主题的颜色 */
|
|
9
|
+
var darkColors = {
|
|
10
|
+
background: "#111113",
|
|
11
|
+
surface: "#18191b",
|
|
12
|
+
surfaceMuted: "#212225",
|
|
13
|
+
fill: "#272a2d",
|
|
14
|
+
fillActive: "#2e3135",
|
|
15
|
+
text: "#edeef0",
|
|
16
|
+
textMuted: "#b0b4ba",
|
|
17
|
+
textPlaceholder: "#777b84",
|
|
18
|
+
textDisabled: "#5a6169",
|
|
19
|
+
border: "#363a3f",
|
|
20
|
+
borderStrong: "#43484e",
|
|
21
|
+
accent: "#3e63dd",
|
|
22
|
+
accentActive: "#3758d4",
|
|
23
|
+
accentContrast: "#ffffff",
|
|
24
|
+
accentText: "#9eb1ff",
|
|
25
|
+
accentSoft: "#182449",
|
|
26
|
+
accentSoftActive: "#1d2e62",
|
|
27
|
+
accentBorder: "#3a4f97",
|
|
28
|
+
danger: "#b54548",
|
|
29
|
+
dangerActive: "#a33b3e",
|
|
30
|
+
dangerContrast: "#ffffff",
|
|
31
|
+
dangerText: "#ff9592",
|
|
32
|
+
dangerSoft: "#3b1219",
|
|
33
|
+
dangerSoftActive: "#500f1c",
|
|
34
|
+
dangerBorder: "#8c333a",
|
|
35
|
+
success: "#30a46c",
|
|
36
|
+
successActive: "#52af7d",
|
|
37
|
+
successContrast: "#111113",
|
|
38
|
+
successText: "#3dd68c",
|
|
39
|
+
successSoft: "#132d21",
|
|
40
|
+
successSoftActive: "#113b29",
|
|
41
|
+
successBorder: "#28684a",
|
|
42
|
+
warning: "#ffc53d",
|
|
43
|
+
warningActive: "#ffcd5f",
|
|
44
|
+
warningContrast: "#111113",
|
|
45
|
+
warningText: "#ffca16",
|
|
46
|
+
warningSoft: "#302008",
|
|
47
|
+
warningSoftActive: "#3f2700",
|
|
48
|
+
warningBorder: "#714f19",
|
|
49
|
+
info: "#0090ff",
|
|
50
|
+
infoActive: "#3d9fff",
|
|
51
|
+
infoContrast: "#111113",
|
|
52
|
+
infoText: "#70b8ff",
|
|
53
|
+
infoSoft: "#0d2847",
|
|
54
|
+
infoSoftActive: "#003362",
|
|
55
|
+
infoBorder: "#205d9e",
|
|
56
|
+
inverseSurface: "#edeef0",
|
|
57
|
+
inverseText: "#111113",
|
|
58
|
+
overlay: "rgba(0, 0, 0, 0.7)",
|
|
59
|
+
focusRing: "#3e63dd"
|
|
60
|
+
};
|
|
61
|
+
/** 暗色主题里阴影看不出来,靠表面的层级区分 */
|
|
62
|
+
var darkShadows = {
|
|
63
|
+
sm: "none",
|
|
64
|
+
md: "none",
|
|
65
|
+
lg: "0 12px 40px -8px rgba(0, 0, 0, 0.6)"
|
|
66
|
+
};
|
|
67
|
+
var SYSTEM_DARK = "@media (prefers-color-scheme: dark)";
|
|
68
|
+
/** 按系统偏好取值:亮色时不改,系统是暗色时换成暗色值 */
|
|
69
|
+
var followSystem = (values) => Object.fromEntries(Object.entries(values).map(([key, value]) => [key, { [SYSTEM_DARK]: value }]));
|
|
70
|
+
/** 暗色主题:颜色与阴影两个类,一起挂上 */
|
|
71
|
+
var darkTheme = [css.createTheme(colors, darkColors), css.createTheme(shadows, darkShadows)];
|
|
72
|
+
/** 跟随系统:系统是暗色时用暗色值 */
|
|
73
|
+
var systemTheme = [css.createTheme(colors, followSystem(darkColors)), css.createTheme(shadows, followSystem(darkShadows))];
|
|
74
|
+
//#endregion
|
|
75
|
+
export { darkTheme, systemTheme };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=dark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dark.js","names":[],"sources":["../../src/theme/dark.ts"],"sourcesContent":["/**\n * 暗色主题与\"跟随系统\":只改颜色与阴影两组变量的类,挂在 ThemeProvider 的根元素上(或任意祖先上),换主题不用重新渲染组件。\n * 取值同 Radix Colors 的暗色色阶(与 tokens.ts 注释里写的暗色值一致)。\n */\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { colors, shadows } from './tokens';\n\n/** 暗色主题的颜色 */\nconst darkColors = {\n background: '#111113',\n surface: '#18191b',\n surfaceMuted: '#212225',\n fill: '#272a2d',\n fillActive: '#2e3135',\n text: '#edeef0',\n textMuted: '#b0b4ba',\n textPlaceholder: '#777b84',\n textDisabled: '#5a6169',\n border: '#363a3f',\n borderStrong: '#43484e',\n accent: '#3e63dd',\n accentActive: '#3758d4',\n accentContrast: '#ffffff',\n accentText: '#9eb1ff',\n accentSoft: '#182449',\n accentSoftActive: '#1d2e62',\n accentBorder: '#3a4f97',\n danger: '#b54548',\n dangerActive: '#a33b3e',\n dangerContrast: '#ffffff',\n dangerText: '#ff9592',\n dangerSoft: '#3b1219',\n dangerSoftActive: '#500f1c',\n dangerBorder: '#8c333a',\n success: '#30a46c',\n successActive: '#52af7d',\n successContrast: '#111113',\n successText: '#3dd68c',\n successSoft: '#132d21',\n successSoftActive: '#113b29',\n successBorder: '#28684a',\n warning: '#ffc53d',\n warningActive: '#ffcd5f',\n warningContrast: '#111113',\n warningText: '#ffca16',\n warningSoft: '#302008',\n warningSoftActive: '#3f2700',\n warningBorder: '#714f19',\n info: '#0090ff',\n infoActive: '#3d9fff',\n infoContrast: '#111113',\n infoText: '#70b8ff',\n infoSoft: '#0d2847',\n infoSoftActive: '#003362',\n infoBorder: '#205d9e',\n inverseSurface: '#edeef0',\n inverseText: '#111113',\n overlay: 'rgba(0, 0, 0, 0.7)',\n focusRing: '#3e63dd',\n} satisfies Record<Extract<keyof typeof colors, string>, string>;\n\n/** 暗色主题里阴影看不出来,靠表面的层级区分 */\nconst darkShadows = { sm: 'none', md: 'none', lg: '0 12px 40px -8px rgba(0, 0, 0, 0.6)' };\n\nconst SYSTEM_DARK = '@media (prefers-color-scheme: dark)';\n\n/** 按系统偏好取值:亮色时不改,系统是暗色时换成暗色值 */\nconst followSystem = (values: Record<string, string>) =>\n Object.fromEntries(Object.entries(values).map(([key, value]) => [key, { [SYSTEM_DARK]: value }]));\n\n/** 暗色主题:颜色与阴影两个类,一起挂上 */\nexport const darkTheme = [\n css.createTheme(colors, darkColors),\n css.createTheme(shadows, darkShadows),\n];\n\n/** 跟随系统:系统是暗色时用暗色值 */\nexport const systemTheme = [\n css.createTheme(colors, followSystem(darkColors)),\n css.createTheme(shadows, followSystem(darkShadows)),\n];\n"],"mappings":";;;;;;;;AAQA,IAAM,aAAa;CACjB,YAAY;CACZ,SAAS;CACT,cAAc;CACd,MAAM;CACN,YAAY;CACZ,MAAM;CACN,WAAW;CACX,iBAAiB;CACjB,cAAc;CACd,QAAQ;CACR,cAAc;CACd,QAAQ;CACR,cAAc;CACd,gBAAgB;CAChB,YAAY;CACZ,YAAY;CACZ,kBAAkB;CAClB,cAAc;CACd,QAAQ;CACR,cAAc;CACd,gBAAgB;CAChB,YAAY;CACZ,YAAY;CACZ,kBAAkB;CAClB,cAAc;CACd,SAAS;CACT,eAAe;CACf,iBAAiB;CACjB,aAAa;CACb,aAAa;CACb,mBAAmB;CACnB,eAAe;CACf,SAAS;CACT,eAAe;CACf,iBAAiB;CACjB,aAAa;CACb,aAAa;CACb,mBAAmB;CACnB,eAAe;CACf,MAAM;CACN,YAAY;CACZ,cAAc;CACd,UAAU;CACV,UAAU;CACV,gBAAgB;CAChB,YAAY;CACZ,gBAAgB;CAChB,aAAa;CACb,SAAS;CACT,WAAW;AACb;;AAGA,IAAM,cAAc;CAAE,IAAI;CAAQ,IAAI;CAAQ,IAAI;AAAsC;AAExF,IAAM,cAAc;;AAGpB,IAAM,gBAAgB,WACpB,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,GAAG,cAAc,MAAM,CAAC,CAAC,CAAC;;AAGlG,IAAa,YAAY,CACvB,IAAI,YAAY,QAAQ,UAAU,GAClC,IAAI,YAAY,SAAS,WAAW,CACtC;;AAGA,IAAa,cAAc,CACzB,IAAI,YAAY,QAAQ,aAAa,UAAU,CAAC,GAChD,IAAI,YAAY,SAAS,aAAa,WAAW,CAAC,CACpD"}
|