@ticatec/uniface-element 5.0.0 → 5.0.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.
Files changed (43) hide show
  1. package/dist/app_layout/ClassicLayout.svelte +8 -40
  2. package/dist/app_layout/ColumnsLayout.svelte +3 -3
  3. package/dist/app_layout/HeaderLayout.svelte +4 -20
  4. package/dist/app_layout/RowsLayout.svelte +3 -3
  5. package/dist/app_layout/SidebarLayout.svelte +4 -20
  6. package/dist/composite/transfer/Transfer.svelte +12 -6
  7. package/dist/data_display/accordion/Accordion.svelte +1 -4
  8. package/dist/data_display/card/CommonCardActionBar.svelte +1 -27
  9. package/dist/data_display/list_box/ListBox.svelte +6 -8
  10. package/dist/data_table/lib/clickOutSide.js +6 -5
  11. package/dist/form/attachment_files/AttachmentEditor.svelte +13 -8
  12. package/dist/form/attachment_files/FileUploadBar.svelte +5 -2
  13. package/dist/form/attachment_files/FileUploadPanel.svelte +6 -72
  14. package/dist/form/checkbox/CheckBox.svelte +2 -7
  15. package/dist/form/color_picker/ColorPicker.svelte +150 -35
  16. package/dist/form/color_picker/ColorPicker.svelte.d.ts +7 -25
  17. package/dist/form/color_picker/README.md +14 -7
  18. package/dist/form/color_picker/README_CN.md +14 -7
  19. package/dist/form/common_editor/CommonPicker.svelte +4 -7
  20. package/dist/form/image-files/ImagePreview.svelte +15 -7
  21. package/dist/form/memo_editor/MemoEditor.svelte +2 -2
  22. package/dist/form/number_editor/NumberEditor.svelte +2 -2
  23. package/dist/form/options_multi_select/OptionsMultiSelect.svelte +1 -9
  24. package/dist/form/radio_button/RadioButton.svelte +2 -8
  25. package/dist/form/text_editor/TextEditor.svelte +2 -2
  26. package/dist/general/button/Button.svelte +2 -0
  27. package/dist/general/split/Split.svelte +24 -4
  28. package/dist/navigation/nav_menu/MenuItem.d.ts +4 -2
  29. package/dist/navigation/nav_menu/NavigatorMenu.svelte +157 -34
  30. package/dist/navigation/nav_menu/NavigatorMenu.svelte.d.ts +2 -3
  31. package/dist/navigation/nav_menu/NavigatorMenuItem.svelte +15 -12
  32. package/dist/navigation/nav_menu/NavigatorMenuItem.svelte.d.ts +3 -3
  33. package/dist/navigation/progress_step_bar/ProgressStepBlock.svelte +1 -5
  34. package/dist/overlay/common/FloatingPanel.svelte +0 -6
  35. package/dist/overlay/common/Popover.svelte +8 -3
  36. package/dist/overlay/common/uniface-utils.js +0 -1
  37. package/dist/overlay/context-menu/ContextMenuPanel.svelte +14 -2
  38. package/dist/overlay/dialog/Dialog.svelte +3 -4
  39. package/dist/overlay/dialog/DialogWrapper.svelte +5 -2
  40. package/dist/ticatec-uniface-web.css +26 -6
  41. package/dist/utils/prefixFilter.d.ts +3 -3
  42. package/dist/utils/prefixFilter.js +3 -5
  43. package/package.json +1 -1
@@ -1,58 +1,173 @@
1
1
  <script lang="ts">
2
2
 
3
-
4
- import type {OnChangeHandler} from "../../types";
5
3
  import CommonPicker from "../common_editor/CommonPicker.svelte";
4
+ import type CommonFieldProps from "../common_editor/CommonFieldProps";
6
5
 
7
- //type OnActionHandler = () => void;
6
+ interface Props extends CommonFieldProps {
7
+ value?: string;
8
+ }
8
9
 
