@ticatec/uniface-element 5.0.2 → 5.0.4

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.
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type {Snippet} from 'svelte';
3
3
  import type CardAction from "./CardAction";
4
+ import type {CardActionsAlign} from "./CardAction";
4
5
  import CommonCardActionBar from "./CommonCardActionBar.svelte";
5
6
  import CardHeader from "./CardHeader.svelte";
6
7
 
@@ -13,6 +14,11 @@
13
14
  headerBar?: Snippet;
14
15
  children?: Snippet;
15
16
  theme?: string;
17
+ /** 操作栏图标排布方式,默认 'distribute'(按数量平分宽度、每个居中,不会被拉到两端) */
18
+ actionsAlign?: CardActionsAlign;
19
+ /** 'center'/'left'/'right' 模式下相邻图标的固定间距,例如 '16px' */
20
+ actionsGap?: string;
21
+ contentStyle?: string;
16
22
  }
17
23
 
18
24
  let {
@@ -23,7 +29,10 @@
23
29
  data = null,
24
30
  headerBar,
25
31
  children,
26
- theme = ""
32
+ theme = "",
33
+ actionsAlign = 'distribute',
34
+ actionsGap,
35
+ contentStyle = ''
27
36
  }: Props = $props();
28
37
  </script>
29
38
 
@@ -38,13 +47,13 @@
38
47
  {@render headerBar()}
39
48
  </div>
40
49
  {/if}
