@shwfed/config 2.2.4 → 2.3.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.
Files changed (62) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +8 -8
  3. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +8 -8
  4. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +8 -8
  5. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +8 -8
  6. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +9 -15
  7. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.d.vue.ts +3 -4
  8. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue +1 -1
  9. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue.d.ts +3 -4
  10. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue +9 -2
  11. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.d.vue.ts +2 -2
  12. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue +32 -52
  13. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue.d.ts +2 -2
  14. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.vue +19 -8
  15. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.d.ts +2 -2
  16. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.js +4 -3
  17. package/dist/runtime/components/form/utils/state.d.ts +10 -2
  18. package/dist/runtime/components/form/utils/state.js +10 -3
  19. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch/config.vue +25 -0
  20. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch/runtime.vue +2 -8
  21. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch/schema.d.ts +4 -0
  22. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch/schema.js +5 -0
  23. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.d.vue.ts +133 -0
  24. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.vue +533 -0
  25. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.vue.d.ts +133 -0
  26. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/runtime.d.vue.ts +9 -0
  27. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/runtime.vue +237 -0
  28. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/runtime.vue.d.ts +9 -0
  29. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/schema.d.ts +124 -0
  30. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/schema.js +96 -0
  31. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/config.d.vue.ts +10 -0
  32. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/config.vue +475 -0
  33. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/config.vue.d.ts +10 -0
  34. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/runtime.d.vue.ts +9 -0
  35. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/runtime.vue +156 -0
  36. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/runtime.vue.d.ts +9 -0
  37. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/schema.d.ts +56 -0
  38. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/schema.js +81 -0
  39. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/config.d.vue.ts +10 -0
  40. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/config.vue +292 -0
  41. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/config.vue.d.ts +10 -0
  42. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/runtime.d.vue.ts +9 -0
  43. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/runtime.vue +140 -0
  44. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/runtime.vue.d.ts +9 -0
  45. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/schema.d.ts +50 -0
  46. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/schema.js +53 -0
  47. package/dist/runtime/components/table/config.d.vue.ts +2 -0
  48. package/dist/runtime/components/table/config.vue +136 -109
  49. package/dist/runtime/components/table/config.vue.d.ts +2 -0
  50. package/dist/runtime/components/table/index.d.vue.ts +2 -2
  51. package/dist/runtime/components/table/index.vue +46 -53
  52. package/dist/runtime/components/table/index.vue.d.ts +2 -2
  53. package/dist/runtime/components/table/row-provider.d.vue.ts +23 -0
  54. package/dist/runtime/components/table/row-provider.vue +55 -0
  55. package/dist/runtime/components/table/row-provider.vue.d.ts +23 -0
  56. package/dist/runtime/components/table/schema.d.ts +17 -29
  57. package/dist/runtime/components/table/schema.js +22 -18
  58. package/dist/runtime/components/table/utils/shared.d.ts +28 -0
  59. package/dist/runtime/components/table/utils/shared.js +40 -0
  60. package/dist/runtime/components/ui/field/index.js +6 -1
  61. package/dist/runtime/components/ui/input/Input.vue +1 -1
  62. package/package.json +1 -1