9
- //export let showHex = true;
10
- //export let showRgb = false;
10
+ // The manually-typed value must resolve to exactly #RRGGBB. An 8-digit #RRGGBBAA
11
+ // form was supported briefly, but <input type="color"> has no concept of alpha —
12
+ // the swatch always showed just the RGB portion and nothing in the UI ever
13
+ // rendered the transparency, so the last 2 digits had no visible effect. Dropped
14
+ // rather than half-implemented; revisit with a real alpha-aware swatch (checkered
15
+ // background + rgba preview) if this is needed later.
16
+ const HEX_PATTERN = /^#[0-9a-fA-F]{6}$/;
17
+ const DEFAULT_SWATCH = '#000000';
18
+
19
+ // CommonPicker always renders its own internal <input> in the non-readonly branch,
20
+ // even when a `children` snippet is also supplied — it doesn't apply the same
21
+ // "children replaces the default input" rule that its readonly branch does. Every
22
+ // other consumer works around this by never passing `children` in the first place
23
+ // (DatePicker, OptionsSelect...) except OptionsMultiSelect, which needs custom
24
+ // content (its tag list) the same way ColorPicker needs its swatch+hex layout —
25
+ // it hides CommonPicker's own input via `input$style` (shrink to 1x1, transparent,
26
+ // click-through) so only the custom children are visible. Same fix here: without
27
+ // this, edit mode showed CommonPicker's own empty input fighting for space with our
28
+ // swatch, while readonly mode (which only renders `children`) looked correct.
29
+ const HIDDEN_INTERNAL_INPUT_STYLE = 'position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none';
30
+
31
+ let {
32
+ variant,
33
+ theme,
34
+ disabled = false,
35
+ readonly = false,
36
+ compact = false,
37
+ value = $bindable<string | undefined>("#000000"),
38
+ style,
39
+ placeholder,
40
+ error,
41
+ tooltip,
42
+ onchange,
43
+ onfocus,
44
+ onblur
45
+ }: Props = $props();
46
+
47
+ let editor: HTMLInputElement | undefined = $state();
48
+ let textInput: HTMLInputElement | undefined = $state();
49
+
50
+ // Editing buffer for the manual hex field, kept separate from `value` so the user
51
+ // can type through transient/incomplete states (e.g. "#3b8") without every
52
+ // keystroke being validated against the full #RRGGBB format — validation only
53
+ // happens on commit (blur / Enter).
54
+ let textValue = $state(value ?? '');
55
+
56
+ $effect(() => {
57
+ textValue = value ?? '';
58
+ });
59
+
60
+ let swatchValue = $derived(value && HEX_PATTERN.test(value) ? value : DEFAULT_SWATCH);
61
+
62
+ export const focus = () => {
63
+ editor?.focus();
64
+ }
11
65
 
12
- export let variant: '' | 'plain' | 'outlined' | 'filled' = '';
13
- export let disabled: boolean = false;
14
- export let readonly: boolean = false;
15
- export let compact: boolean = false;
16
- export let value: any = "#ff3e00";
17
- export let style: string = '';
18
- export let onChange: OnChangeHandler<any> = null as unknown as OnChangeHandler<any>;
66
+ const clean = () => {
67
+ value = undefined;
68
+ onchange?.(undefined);
69
+ }
19
70
 
71
+ const handleActionIconClick = () => {
72
+ if (!readonly && !disabled) {
73
+ editor?.click();
74
+ }
75
+ }
20
76
 
21
- let oldValue = value;
77
+ const handleSwatchChange = (event: Event) => {
78
+ const newValue = (event.target as HTMLInputElement).value;
79
+ value = newValue;
80
+ onchange?.(newValue);
81
+ }
22
82
 