41
- <div class="card-content">
50
+ <div class="card-content" style={contentStyle}>
42
51
  {#if children}
43
52
  {@render children()}
44
53
  {/if}
45
54
  </div>
46
55
  {#if actions && actions.length > 0}
47
- <CommonCardActionBar {actions} {data}/>
56
+ <CommonCardActionBar {actions} {data} align={actionsAlign} gap={actionsGap}/>
48
57
  {/if}
49
58
  </div>
50
59
  </div>
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type CardAction from "./CardAction";
3
+ import type { CardActionsAlign } from "./CardAction";
3
4
  interface Props {
4
5
  style?: string;
5
6
  title?: string;
@@ -9,6 +10,11 @@ interface Props {
9
10
  headerBar?: Snippet;
10
11
  children?: Snippet;
11
12
  theme?: string;
13
+ /** 操作栏图标排布方式,默认 'distribute'(按数量平分宽度、每个居中,不会被拉到两端) */
14
+ actionsAlign?: CardActionsAlign;
15
+ /** 'center'/'left'/'right' 模式下相邻图标的固定间距,例如 '16px' */
16
+ actionsGap?: string;
17
+ contentStyle?: string;
12
18
  }
13
19
  declare const Card: import("svelte").Component<Props, {}, "">;
14
20
  type Card = ReturnType<typeof Card>;
@@ -21,3 +21,11 @@ export default interface CardAction {
21
21
  */
22
22
  callback: ActionCallback;
23
23
  }
24
+ /**
25
+ * 操作栏图标的排布方式:
26
+ * - distribute(默认):按图标数量平分整行宽度,每个图标在自己的等分格子里居中
27
+ * (等价于 justify-content: space-around;数量少时不会被拉到两端)
28
+ * - center / left / right:固定间距(见 CommonCardActionBar 的 gap),整组图标
29
+ * 在操作栏里居中 / 左对齐 / 右对齐
30
+ */
31
+ export type CardActionsAlign = 'distribute' | 'center' | 'left' | 'right';
@@ -6,4 +6,15 @@
6
6
  let {title}: Props = $props();
7
7
  </script>
8
8
 
9
- <span>{title}</span>
9
+ <span class="card-header-title" title={title}>{title}</span>
10
+
11
+ <style>
12
+ .card-header-title {
13
+ display: block;
14
+ flex: 1 1 auto;
15
+ min-width: 0;
16
+ overflow: hidden;
17
+ text-overflow: ellipsis;
18
+ white-space: nowrap;
19
+ }
20
+ </style>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type CardAction from "./CardAction";
3
+ import type {CardActionsAlign} from "./CardAction";
3
4
  import utils from "../../overlay/common/utils";
4
5
 
5
6
  interface Props {
@@ -7,15 +8,23 @@
7
8
  color?: string | null;
8
9
  data?: any;
9
10
  theme?: string;
11
+ /** 图标排布方式,默认 'distribute'(按数量平分宽度、每个居中) */
12
+ align?: CardActionsAlign;
13
+ /** 'center'/'left'/'right' 模式下相邻图标的固定间距;不设置则用 --uniface-spacing-actionbar */
14
+ gap?: string;
10
15
  }
11
16
 
12
17
  let {
13
18
  actions,
14
19
  color = null,
15
20
  data,
16
- theme = ""
21
+ theme = "",
22
+ align = 'distribute',
23
+ gap
17
24
  }: Props = $props();
18
25
 
26
+ let barStyle = $derived(gap ? `--card-action-gap: ${gap}` : '');
27
+
19
28
  let clickEnable = $state(true);
20
29
 
21
30
  const handleActionClick = (action: CardAction) => async (e: MouseEvent) => {
@@ -35,7 +44,7 @@
35
44
  };
36
45
  </script>
37
46
 
38
- <div class="card-action-bar">
47
+ <div class="card-action-bar align-{align}" style={barStyle}>
39
48
  {#each actions as action}
40
49
  {#if action.icon}
41
50
  <i class="action-icon {action.icon}"
@@ -69,13 +78,32 @@
69
78
  flex-direction: row;
70
79
  width: 100%;
71
80
  align-items: center;
72
- justify-content: space-between;
73
81
  text-align: center;
74
82
  font-size: var(--uniface-card-bar-font-size);
75
83
  border-top: 1px solid var(--uniface-card-color-inside-border);
76
84
  color: var(--uniface-card-color-action-bar);
77
85
  }
78
86
 
87
+ /* 按数量平分整行宽度,每个图标在自己的等分格子里居中 — 数量少时不会被拉到两端 */
88
+ .card-action-bar.align-distribute {
89
+ justify-content: space-around;
90
+ }
91
+
92
+ .card-action-bar.align-center {
93
+ justify-content: center;
94
+ gap: var(--card-action-gap, var(--uniface-spacing-actionbar, 8px));
95
+ }
96
+
97
+ .card-action-bar.align-left {
98
+ justify-content: flex-start;
99
+ gap: var(--card-action-gap, var(--uniface-spacing-actionbar, 8px));
100
+ }
101
+
102
+ .card-action-bar.align-right {
103
+ justify-content: flex-end;
104
+ gap: var(--card-action-gap, var(--uniface-spacing-actionbar, 8px));
105
+ }
106
+
79
107
  .action-icon {
80
108
  display: block;
81
109
  cursor: pointer;
@@ -1,9 +1,14 @@
1
1
  import type CardAction from "./CardAction";
2
+ import type { CardActionsAlign } from "./CardAction";
2
3
  interface Props {
3
4
  actions: Array<CardAction>;
4
5
  color?: string | null;
5
6
  data?: any;
6
7
  theme?: string;
8
+ /** 图标排布方式,默认 'distribute'(按数量平分宽度、每个居中) */
9
+ align?: CardActionsAlign;
10
+ /** 'center'/'left'/'right' 模式下相邻图标的固定间距;不设置则用 --uniface-spacing-actionbar */
11
+ gap?: string;
7
12
  }
8
13
  declare const CommonCardActionBar: import("svelte").Component<Props, {}, "">;
9
14
  type CommonCardActionBar = ReturnType<typeof CommonCardActionBar>;
@@ -23,12 +23,15 @@ npm install @ticatec/uniface-element
23
23
 
24
24
  | Prop | Type | Default | Description |
25
25
  |------|------|---------|-------------|
26
- | `title` | `string` | `''` | Plain-text title rendered by `CardHeader`. If empty and `headerBar` is provided, the custom snippet is used instead |
26
+ | `title` | `string` | `''` | Plain-text title rendered by `CardHeader`. Single-line, truncated with an ellipsis (native tooltip shows the full text) when it overflows. If empty and `headerBar` is provided, the custom snippet is used instead |
27
27
  | `headerBar` | `Snippet` | `undefined` | Custom header content (takes over when `title` is empty) |
28
28
  | `actions` | `CardAction[]` | `[]` | Action bar items rendered at the bottom. Empty array → no footer |
29
29
  | `data` | `any` | `null` | Passed as the argument to each action's `callback(data)` |
30
+ | `actionsAlign` | `'distribute' \| 'center' \| 'left' \| 'right'` | `'distribute'` | How action icons are laid out — see below |
31
+ | `actionsGap` | `string` | `undefined` | Fixed spacing between icons for `'center'`/`'left'`/`'right'` (e.g. `'16px'`); falls back to `--uniface-spacing-actionbar` |
30
32
  | `variant` | `'plain' \| '3d'` | `'plain'` | `'3d'` adds a box-shadow to lift the card |
31
33
  | `style` | `string` | `''` | Inline CSS on the root |
34
+ | `contentStyle` | `string` | `''` | Inline CSS on the body wrapper (`.card-content`) |
32
35
  | `theme` | `string` | `''` | Theme value, exposed as `data-theme` on the root for scoped CSS |
33
36
  | `children` | `Snippet` | `undefined` | Card body |
34
37
 
@@ -92,6 +95,19 @@ interface CardAction {
92
95
  </Card>
93
96
  ```
94
97
 
98
+ ### Action bar layout
99
+
100
+ With few actions, `justify-content: space-between` (the old, fixed behavior) pushes them to opposite edges — two icons end up one at the far left, one at the far right. `actionsAlign` controls this:
101
+
102
+ - `'distribute'` (default) — the row is split into as many equal-width slots as there are actions, each icon centered in its own slot. Equivalent to `justify-content: space-around`; icons never get pushed all the way to the edges regardless of count.
103
+ - `'center'` / `'left'` / `'right'` — icons keep a fixed gap (`actionsGap`, default `--uniface-spacing-actionbar`) and the whole group is aligned as a block within the footer.
104
+
105
+ ```svelte
106
+ <Card title="Item Details" {actions} data={cardData} actionsAlign="center" actionsGap="24px">
107
+ ...
108
+ </Card>
109
+ ```
110
+
95
111
  ## Styling
96
112
 
97
113
  ### Card body
@@ -118,6 +134,7 @@ interface CardAction {
118
134
  | `--uniface-card-color-action-bar-hover` | Icon hover color |
119
135
  | `--uniface-card-color-action-bar-disabled` | Icon disabled color |
120
136
  | `--action-image-size` / `--uniface-action-image-size` | Per-card override for action icon/image size |
137
+ | `--uniface-spacing-actionbar` | Default `actionsGap` for `'center'`/`'left'`/`'right'` (falls back to `8px`) |
121
138
 
122
139
  ## Variants
123
140
 
@@ -23,12 +23,15 @@ npm install @ticatec/uniface-element
23
23
 
24
24
  | 属性 | 类型 | 默认值 | 描述 |
25
25
  |------|------|--------|------|
26
- | `title` | `string` | `''` | 纯文本标题,由 `CardHeader` 渲染。为空且传了 `headerBar` 时改用自定义 snippet |
26
+ | `title` | `string` | `''` | 纯文本标题,由 `CardHeader` 渲染。默认单行显示,超出宽度会用省略号截断(原生 tooltip 显示完整文字)。为空且传了 `headerBar` 时改用自定义 snippet |
27
27
  | `headerBar` | `Snippet` | `undefined` | 自定义标题内容(`title` 为空时生效) |
28
28
  | `actions` | `CardAction[]` | `[]` | 底部操作栏。空数组 → 不渲染底栏 |
29
29
  | `data` | `any` | `null` | 作为参数传给每个 action 的 `callback(data)` |
30
+ | `actionsAlign` | `'distribute' \| 'center' \| 'left' \| 'right'` | `'distribute'` | 操作图标的排布方式,见下文 |
31
+ | `actionsGap` | `string` | `undefined` | `'center'`/`'left'`/`'right'` 模式下图标之间的固定间距(比如 `'16px'`),不设则回退到 `--uniface-spacing-actionbar` |
30
32
  | `variant` | `'plain' \| '3d'` | `'plain'` | `'3d'` 加盒阴影让卡片浮起来 |
31
33
  | `style` | `string` | `''` | 根节点的内联 CSS |
34
+ | `contentStyle` | `string` | `''` | 内容区域包裹层(`.card-content`)的内联 CSS |
32
35
  | `theme` | `string` | `''` | 主题值,作为 `data-theme` 写到根节点上方便做作用域 CSS |
33
36
  | `children` | `Snippet` | `undefined` | 卡片内容体 |
34
37
 
@@ -92,6 +95,19 @@ interface CardAction {
92
95
  </Card>
93
96
  ```
94
97
 
98
+ ### 操作栏排布方式
99
+
100
+ 当图标数量比较少时,`justify-content: space-between`(旧的固定行为)会把它们推到两端 —— 两个图标就变成一个在最左,一个在最右。`actionsAlign` 用来控制这个行为:
101
+
102
+ - `'distribute'`(默认)—— 把整行按图标数量平分成若干等宽格子,每个图标在自己的格子里居中。等价于 `justify-content: space-around`;不管图标数量多少,都不会被拉到两端。
103
+ - `'center'` / `'left'` / `'right'` —— 图标之间保持固定间距(`actionsGap`,默认回退到 `--uniface-spacing-actionbar`),整组图标作为一个整体在底栏里居中 / 左对齐 / 右对齐。
104
+
105
+ ```svelte
106
+ <Card title="项目详情" {actions} data={cardData} actionsAlign="center" actionsGap="24px">
107
+ ...
108
+ </Card>
109
+ ```
110
+
95
111
  ## 样式定制
96
112
 
97
113
  ### 卡片本体
@@ -118,6 +134,7 @@ interface CardAction {
118
134
  | `--uniface-card-color-action-bar-hover` | 图标悬停颜色 |
119
135
  | `--uniface-card-color-action-bar-disabled` | 图标禁用颜色 |
120
136
  | `--action-image-size` / `--uniface-action-image-size` | 单卡片覆盖操作图标/图片的尺寸 |
137
+ | `--uniface-spacing-actionbar` | `'center'`/`'left'`/`'right'` 模式下 `actionsGap` 的默认值(缺省回退到 `8px`) |
121
138
 
122
139
  ## variant
123
140
 
@@ -50,6 +50,23 @@
50
50
  if (disabled) return;
51
51
  focus();
52
52
  }
53
+
54
+ // Only intercept mousedowns that land on the wrapper's own decorative chrome
55
+ // (padding, prefix/suffix, icons) — preventing their default avoids a
56
+ // blur-then-refocus flicker when clicking near the control. A mousedown that
57
+ // originates on the actual form control (input/textarea, including the one
58
+ // rendered by NumberInput.svelte) must NOT be intercepted: preventDefault()
59
+ // there cancels the browser's native text-selection-start behavior for that
60
+ // same event, even though this listener runs on an ancestor during bubbling —
61
+ // that's what made it impossible to drag-select or double-click-select text
62
+ // inside every CommonEditor-based input (SearchBox, TextEditor, PasswordEditor,
63
+ // NumberEditor, TimeEditor, and anything built on CommonPopupEditor).
64
+ const handleContainerMouseDown = (e: MouseEvent) => {
65
+ const tag = (e.target as HTMLElement).tagName;
66
+ if (tag !== 'INPUT' && tag !== 'TEXTAREA') {
67
+ e.preventDefault();
68
+ }
69
+ };
53
70
  </script>
54
71
 
55
72
  <FormEditor {theme} {tooltip} {error} {style} {readonly} {disabled} {compact} {variant}>
@@ -61,7 +78,7 @@
61
78
  <!-- svelte-ignore a11y_click_events_have_key_events -->
62
79
  <!-- svelte-ignore a11y_no_static_element_interactions -->
63
80
  <div class="uniface-common-editor" bind:this={container} {style}
64
- onmousedown={(e: MouseEvent)=>{e.preventDefault()}} onclick={handleContainerClick}>
81
+ onmousedown={handleContainerMouseDown} onclick={handleContainerClick}>
65
82
  {#if leadingIcon}
66
83
  <div class="editor-embed-icon" style="flex: 0 0 auto;">
67
84
  {@render leadingIcon()}
@@ -18,6 +18,7 @@
18
18
 
19
19
  let {
20
20
  variant,
21
+ theme,
21
22
  compact = false,
22
23
  disabled = false,
23
24
  readonly = false,
@@ -68,6 +69,7 @@
68
69
  <CommonEditor
69
70
  bind:this={editorRef}
70
71
  {variant}
72
+ {theme}
71
73
  {compact}
72
74
  {style}
73
75
  {tooltip}
@@ -88,7 +90,7 @@
88
90
  {/snippet}
89
91
 
90
92
  {#if showPopup && getTargetElement()}
91
- <Popover target={getTargetElement()} {autoFit} align="right" onclose={closePopup}>
93
+ <Popover target={getTargetElement()} {autoFit} {theme} align="right" onclose={closePopup}>
92
94
  {#if popupLayer}
93
95
  {@render popupLayer()}
94
96
  {/if}
@@ -31,6 +31,7 @@
31
31
  tooltip,
32
32
  error,
33
33
  variant,
34
+ theme,
34
35
  compact = false,
35
36
  value = $bindable(),
36
37
  precision,
@@ -76,6 +77,7 @@
76
77
  {readonly}
77
78
  {disabled}
78
79
  {variant}
80
+ {theme}
79
81
  {compact}
80
82
  {leadingIcon}
81
83
  {trailingIcon}
@@ -17,6 +17,7 @@
17
17
 
18
18
  let {
19
19
  variant,
20
+ theme,
20
21
  compact = false,
21
22
  disabled = false,
22
23
  readonly = false,
@@ -69,7 +70,7 @@
69
70
  </script>
70
71
 
71
72
 
72
- <CommonPopupEditor bind:this={editorRef} {variant} {compact} {style} {tooltip} {disabled} {readonly} {prefix} autoFit
73
+ <CommonPopupEditor bind:this={editorRef} {variant} {theme} {compact} {style} {tooltip} {disabled} {readonly} {prefix} autoFit
73
74
  {suffix} {clear} textValue={value}>
74
75
  <input bind:this={editor} bind:value {disabled} {readonly} {placeholder} {...eventHandlers}/>
75
76
 
@@ -13,6 +13,7 @@
13
13
 
14
14
  let {
15
15
  variant,
16
+ theme,
16
17
  tooltip,
17
18
  compact = false,
18
19
  style,
@@ -71,7 +72,7 @@
71
72
  };
72
73
  </script>
73
74
 
74
- <CommonEditor {variant} {compact} {tooltip} {style} {disabled} input$class='search-box'>
75
+ <CommonEditor {variant} {theme} {compact} {tooltip} {style} {disabled} input$class='search-box'>
75
76
  {#snippet leadingIcon()}
76
77
  <div style="line-height: 16px; font-size: 18px">
77
78
  <i class="icon_google_search"></i>
@@ -18,6 +18,7 @@
18
18
  disabled = false,
19
19
  readonly = false,
20
20
  variant,
21
+ theme,
21
22
  compact = false,
22
23
  style,
23
24
  value = $bindable(),
@@ -56,7 +57,7 @@
56
57
 
57
58
  </script>
58
59
 
59
- <CommonEditor textValue={value} {clear} {disabled} {readonly} {variant} {compact} {style} {tooltip} {error}>
60
+ <CommonEditor textValue={value} {clear} {disabled} {readonly} {variant} {theme} {compact} {style} {tooltip} {error}>
60
61
  <input bind:this={editor} bind:value type={showPassword ? 'text' : 'password'} {...input$} onchange={handleChangeEvent} {...eventHandlers()} />
61
62
  {#snippet trailingIcon()}
62
63
  <div class="password-icon">
@@ -26,6 +26,7 @@
26
26
  disabled = false,
27
27
  readonly = false,
28
28
  variant,
29
+ theme,
29
30
  compact = false,
30
31
  style,
31
32
  value = $bindable(),
@@ -79,7 +80,7 @@
79
80
  </script>
80
81
 
81
82
 
82
- <CommonEditor textValue={value} {clear} {suffix} {prefix} {readonly} {disabled} {variant} {compact} {leadingIcon} {trailingIcon} {tooltip}
83
+ <CommonEditor textValue={value} {clear} {suffix} {prefix} {readonly} {disabled} {variant} {theme} {compact} {leadingIcon} {trailingIcon} {tooltip}
83
84
  {error}>
84
85
  <input bind:this={editor} {...input$} {placeholder} {...eventHandlers}
85
86
  oninput={handleInput} onchange={handleChange} bind:value/>
@@ -22,6 +22,7 @@
22
22
  mandatory = false,
23
23
  precision = 'M',
24
24
  variant,
25
+ theme,
25
26
  compact = false,
26
27
  style,
27
28
  value = $bindable<number | undefined>(),
@@ -202,6 +203,7 @@
202
203
  {disabled}
203
204
  {readonly}
204
205
  {variant}
206
+ {theme}
205
207
  {compact}
206
208
  {leadingIcon}
207
209
  {trailingIcon}
@@ -9,6 +9,7 @@
9
9
 
10
10
  interface Props {
11
11
  variant?: EditorVariant;
12
+ theme?: string;
12
13
  compact?: boolean;
13
14
  style?: string;
14
15
  tooltip?: string;
@@ -30,6 +31,7 @@
30
31
 
31
32
  let {
32
33
  variant,
34
+ theme,
33
35
  compact = false,
34
36
  style,
35
37
  tooltip,
@@ -161,6 +163,7 @@
161
163
 
162
164
  <CommonPopupEditor bind:this={editorRef}
163
165
  {variant}
166
+ {theme}
164
167
  {error}
165
168
  {compact}
166
169
  {style}
@@ -3,6 +3,7 @@ import type { EditorVariant } from "../types";
3
3
  import type UnitOption from "./UnitOption";
4
4
  interface Props {
5
5
  variant?: EditorVariant;
6
+ theme?: string;
6
7
  compact?: boolean;
7
8
  style?: string;
8
9
  tooltip?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticatec/uniface-element",
3
- "version": "5.0.2",
3
+ "version": "5.0.4",
4
4
  "description": "A comprehensive UI component library for Svelte applications with rich form controls, data tables, layouts and interactive elements",
5
5
  "keywords": [
6
6
  "svelte",