@@ -0,0 +1,56 @@
1
+ import { Schema } from 'effect';
2
+ import type { ColumnDef } from '@tanstack/vue-table';
3
+ import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
4
+ import type { ColumnDefDeps } from '../../../utils/resolve.js';
5
+ export declare const type: "com.shwfed.table.column.number-input";
6
+ export declare const compatibilityDate: "2026-05-20";
7
+ export declare const metadata: {
8
+ readonly name: "数值输入";
9
+ readonly icon: "fluent:number-symbol-20-regular";
10
+ };
11
+ export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
12
+ placeholder: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
13
+ locale: Schema.Literal<["zh"]>;
14
+ message: Schema.SchemaClass<string, string, never>;
15
+ }>], [Schema.Struct<{
16
+ locale: Schema.Literal<["ja", "en", "ko"]>;
17
+ message: Schema.SchemaClass<string, string, never>;
18
+ }>]>>;
19
+ hidden: Schema.optional<Schema.Schema<string, string, never>>;
20
+ disabled: Schema.optional<Schema.Schema<string, string, never>>;
21
+ readonly: Schema.optional<Schema.Schema<string, string, never>>;
22
+ precision: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
23
+ roundingMode: Schema.optional<Schema.Literal<["round", "floor", "ceil"]>>;
24
+ step: Schema.optional<Schema.refine<number, typeof Schema.Number>>;
25
+ valueAsString: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
26
+ min: Schema.optional<Schema.Schema<string, string, never>>;
27
+ max: Schema.optional<Schema.Schema<string, string, never>>;
28
+ derived: Schema.optional<Schema.Struct<{
29
+ mode: Schema.Literal<["formula", "prefill"]>;
30
+ expression: Schema.Schema<string, string, never>;
31
+ }>>;
32
+ title: Schema.TupleType<readonly [Schema.Struct<{
33
+ locale: Schema.Literal<["zh"]>;
34
+ message: Schema.SchemaClass<string, string, never>;
35
+ }>], [Schema.Struct<{
36
+ locale: Schema.Literal<["ja", "en", "ko"]>;
37
+ message: Schema.SchemaClass<string, string, never>;
38
+ }>]>;
39
+ binding: Schema.refine<string, typeof Schema.String>;
40
+ enableSorting: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
41
+ size: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
42
+ grow: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
43
+ tooltip: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
44
+ locale: Schema.Literal<["zh"]>;
45
+ message: Schema.SchemaClass<string, string, never>;
46
+ }>], [Schema.Struct<{
47
+ locale: Schema.Literal<["ja", "en", "ko"]>;
48
+ message: Schema.SchemaClass<string, string, never>;
49
+ }>]>>;
50
+ id: Schema.refine<string, typeof Schema.String>;
51
+ groupId: Schema.optional<typeof Schema.UUID>;
52
+ type: Schema.Literal<["com.shwfed.table.column.number-input"]>;
53
+ compatibilityDate: Schema.Literal<["2026-05-20"]>;
54
+ }>;
55
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
56
+ export declare function toColumnDef(value: Value, { getLocaleText }: ColumnDefDeps): Partial<ColumnDef<unknown, unknown>>;
@@ -0,0 +1,81 @@
1
+ import { Schema } from "effect";
2
+ import { getProperty } from "dot-prop";
3
+ import { Locale } from "../../../../../share/locale.js";
4
+ import { CelRowAccess, derivedRowField, editableColumnFields, editableHeader } from "../../../utils/shared.js";
5
+ export const type = "com.shwfed.table.column.number-input";
6
+ export const compatibilityDate = "2026-05-20";
7
+ export const metadata = {
8
+ name: "\u6570\u503C\u8F93\u5165",
9
+ icon: "fluent:number-symbol-20-regular"
10
+ };
11
+ export function schema(configure) {
12
+ const CelBool = CelRowAccess(configure, { resultType: "bool" });
13
+ const CelNumber = CelRowAccess(configure, { resultType: "number" });
14
+ return Schema.Struct({
15
+ type: Schema.Literal(type),
16
+ compatibilityDate: Schema.Literal(compatibilityDate),
17
+ ...editableColumnFields(),
18
+ placeholder: Schema.optional(Locale.annotations({
19
+ title: "\u5360\u4F4D\u7B26",
20
+ description: "\u5355\u5143\u683C\u4E3A\u7A7A\u65F6\u663E\u793A\u7684\u63D0\u793A\u6587\u672C"
21
+ })),
22
+ hidden: Schema.optional(CelBool.annotations({
23
+ title: "\u9690\u85CF\u6761\u4EF6",
24
+ description: "\u8FD4\u56DE `true` \u65F6\u8BE5\u884C\u7684\u8F93\u5165\u6846\u4E0D\u6E32\u67D3\uFF08\u5176\u4F59\u884C\u4E0D\u53D7\u5F71\u54CD\uFF09"
25
+ })),
26
+ disabled: Schema.optional(CelBool.annotations({
27
+ title: "\u7981\u7528\u6761\u4EF6",
28
+ description: "\u8FD4\u56DE `true` \u65F6\u8F93\u5165\u6846\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u7F16\u8F91"
29
+ })),
30
+ readonly: Schema.optional(CelBool.annotations({
31
+ title: "\u53EA\u8BFB\u6761\u4EF6",
32
+ description: "\u8FD4\u56DE `true` \u65F6\u4EC5\u4EE5\u7EAF\u6587\u672C\u5C55\u793A\u5F53\u524D\u503C"
33
+ })),
34
+ precision: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.nonNegative()).annotations({
35
+ title: "\u5C0F\u6570\u4F4D\u6570",
36
+ description: "\u4FDD\u7559\u7684\u5C0F\u6570\u4F4D\u6570\uFF1B\u7559\u7A7A\u8868\u793A\u4E0D\u9650\u5236\uFF08\u4EFB\u610F\u7CBE\u5EA6\uFF09"
37
+ })),
38
+ roundingMode: Schema.optional(Schema.Literal("round", "floor", "ceil").annotations({
39
+ title: "\u53D6\u6574\u6A21\u5F0F",
40
+ description: "\u8BBE\u7F6E\u4E86\u300C\u5C0F\u6570\u4F4D\u6570\u300D\u65F6\uFF0C\u5931\u7126\u65F6\u6309\u6B64\u6A21\u5F0F\u5C06\u503C\u53D6\u6574\uFF1B\u9ED8\u8BA4 `round`\uFF08\u56DB\u820D\u4E94\u5165\uFF09"
41
+ })),
42
+ step: Schema.optional(Schema.Number.pipe(Schema.positive()).annotations({
43
+ title: "\u6B65\u957F",
44
+ description: "\u53D6\u503C\u7684\u6700\u5C0F\u6B65\u957F\uFF1B\u7559\u7A7A\u65F6\u4E0D\u9650\u5236\uFF08\u4EFB\u610F\u7CBE\u5EA6\uFF09"
45
+ })),
46
+ valueAsString: Schema.optional(Schema.Boolean.annotations({
47
+ title: "\u4EE5\u5B57\u7B26\u4E32\u8BFB\u5199",
48
+ description: "\u5F00\u542F\u540E\u884C\u6570\u636E\u4EE5\u5B57\u7B26\u4E32\u5F62\u5F0F\u5B58\u50A8\u8BE5\u503C\uFF08\u9002\u7528\u4E8E\u540E\u7AEF\u4F7F\u7528 `DECIMAL` / `BIGINT` \u7B49\u9700\u8981\u539F\u6837\u56DE\u4F20\u7684\u5B57\u6BB5\uFF09\uFF1B\u5173\u95ED\u65F6\u82E5\u8BFB\u5230\u7684\u662F\u6570\u503C\u5B57\u7B26\u4E32\u4E5F\u4F1A\u88AB\u89E3\u6790\uFF0C\u4F46\u5199\u56DE\u4ECD\u4E3A\u6570\u503C"
49
+ })),
50
+ min: Schema.optional(CelNumber.annotations({
51
+ title: "\u6700\u5C0F\u503C",
52
+ description: "\u5141\u8BB8\u8F93\u5165\u7684\u6700\u5C0F\u503C\u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `row` / `index` \u548C `form`\uFF1B\u7559\u7A7A\u65F6\u4E0D\u9650\u5236\u4E0B\u9650"
53
+ })),
54
+ max: Schema.optional(CelNumber.annotations({
55
+ title: "\u6700\u5927\u503C",
56
+ description: "\u5141\u8BB8\u8F93\u5165\u7684\u6700\u5927\u503C\u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `row` / `index` \u548C `form`\uFF1B\u7559\u7A7A\u65F6\u4E0D\u9650\u5236\u4E0A\u9650"
57
+ })),
58
+ derived: derivedRowField(configure, "number")
59
+ }).annotations({ title: "NumberInputRenderer", description: "\u6570\u503C\u8F93\u5165\u6E32\u67D3\u5668\uFF08\u53EF\u7F16\u8F91\uFF09" });
60
+ }
61
+ export function toColumnDef(value, { getLocaleText }) {
62
+ return {
63
+ header: editableHeader(getLocaleText(value.title)),
64
+ // `binding` is a dot-prop path (validated non-empty); the cell renderer
65
+ // reads through the same path it writes to. No CEL — accessor is literal.
66
+ accessorFn: (row) => {
67
+ if (!row || typeof row !== "object") return void 0;
68
+ return getProperty(row, value.binding);
69
+ },
70
+ enableSorting: value.enableSorting ?? false,
71
+ // Numeric sort: TanStack's `alphanumeric` already groups numeric runs
72
+ // correctly, and tolerates the `valueAsString` case (a "12" still sorts
73
+ // numerically against a `2`) without us needing a custom comparator.
74
+ sortingFn: "alphanumeric",
75
+ size: value.size,
76
+ meta: {
77
+ grow: value.grow ?? false,
78
+ tooltip: getLocaleText(value.tooltip)
79
+ }
80
+ };
81
+ }
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,292 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { Icon } from "@iconify/vue";
4
+ import { ExpressionEditor } from "../../../../ui/expression-editor";
5
+ import { Switch } from "../../../../ui/switch";
6
+ import { Separator } from "../../../../ui/separator";
7
+ import { Field, FieldLabel } from "../../../../ui/field";
8
+ import { Locale } from "../../../../ui/locale";
9
+ import {
10
+ InputGroup,
11
+ InputGroupAddon,
12
+ InputGroupButton,
13
+ InputGroupInput,
14
+ InputGroupNumberField
15
+ } from "../../../../ui/input-group";
16
+ import { getStructFieldDescription, getStructFieldTitle } from "../../../utils/schema-meta";
17
+ import { Markdown } from "../../../../ui/markdown";
18
+ import DerivedValueEditor from "../../../../form/DerivedValueEditor.vue";
19
+ import { schema } from "./schema";
20
+ defineOptions({ name: "ShwfedTableTextInputRendererConfig" });
21
+ const value = defineModel({ type: Object, ...{ required: true } });
22
+ const fieldSchema = schema(() => {
23
+ });
24
+ const fieldTitle = (field) => getStructFieldTitle(fieldSchema, field) ?? field;
25
+ const fieldDescription = (field) => getStructFieldDescription(fieldSchema, field);
26
+ const ROW_VARS = {
27
+ row: { type: "dyn", label: "\u5F53\u524D\u884C\u6570\u636E" },
28
+ index: { type: "number", label: "\u884C\u7D22\u5F15" }
29
+ };
30
+ const bindingText = computed({
31
+ get: () => value.value.binding ?? "",
32
+ set: (v) => {
33
+ const trimmed = v.trim();
34
+ if (trimmed === "") delete value.value.binding;
35
+ else value.value.binding = trimmed;
36
+ }
37
+ });
38
+ const hiddenModel = computed({
39
+ get: () => value.value.hidden ?? "",
40
+ set: (v) => {
41
+ if (v === "") delete value.value.hidden;
42
+ else value.value.hidden = v;
43
+ }
44
+ });
45
+ const disabledModel = computed({
46
+ get: () => value.value.disabled ?? "",
47
+ set: (v) => {
48
+ if (v === "") delete value.value.disabled;
49
+ else value.value.disabled = v;
50
+ }
51
+ });
52
+ const readonlyModel = computed({
53
+ get: () => value.value.readonly ?? "",
54
+ set: (v) => {
55
+ if (v === "") delete value.value.readonly;
56
+ else value.value.readonly = v;
57
+ }
58
+ });
59
+ const derivedModel = computed({
60
+ get: () => value.value.derived,
61
+ set: (v) => {
62
+ if (v == null) delete value.value.derived;
63
+ else value.value.derived = v;
64
+ }
65
+ });
66
+ </script>
67
+
68
+ <template>
69
+ <div class="space-y-5">
70
+ <div class="grid grid-cols-2 gap-x-6 gap-y-4">
71
+ <Field orientation="vertical">
72
+ <FieldLabel class="text-xs text-zinc-500">
73
+ <template
74
+ v-if="fieldDescription('title')"
75
+ #tooltip
76
+ >
77
+ <Markdown
78
+ :source="fieldDescription('title')"
79
+ block
80
+ class="prose prose-sm prose-zinc"
81
+ />
82
+ </template>
83
+ {{ fieldTitle("title") }}
84
+ </FieldLabel>
85
+ <Locale v-model="value.title" />
86
+ </Field>
87
+ <Field orientation="vertical">
88
+ <FieldLabel class="text-xs text-zinc-500">
89
+ <template
90
+ v-if="fieldDescription('tooltip')"
91
+ #tooltip
92
+ >
93
+ <Markdown
94
+ :source="fieldDescription('tooltip')"
95
+ block
96
+ class="prose prose-sm prose-zinc"
97
+ />
98
+ </template>
99
+ {{ fieldTitle("tooltip") }}
100
+ </FieldLabel>
101
+ <Locale
102
+ v-model="value.tooltip"
103
+ markdown
104
+ />
105
+ </Field>
106
+ </div>
107
+ <div class="grid grid-cols-2 gap-x-6 gap-y-4">
108
+ <Field orientation="vertical">
109
+ <FieldLabel class="text-xs text-zinc-500">
110
+ <template
111
+ v-if="fieldDescription('binding')"
112
+ #tooltip
113
+ >
114
+ <Markdown
115
+ :source="fieldDescription('binding')"
116
+ block
117
+ class="prose prose-sm prose-zinc"
118
+ />
119
+ </template>
120
+ {{ fieldTitle("binding") }}
121
+ </FieldLabel>
122
+ <InputGroup>
123
+ <InputGroupInput
124
+ v-model="bindingText"
125
+ placeholder="例:name"
126
+ class="font-mono"
127
+ />
128
+ </InputGroup>
129
+ </Field>
130
+ <Field orientation="vertical">
131
+ <FieldLabel class="text-xs text-zinc-500">
132
+ <template
133
+ v-if="fieldDescription('placeholder')"
134
+ #tooltip
135
+ >
136
+ <Markdown
137
+ :source="fieldDescription('placeholder')"
138
+ block
139
+ class="prose prose-sm prose-zinc"
140
+ />
141
+ </template>
142
+ {{ fieldTitle("placeholder") }}
143
+ </FieldLabel>
144
+ <Locale v-model="value.placeholder" />
145
+ </Field>
146
+ <Field orientation="vertical">
147
+ <FieldLabel class="text-xs text-zinc-500">
148
+ <template
149
+ v-if="fieldDescription('size')"
150
+ #tooltip
151
+ >
152
+ <Markdown
153
+ :source="fieldDescription('size')"
154
+ block
155
+ class="prose prose-sm prose-zinc"
156
+ />
157
+ </template>
158
+ {{ fieldTitle("size") }}
159
+ </FieldLabel>
160
+ <InputGroup>
161
+ <InputGroupNumberField
162
+ :model-value="value.size"
163
+ :disabled="value.grow"
164
+ :min="0"
165
+ @update:model-value="(v) => value.size = v"
166
+ />
167
+ <InputGroupAddon align="inline-end">
168
+ <InputGroupButton
169
+ :variant="value.grow ? 'primary' : 'ghost'"
170
+ size="xs"
171
+ @click="value.grow = !value.grow"
172
+ >
173
+ <Icon :icon="value.grow ? 'fluent:lock-closed-20-regular' : 'fluent:arrow-autofit-width-20-regular'" />
174
+ {{ fieldTitle("grow") }}
175
+ </InputGroupButton>
176
+ </InputGroupAddon>
177
+ </InputGroup>
178
+ </Field>
179
+ <Field orientation="vertical">
180
+ <FieldLabel class="text-xs text-zinc-500">
181
+ <template
182
+ v-if="fieldDescription('hidden')"
183
+ #tooltip
184
+ >
185
+ <Markdown
186
+ :source="fieldDescription('hidden')"
187
+ block
188
+ class="prose prose-sm prose-zinc"
189
+ />
190
+ </template>
191
+ {{ fieldTitle("hidden") }}
192
+ </FieldLabel>
193
+ <ExpressionEditor
194
+ v-model="hiddenModel"
195
+ placeholder="例:row.archived"
196
+ result-type="bool"
197
+ :extra-vars="ROW_VARS"
198
+ />
199
+ </Field>
200
+ <Field orientation="vertical">
201
+ <FieldLabel class="text-xs text-zinc-500">
202
+ <template
203
+ v-if="fieldDescription('disabled')"
204
+ #tooltip
205
+ >
206
+ <Markdown
207
+ :source="fieldDescription('disabled')"
208
+ block
209
+ class="prose prose-sm prose-zinc"
210
+ />
211
+ </template>
212
+ {{ fieldTitle("disabled") }}
213
+ </FieldLabel>
214
+ <ExpressionEditor
215
+ v-model="disabledModel"
216
+ placeholder="例:row.locked"
217
+ result-type="bool"
218
+ :extra-vars="ROW_VARS"
219
+ />
220
+ </Field>
221
+ <Field orientation="vertical">
222
+ <FieldLabel class="text-xs text-zinc-500">
223
+ <template
224
+ v-if="fieldDescription('readonly')"
225
+ #tooltip
226
+ >
227
+ <Markdown
228
+ :source="fieldDescription('readonly')"
229
+ block
230
+ class="prose prose-sm prose-zinc"
231
+ />
232
+ </template>
233
+ {{ fieldTitle("readonly") }}
234
+ </FieldLabel>
235
+ <ExpressionEditor
236
+ v-model="readonlyModel"
237
+ placeholder="例:row.id != null"
238
+ result-type="bool"
239
+ :extra-vars="ROW_VARS"
240
+ />
241
+ </Field>
242
+ <Field
243
+ orientation="vertical"
244
+ class="col-span-2"
245
+ >
246
+ <FieldLabel class="text-xs text-zinc-500">
247
+ <template
248
+ v-if="fieldDescription('derived')"
249
+ #tooltip
250
+ >
251
+ <Markdown
252
+ :source="fieldDescription('derived')"
253
+ block
254
+ class="prose prose-sm prose-zinc"
255
+ />
256
+ </template>
257
+ {{ fieldTitle("derived") }}
258
+ </FieldLabel>
259
+ <DerivedValueEditor
260
+ v-model="derivedModel"
261
+ result-type="string"
262
+ placeholder="例:string(row.first) + ' ' + string(row.last)"
263
+ />
264
+ </Field>
265
+ </div>
266
+ <Separator />
267
+ <div class="flex flex-wrap gap-x-8 gap-y-3">
268
+ <Field
269
+ orientation="horizontal"
270
+ class="w-auto gap-2"
271
+ >
272
+ <Switch
273
+ :model-value="value.enableSorting ?? false"
274
+ @update:model-value="(v) => value.enableSorting = v"
275
+ />
276
+ <FieldLabel class="text-sm text-zinc-600">
277
+ <template
278
+ v-if="fieldDescription('enableSorting')"
279
+ #tooltip
280
+ >
281
+ <Markdown
282
+ :source="fieldDescription('enableSorting')"
283
+ block
284
+ class="prose prose-sm prose-zinc"
285
+ />
286
+ </template>
287
+ {{ fieldTitle("enableSorting") }}
288
+ </FieldLabel>
289
+ </Field>
290
+ </div>
291
+ </div>
292
+ </template>
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import type { CellContext } from '@tanstack/vue-table';
2
+ import type { Value } from './schema.js';
3
+ type __VLS_Props = {
4
+ column: Value;
5
+ ctx: CellContext<unknown, unknown>;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,140 @@
1
+ <script setup>
2
+ import { Effect } from "effect";
3
+ import { computed, ref, watch } from "vue";
4
+ import { useI18n } from "vue-i18n";
5
+ import { Icon } from "@iconify/vue";
6
+ import { cel as $cel } from "../../../../../utils/cel";
7
+ import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
8
+ import { getLocalizedText } from "../../../../../share/locale";
9
+ import {
10
+ InputGroup,
11
+ InputGroupAddon,
12
+ InputGroupButton,
13
+ InputGroupInput
14
+ } from "../../../../ui/input-group";
15
+ import { useFormState } from "../../../../form/utils/state";
16
+ defineOptions({ name: "ShwfedTableTextInputRendererRuntime" });
17
+ const props = defineProps({
18
+ column: { type: null, required: true },
19
+ ctx: { type: Object, required: true }
20
+ });
21
+ const { locale } = useI18n();
22
+ const celContext = injectCELContext();
23
+ const formState = useFormState();
24
+ const placeholderText = computed(
25
+ () => props.column.placeholder ? getLocalizedText(props.column.placeholder, locale.value) : void 0
26
+ );
27
+ function evalBool(expression, label) {
28
+ if (!expression) return false;
29
+ try {
30
+ return Effect.runSync($cel(expression, celBindings(celContext))) === true;
31
+ } catch (e) {
32
+ console.error(`[shwfed-table] text-input ${label} failed`, e);
33
+ return false;
34
+ }
35
+ }
36
+ const isHidden = computed(() => evalBool(props.column.hidden, "hidden"));
37
+ const isDisabled = computed(() => evalBool(props.column.disabled, "disabled"));
38
+ const isReadonly = computed(() => evalBool(props.column.readonly, "readonly"));
39
+ const effectiveReadonly = computed(
40
+ () => isReadonly.value || props.column.derived?.mode === "formula"
41
+ );
42
+ const cellValue = computed(() => {
43
+ const v = formState.getAt(props.column.binding);
44
+ return v == null ? "" : String(v);
45
+ });
46
+ const draft = ref(cellValue.value);
47
+ const isFocused = ref(false);
48
+ watch(cellValue, (next) => {
49
+ if (!isFocused.value && next !== draft.value) draft.value = next;
50
+ });
51
+ function commit() {
52
+ const next = draft.value === "" ? null : draft.value;
53
+ if (Object.is(next, formState.getAt(props.column.binding))) return;
54
+ formState.setAt(props.column.binding, next);
55
+ }
56
+ function onFocus() {
57
+ isFocused.value = true;
58
+ }
59
+ function onBlur() {
60
+ isFocused.value = false;
61
+ commit();
62
+ }
63
+ const showClear = computed(() => !isDisabled.value && draft.value.length > 0);
64
+ function handleClear() {
65
+ draft.value = "";
66
+ commit();
67
+ }
68
+ </script>
69
+
70
+ <template>
71
+ <!--
72
+ Row budget: 2px outer inset (`p-[0.125rem]`) on each side around a 28px
73
+ (`h-7`) input — total 32px. The inset is the deliberate gap between the
74
+ input border and the td borders; the 28px input gives the 12px text some
75
+ vertical breathing room. Every branch (hidden / readonly / editable) is
76
+ sized identically so rows do not jump when a CEL condition flips. The
77
+ default `Input` ships `h-9` + `text-base` + `md:text-sm` — we override
78
+ each at the same breakpoint or `md:text-sm` would beat our unprefixed
79
+ `text-[0.75rem]` and the input would render at 14px from md+ up. The
80
+ `InputGroup`'s default border is kept on purpose (a borderless cell would
81
+ not read as editable) but its `rounded-md` is overridden to `rounded` for
82
+ a tighter cell look. Clear button uses `icon-xs` with an explicit `size-3`
83
+ icon — the `icon-xs` variant does not constrain svg size on its own, so
84
+ the iconify default would render at the parent's font-size and look
85
+ oversized.
86
+ -->
87
+ <div class="p-[0.125rem] w-full">
88
+ <span
89
+ v-if="isHidden"
90
+ class="block h-7 w-full"
91
+ />
92
+ <span
93
+ v-else-if="effectiveReadonly"
94
+ class="flex items-center h-7 w-full px-2 text-[0.75rem] text-zinc-700 truncate"
95
+ >
96
+ {{ draft || "\u2014" }}
97
+ </span>
98
+ <!--
99
+ Idle border is faded to 30% so a whole grid of inputs reads as data, not
100
+ chrome. `hover:` and `focus-within:` snap it back to full so the active
101
+ cell stands out. `transition-colors ease-out duration-180` overrides
102
+ `InputGroup`'s default `transition-[color,box-shadow]` to set the curve
103
+ and timing explicitly. `data-[disabled=true]:border-zinc-100` from
104
+ `InputGroup` still takes over for disabled, no override needed.
105
+ -->
106
+ <InputGroup
107
+ v-else
108
+ class="group/text-input h-7 rounded border-zinc-200/30 hover:border-zinc-200 focus-within:border-zinc-200 transition-colors ease-out duration-180"
109
+ >
110
+ <InputGroupInput
111
+ v-model="draft"
112
+ :placeholder="placeholderText"
113
+ :disabled="isDisabled"
114
+ class="h-7 text-[0.75rem] md:text-[0.75rem] px-2"
115
+ @focus="onFocus"
116
+ @blur="onBlur"
117
+ @keydown.enter="commit"
118
+ />
119
+ <InputGroupAddon
120
+ v-if="showClear"
121
+ align="inline-end"
122
+ class="[@media(hover:hover)]:opacity-0 transition-opacity group-hover/text-input:opacity-100 focus-within:opacity-100"
123
+ >
124
+ <InputGroupButton
125
+ size="icon-xs"
126
+ data-slot="text-input-clear"
127
+ class="size-4 text-zinc-500 hover:text-zinc-700"
128
+ tabindex="-1"
129
+ @mousedown.prevent
130
+ @click.stop="handleClear"
131
+ >
132
+ <Icon
133
+ icon="fluent:dismiss-20-regular"
134
+ class="size-3"
135
+ />
136
+ </InputGroupButton>
137
+ </InputGroupAddon>
138
+ </InputGroup>
139
+ </div>
140
+ </template>
@@ -0,0 +1,9 @@
1
+ import type { CellContext } from '@tanstack/vue-table';
2
+ import type { Value } from './schema.js';
3
+ type __VLS_Props = {
4
+ column: Value;
5
+ ctx: CellContext<unknown, unknown>;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,50 @@
1
+ import { Schema } from 'effect';
2
+ import type { ColumnDef } from '@tanstack/vue-table';
3
+ import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
4
+ import type { ColumnDefDeps } from '../../../utils/resolve.js';
5
+ export declare const type: "com.shwfed.table.column.text-input";
6
+ export declare const compatibilityDate: "2026-05-20";
7
+ export declare const metadata: {
8
+ readonly name: "文本输入";
9
+ readonly icon: "fluent:text-field-20-regular";
10
+ };
11
+ export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
12
+ placeholder: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
13
+ locale: Schema.Literal<["zh"]>;
14
+ message: Schema.SchemaClass<string, string, never>;
15
+ }>], [Schema.Struct<{
16
+ locale: Schema.Literal<["ja", "en", "ko"]>;
17
+ message: Schema.SchemaClass<string, string, never>;
18
+ }>]>>;
19
+ hidden: Schema.optional<Schema.Schema<string, string, never>>;
20
+ disabled: Schema.optional<Schema.Schema<string, string, never>>;
21
+ readonly: Schema.optional<Schema.Schema<string, string, never>>;
22
+ derived: Schema.optional<Schema.Struct<{
23
+ mode: Schema.Literal<["formula", "prefill"]>;
24
+ expression: Schema.Schema<string, string, never>;
25
+ }>>;
26
+ title: Schema.TupleType<readonly [Schema.Struct<{
27
+ locale: Schema.Literal<["zh"]>;
28
+ message: Schema.SchemaClass<string, string, never>;
29
+ }>], [Schema.Struct<{
30
+ locale: Schema.Literal<["ja", "en", "ko"]>;
31
+ message: Schema.SchemaClass<string, string, never>;
32
+ }>]>;
33
+ binding: Schema.refine<string, typeof Schema.String>;
34
+ enableSorting: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
35
+ size: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
36
+ grow: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
37
+ tooltip: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
38
+ locale: Schema.Literal<["zh"]>;
39
+ message: Schema.SchemaClass<string, string, never>;
40
+ }>], [Schema.Struct<{
41
+ locale: Schema.Literal<["ja", "en", "ko"]>;
42
+ message: Schema.SchemaClass<string, string, never>;
43
+ }>]>>;
44
+ id: Schema.refine<string, typeof Schema.String>;
45
+ groupId: Schema.optional<typeof Schema.UUID>;
46
+ type: Schema.Literal<["com.shwfed.table.column.text-input"]>;
47
+ compatibilityDate: Schema.Literal<["2026-05-20"]>;
48
+ }>;
49
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
50
+ export declare function toColumnDef(value: Value, { getLocaleText }: ColumnDefDeps): Partial<ColumnDef<unknown, unknown>>;