23
- $: if (oldValue != value) {
24
- oldValue = value;
83
+ // The native color picker dialog opens on `click`, and preventing the default
84
+ // action here blocks it — this is the only reliable way to make the swatch
85
+ // non-interactive for `readonly`, since <input type="color"> ignores the
86
+ // `readonly` attribute entirely. Using the native `disabled` attribute instead
87
+ // would work too, but it also greys the swatch out, which conflates readonly
88
+ // with disabled (see the component README: readonly stays fully visible,
89
+ // disabled is dimmed).
90
+ const handleColorInputClick = (event: MouseEvent) => {
91
+ if (readonly) {
92
+ event.preventDefault();
93
+ }
25
94
  }
26
95
 
27
- let editor: any;
96
+ const handleColorInputKeydown = (event: KeyboardEvent) => {
97
+ if (readonly && (event.key === 'Enter' || event.key === ' ')) {
98
+ event.preventDefault();
99
+ }
100
+ }
28
101
 
29
- const clean = () => {
30
- onChange(null)
102
+ const commitTextValue = () => {
103
+ let candidate = textValue.trim();
104
+ if (candidate && !candidate.startsWith('#')) {
105
+ candidate = `#${candidate}`;
106
+ }
107
+ if (HEX_PATTERN.test(candidate)) {
108
+ textValue = candidate;
109
+ if (candidate !== value) {
110
+ value = candidate;
111
+ onchange?.(candidate);
112
+ }
113
+ } else {
114
+ // Doesn't resolve to a valid #RRGGBB value on commit — revert to the last
115
+ // known-good value rather than accepting a malformed color.
116
+ textValue = value ?? '';
117
+ }
118
+ }
119
+
120
+ // Only '#' and hex digits are allowed while typing; the exact length requirement
121
+ // is enforced separately on commit (see commitTextValue), since rejecting
122
+ // incomplete input on every keystroke would make it impossible to type a value
123
+ // character by character.
124
+ const handleTextKeydown = (event: KeyboardEvent) => {
125
+ const allowKeys = ['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
126
+ if (allowKeys.includes(event.key) || event.metaKey || event.ctrlKey) return;
127
+ if (event.key === 'Enter') {
128
+ event.preventDefault();
129
+ textInput?.blur();
130
+ return;
131
+ }
132
+ if (event.key === 'Escape') {
133
+ textValue = value ?? '';
134
+ textInput?.blur();
135
+ return;
136
+ }
137
+ if (!/^[0-9a-fA-F#]$/.test(event.key)) {
138
+ event.preventDefault();
139
+ }
31
140
  }
32
141
 
33
- const handleActionIconClick = async () => {
34
- editor.click();
142
+ const handleTextPaste = (event: ClipboardEvent) => {
143
+ const pasted = event.clipboardData?.getData('text') ?? '';
144
+ if (/[^0-9a-fA-F#]/.test(pasted)) {
145
+ event.preventDefault();
146
+ }
35
147
  }
36
148
 
37
- function handleColorChange(event: Event) {
38
- // selectedColor = (event.target as HTMLElement).value;
39
- // rgbValues = hexToRgb(selectedColor);
40
- // console.log(selectedColor, rgbValues);
41
- // dispatch('change', {
42
- // hex: selectedColor,
43
- // rgb: rgbValues
44
- // });
149
+ // Defense in depth against anything that bypasses handleTextKeydown (IME, mobile
150
+ // virtual keyboards, drag-and-drop text) — strip disallowed characters and cap at
151
+ // one leading '#' plus 6 hex digits.
152
+ const handleTextInput = () => {
153
+ const hasHash = textValue.startsWith('#');
154
+ const hexOnly = textValue.replace(/#/g, '').replace(/[^0-9a-fA-F]/g, '').slice(0, 6);
155
+ const sanitized = (hasHash ? '#' : '') + hexOnly;
156
+ if (sanitized !== textValue) {
157
+ textValue = sanitized;
158
+ }
45
159
  }
46
160
 
47
161
  </script>
48
162
 
49
- <CommonPicker {variant} {style} {compact} className="multiple" iconName="icon_google_color_lens"
50
- autoFit clear={clean} textValue={value} {readonly} {disabled} iconClickHandler={handleActionIconClick}>
51
- <!-- <div style="flex: 1 1 auto; width: 50%; height: 60%; border-radius: 4px; background-color: {value}"></div>-->
163
+ <CommonPicker {variant} {style} {theme} {error} {tooltip} {compact} {placeholder} className="multiple" iconName="icon_google_color_lens"
164
+ autoFit clear={clean} textValue={value} {readonly} {disabled} {onfocus} {onblur} iconClickHandler={handleActionIconClick}
165
+ input$style={HIDDEN_INTERNAL_INPUT_STYLE}>
52
166
  <div style="width: 100%; display: flex; overflow: hidden; flex-direction: row; align-items: center; height: 100%; gap: 8px">
53
- <input bind:this={editor} style="flex: 0 0 auto; width: 40%; height: 80%; border-radius: 8px;" type="color" bind:value on:change={handleColorChange}/>
54
- <input style="flex: 0 0 auto; width: 50%; text-align: center" class="text-editor" readonly value={value??''} {disabled}/>
167
+ <input bind:this={editor} style="flex: 0 0 auto; width: 40%; height: 80%; border-radius: 8px;" type="color" value={swatchValue} {disabled}
168
+ tabindex={readonly ? -1 : 0} onclick={handleColorInputClick} onkeydown={handleColorInputKeydown} onchange={handleSwatchChange}/>
169
+ <input bind:this={textInput} style="flex: 0 0 auto; width: 50%; text-align: center" class="text-editor" bind:value={textValue}
170
+ readonly={readonly} {disabled} {placeholder} maxlength="7" spellcheck="false" autocomplete="off"
171
+ onblur={commitTextValue} onkeydown={handleTextKeydown} onpaste={handleTextPaste} oninput={handleTextInput}/>
55
172
  </div>
56
-
57
-
58
173
  </CommonPicker>
@@ -1,27 +1,9 @@
1
- import type { OnChangeHandler } from "../../types";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
1
+ import type CommonFieldProps from "../common_editor/CommonFieldProps";
2
+ interface Props extends CommonFieldProps {
3
+ value?: string;
14
4
  }
15
- declare const ColorPicker: $$__sveltets_2_IsomorphicComponent<{
16
- variant?: "" | "plain" | "outlined" | "filled";
17
- disabled?: boolean;
18
- readonly?: boolean;
19
- compact?: boolean;
20
- value?: any;
21
- style?: string;
22
- onChange?: OnChangeHandler<any>;
23
- }, {
24
- [evt: string]: CustomEvent<any>;
25
- }, {}, {}, string>;
26
- type ColorPicker = InstanceType<typeof ColorPicker>;
5
+ declare const ColorPicker: import("svelte").Component<Props, {
6
+ focus: () => void;
7
+ }, "value">;
8
+ type ColorPicker = ReturnType<typeof ColorPicker>;
27
9
  export default ColorPicker;
@@ -40,7 +40,7 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
40
40
 
41
41
  | Prop | Type | Default | Description |
42
42
  |------|------|---------|-------------|
43
- | `value` | `string` (bindable) | `"#000000"` | The selected color in hex format (e.g., `#3b82f6`). `bind:value={...}` for two-way binding. |
43
+ | `value` | `string` (bindable) | `"#000000"` | The selected color in `#RRGGBB` hex format (e.g., `#3b82f6`). `bind:value={...}` for two-way binding. |
44
44
  | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual style variant. |
45
45
  | `compact` | `boolean` | `false` | Dense layout for table cells and inline forms. |
46
46
  | `disabled` | `boolean` | `false` | Fully disabled — the color picker is non-interactive. |
@@ -263,18 +263,25 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
263
263
 
264
264
  ## Behavior Notes
265
265
 
266
- ### Native color input
266
+ ### Native color input + editable hex field
267
267
 
268
- The component uses the browser's native `<input type="color">` element, which provides a consistent color picker across all platforms. The visual swatch is a custom preview layer on top of the native input.
268
+ The component shows two parts side by side: a native `<input type="color">` swatch (opens the browser's built-in color picker) and a text field showing the hex value. The text field is directly editable — typing a value and committing it (blur or Enter) updates `value` the same as picking a color from the swatch.
269
269
 
270
- ### Hex format
270
+ There's no alpha/transparency support — `<input type="color">` has no concept of it, so `value` is always a plain 6-digit `#rrggbb`.
271
271
 
272
- The `value` prop expects and returns colors in hex format (e.g., `#3b82f6`). The native color input always returns hex format.
272
+ ### Hex format and validation
273
+
274
+ `value` must be exactly `#RRGGBB` (6 hex digits after `#`, case-insensitive). When typing in the text field:
275
+
276
+ - Only `#` and hex digits (`0-9a-fA-F`) can be typed, capped at 6 digits — anything else is blocked or truncated as you type.
277
+ - The value is validated on commit (blur or Enter), not on every keystroke, so you can type through incomplete intermediate states.
278
+ - A missing leading `#` is added automatically on commit (e.g. typing `ff0000` commits as `#ff0000`).
279
+ - If the committed text isn't a valid `#RRGGBB` string, the field reverts to the last valid value — `onchange` is not called.
273
280
 
274
281
  ### Disabled and readonly states
275
282
 
276
- - **Disabled**: The color picker is non-interactive. The visual swatch is dimmed.
277
- - **Readonly**: The color picker is non-interactive but the visual swatch is fully visible.
283
+ - **Disabled**: The color picker is non-interactive. The visual swatch is dimmed, and the hex field is disabled.
284
+ - **Readonly**: The color picker is non-interactive but fully visible — the swatch blocks clicks (it can't open the native picker) and the hex field becomes a non-editable (but not dimmed) readonly text field.
278
285
 
279
286
  ## Accessibility
280
287
 
@@ -40,7 +40,7 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
40
40
 
41
41
  | 属性 | 类型 | 默认值 | 说明 |
42
42
  |------|------|--------|------|
43
- | `value` | `string`(可绑定) | `"#000000"` | hex 格式选择的颜色(例如 `#3b82f6`)。`bind:value={...}` 用于双向绑定。 |
43
+ | `value` | `string`(可绑定) | `"#000000"` | `#RRGGBB` 格式的颜色(例如 `#3b82f6`)。`bind:value={...}` 用于双向绑定。 |
44
44
  | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉风格变体。 |
45
45
  | `compact` | `boolean` | `false` | 紧凑布局,适用于表格单元格和行内表单。 |
46
46
  | `disabled` | `boolean` | `false` | 完全禁用 —— 颜色选择器不可交互。 |
@@ -263,18 +263,25 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
263
263
 
264
264
  ## 行为说明
265
265
 
266
- ### 原生颜色输入
266
+ ### 原生颜色输入 + 可编辑的 hex 文本框
267
267
 
268
- 该组件使用浏览器的原生 `<input type="color">` 元素,在所有平台上提供一致的颜色选择器。视觉色板是原生输入上方的自定义预览层。
268
+ 组件并排展示两部分:原生 `<input type="color">` 色板(点击打开浏览器内置的颜色选择器)和展示 hex 值的文本框。文本框可以直接编辑——输入一个值并提交(失焦或回车)后,`value` 会像通过色板选色一样更新。
269
269
 
270
- ### Hex 格式
270
+ 不支持透明通道——`<input type="color">` 本身没有这个概念,因此 `value` 始终是纯 6 位 `#rrggbb`。
271
271
 
272
- `value` 属性期望并返回 hex 格式的颜色(例如 `#3b82f6`)。原生颜色输入始终返回 hex 格式。
272
+ ### Hex 格式与校验
273
+
274
+ `value` 必须是合法的 `#RRGGBB`(`#` 后跟 6 位十六进制数字,大小写不敏感)。在文本框中输入时:
275
+
276
+ - 只能输入 `#` 和十六进制字符(`0-9a-fA-F`),最多 6 位——其他字符在输入时会被拦截或截断。
277
+ - 校验只在提交时(失焦或回车)进行,不是每次按键都校验,因此可以正常输入中间的不完整状态。
278
+ - 提交时如果缺少开头的 `#` 会自动补上(例如输入 `ff0000` 会被提交为 `#ff0000`)。
279
+ - 如果提交的文本不是合法的 `#RRGGBB`,文本框会回退到上一个合法值——不会触发 `onchange`。
273
280
 
274
281
  ### 禁用和只读状态
275
282
 
276
- - **禁用**:颜色选择器不可交互。视觉色板变暗。
277
- - **只读**:颜色选择器不可交互,但视觉色板完全可见。
283
+ - **禁用**:颜色选择器不可交互。视觉色板变暗,hex 文本框也被禁用。
284
+ - **只读**:颜色选择器不可交互,但完全可见——色板会拦截点击(无法打开原生选择器),hex 文本框变为不可编辑(但不变暗)的只读文本框。
278
285
 
279
286
  ## 无障碍
280
287
 
@@ -70,7 +70,7 @@
70
70
  let editorElement: HTMLElement | undefined = $state();
71
71
  let editor: HTMLDivElement | undefined = $state();
72
72
  let hasFocus = $state(false);
73
- let canClean = $state(!mandatory && value != null);
73
+ let canClean = $derived(!mandatory && value != null);
74
74
 
75
75
 
76
76
  const handleFocus = (event: FocusEvent) => {
@@ -111,7 +111,6 @@
111
111
  }
112
112
 
113
113
  $effect(() => {
114
- canClean = !mandatory && value != null;
115
114
  if (!isOpen && hasFocus) {
116
115
  setTimeout(() => {
117
116
  if (document.activeElement !== editor) {
@@ -148,12 +147,12 @@
148
147
  <input style={input$style} tabindex="-1" readonly {disabled} value={textValue}/>
149
148
  {/if}
150
149
  {:else}
151
- <div style="flex: 1 1 auto; position: relative; overflow: hidden; height: 100%" onclick={openPopup}>
150
+ <div style="flex: 1 1 auto; position: relative; overflow: hidden; height: 100%">
152
151
  {#if input$readonly}
153
- <input bind:this={editor} style={input$style} value={textValue} readonly {placeholder} onkeydown={handleKeyDown}
152
+ <input bind:this={editor} style={input$style} value={textValue} readonly {placeholder} onclick={openPopup} onkeydown={handleKeyDown}
154
153
  tabindex="0"/>
155
154
  {:else}
156
- <input bind:this={editor} style={input$style} value={textValue} {oninput} {oncompositionstart} {oncompositionend}
155
+ <input bind:this={editor} style={input$style} value={textValue} {oninput} {oncompositionstart} {oncompositionend} onclick={openPopup}
157
156
  {placeholder} tabindex="0" onkeydown={handleKeyDown}/>
158
157
  {/if}
159
158
  {#if children}
@@ -181,5 +180,3 @@
181
180
  {/if}
182
181
  </Popover>
183
182
  {/if}
184
-
185
-
@@ -65,12 +65,13 @@
65
65
  <div style="width: 100%; height: 100%; overflow: auto">
66
66
  <div class="image-container"
67
67
  style="width: {containerWidth}px; height: {containerHeight}px; {imgStyle}">
68
- <img onclick={(e: Event) => e.stopPropagation()}
69
- src={src}
70
- alt="Preview"
71
- onload={handleImageLoad}
72
- style="width: {imgPanelWidth}px; height: {imgPanelHeight}px;"
73
- />
68
+ <button type="button" class="image-content" aria-label="Preview image" onclick={(e: Event) => e.stopPropagation()}>
69
+ <img src={src}
70
+ alt="Preview"
71
+ onload={handleImageLoad}
72
+ style="width: {imgPanelWidth}px; height: {imgPanelHeight}px;"
73
+ />
74
+ </button>
74
75
  </div>
75
76
  </div>
76
77
  <div class="toolbar" onclick={(e: Event) => e.stopPropagation()} aria-hidden="true">
@@ -120,10 +121,17 @@
120
121
  img {
121
122
  transition: transform 0.3s ease;
122
123
  }
124
+
125
+ .image-content {
126
+ display: inline-flex;
127
+ padding: 0;
128
+ border: 0;
129
+ background: transparent;
130
+ }
123
131
  }
124
132
 
125
133
  &:hover .toolbar {
126
134
  display: flex;
127
135
  }
128
136
  }
129
- </style>
137
+ </style>
@@ -78,7 +78,7 @@
78
78
  {disabled}
79
79
  rows={autoHeight ? undefined : rows}
80
80
  {wrap}
81
- {maxLength}
81
+ maxlength={maxLength}
82
82
  bind:value
83
83
  {...events}
84
84
  {...input$}
@@ -168,4 +168,4 @@
168
168
  z-index: 10;
169
169
  }
170
170
 
171
- </style>
171
+ </style>
@@ -45,7 +45,7 @@
45
45
  ...restProps
46
46
  }: Props = $props();
47
47
 
48
- let numberInput: NumberInput;
48
+ let numberInput = $state<NumberInput>();
49
49
 
50
50
  export function focus() {
51
51
  numberInput?.focus();
@@ -94,4 +94,4 @@
94
94
  {...input$}
95
95
  />
96
96
  {/if}
97
- </CommonEditor>
97
+ </CommonEditor>
@@ -214,16 +214,8 @@
214
214
  outline: none;
215
215
  }
216
216
 
217
- .options-editor.disabled {
218
- filter: grayscale(1);
219
- }
220
-
221
- .options-editor.disabled * {
222
- cursor: not-allowed;
223
- }
224
-
225
217
  .options-editor .placeholder {
226
218
  color: var(--uniface-editor-color-placeholder);
227
219
  }
228
220
 
229
- </style>
221
+ </style>
@@ -72,13 +72,6 @@
72
72
  text-overflow: ellipsis;
73
73
  color: var(--uniface-checkbox-text-color);
74
74
  }
75
- .uniface-radio-button label {
76
- display: inline-block;
77
- width: 100%;
78
- text-overflow: ellipsis;
79
- overflow: hidden;
80
- cursor: pointer;
81
- }
82
75
  .uniface-radio-button > span {
83
76
  line-height: var(--uniface-checkbox-size);
84
77
  padding-left: var(--uniface-checkbox-label-padding-left);
@@ -107,6 +100,7 @@
107
100
  --disabled-inner: var(--uniface-checkbox-checkmark-color-disabled);
108
101
  -webkit-appearance: none;
109
102
  -moz-appearance: none;
103
+ appearance: none;
110
104
  height: var(--uniface-checkbox-size);
111
105
  width: var(--uniface-checkbox-size);
112
106
  outline: none;
@@ -180,4 +174,4 @@
180
174
  .uniface-radio-button *::before,
181
175
  .uniface-radio-button *::after {
182
176
  box-sizing: inherit;
183
- }</style>
177
+ }</style>
@@ -69,7 +69,7 @@
69
69
  }
70
70
 
71
71
  let input$ = $derived(prefixFilter(restProps, 'input$', excludeAttrs));
72
- let eventHandlers = filterProps(restProps, keyboardAttrs);
72
+ let eventHandlers = $derived(filterProps(restProps, keyboardAttrs));
73
73
 
74
74
  const clear = () => {
75
75
  value = undefined;
@@ -83,4 +83,4 @@
83
83
  {error}>
84
84
  <input bind:this={editor} {...input$} {placeholder} {...eventHandlers}
85
85
  oninput={handleInput} onchange={handleChange} bind:value/>
86
- </CommonEditor>
86
+ </CommonEditor>
@@ -44,6 +44,8 @@
44
44
 
45
45
  </script>
46
46
 
47
+ <!-- autofocus is an explicit opt-in component API; consumers decide when initial focus is appropriate. -->
48
+ <!-- svelte-ignore a11y_autofocus -->
47
49
  <button
48
50
  {style}
49
51
  class="uniface-button common-button {variant} {size} {type} {theme}"
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { onMount, onDestroy } from 'svelte';
2
+ import {onDestroy} from 'svelte';
3
3
 
4
4
  interface Props {
5
5
  direction?: "horizontal" | "vertical";
@@ -76,6 +76,22 @@
76
76
  }
77
77
  };
78
78
 
79
+ const resizeWithKeyboard = (event: KeyboardEvent) => {
80
+ if (fixed || !bindingPanel) return;
81
+ const decreaseKey = direction === 'horizontal' ? 'ArrowUp' : 'ArrowLeft';
82
+ const increaseKey = direction === 'horizontal' ? 'ArrowDown' : 'ArrowRight';
83
+ if (event.key !== decreaseKey && event.key !== increaseKey) return;
84
+
85
+ event.preventDefault();
86
+ const directionFactor = event.key === increaseKey ? 1 : -1;
87
+ const delta = directionFactor * (reverse ? -10 : 10);
88
+ const currentSize = direction === 'horizontal' ? bindingPanel.clientHeight : bindingPanel.clientWidth;
89
+ const newSize = Math.max(5, currentSize + delta);
90
+ if (direction === 'horizontal') bindingPanel.style.height = `${newSize}px`;
91
+ else bindingPanel.style.width = `${newSize}px`;
92
+ onResizeEnd?.(newSize);
93
+ };
94
+
79
95
  // 组件销毁时清理
80
96
  onDestroy(() => {
81
97
  if (isDragging) {
@@ -85,6 +101,9 @@
85
101
 
86
102
  </script>
87
103
 
104
+ <!-- The separator is intentionally focusable because its arrow-key handler resizes the bound panel. -->
105
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
106
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
88
107
  <div
89
108
  class="uniface-divider {direction}"
90
109
  class:fixed
@@ -92,9 +111,10 @@
92
111
  role="separator"
93
112
  aria-orientation={direction}
94
113
  aria-label="Resize handle"
95
- tabindex="0"
114
+ tabindex={fixed ? -1 : 0}
96
115
  onmousedown={startDrag}
97
- />
116
+ onkeydown={resizeWithKeyboard}
117
+ ></div>
98
118
 
99
119
  <style>
100
120
  .uniface-divider {
@@ -127,4 +147,4 @@
127
147
  .uniface-divider:active {
128
148
  transition: none;
129
149
  }
130
- </style>
150
+ </style>
@@ -1,7 +1,9 @@
1
- import type { IHierarchyData } from "../../lib";
2
1
  export default interface MenuItem {
3
2
  [key: string]: any;
4
3
  }
5
- export interface MenuNode extends IHierarchyData<MenuItem> {
4
+ export interface MenuNode {
5
+ item: MenuItem;
6
+ children?: MenuNode[];
7
+ expand?: boolean;
6
8
  }
7
9
  export type OnMenuClick = (item: MenuItem) => void;