@h-ai/ui 0.1.0-alpha.45 → 0.1.0-alpha.46

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/README.md CHANGED
@@ -172,7 +172,7 @@ components/
172
172
  | 组件 | 描述 | 主要属性 |
173
173
  | --------- | ------ | ------------------------------------------------------- |
174
174
  | `Modal` | 模态框 | `open`, `title`, `size`, `radius`, `bodyOverflow`, `showClose` |
175
- | `Drawer` | 抽屉 | `open`, `position`, `title`, `size` |
175
+ | `Drawer` | 抽屉 | `open`, `position`, `title`, `size`, `width`, `resizable`, `widthStorageKey` |
176
176
  | `Confirm` | 确认框 | `open`, `title`, `message`, `variant`, `onconfirm` |
177
177
  | `Popover` | 弹出层 | `open`, `position`, `trigger`, `offset` |
178
178
 
@@ -334,13 +334,13 @@ components/
334
334
 
335
335
  | 组件 | 描述 | 主要属性 / 能力 |
336
336
  | -------------------- | ------------ | ---------------- |
337
- | `CrudPage` | CRUD 主页面 | `crud`, `data`, `permissions`, `form`, `pagination`, `density`, `showHeader`, `toolbarStyle`, `toolbarLeading`, `toolbarActions`, `card`, `tableCell`, `onrowclick`, `sortableColumns`, `nav` |
337
+ | `CrudPage` | CRUD 主页面 | `crud`, `data`, `permissions`, `form`, `pagination`, `density`, `detailColumns`, `showHeader`, `toolbarStyle`, `toolbarLeading`, `toolbarActions`, `card`, `tableCell`, `onrowclick`, `sortableColumns`, `nav` |
338
338
  | `CrudFilterBar` | 过滤工具栏(搜索 + 多类型过滤 + 重置) | `filterFields`, `searchValue`, `onsearch`, `onfilterchange`, `onreset` |
339
339
  | `CrudDetailPanel` | 详情面板(抽屉/弹窗) | `open`, `item`, `fields`, `variant`, `drawerWidth`, `modalSize` |
340
340
  | `CrudEditPanel` | 编辑面板(抽屉/弹窗) | `open`, `fields`, `variant`, `drawerWidth`, `modalSize`, `onsubmit` |
341
341
  | `CrudDeleteConfirm` | 删除确认框 | `open`, `loading`, `onconfirm` |
342
342
 
343
- > `CrudPage` 默认使用 `TableToolbar` 图标弹层工具栏,也可通过 `toolbarStyle='filter-bar'` 切回平铺筛选栏;`toolbarLeading` 用于注入左侧标题或说明,配合 `showHeader={false}` 可避免重复标题;`toolbarActions` 注入右侧业务操作,`card` 插槽可将列表项替换为卡片视图,`tableCell` 可按行和列 key 渲染交互式单元格内容(例如 Tooltip),`sortableColumns` 控制服务端排序字段。过滤工具栏支持 `select`、`boolean`、`number`、`date`、`date-range`、`text`。
343
+ > `CrudPage` 默认使用 `TableToolbar` 图标弹层工具栏,也可通过 `toolbarStyle='filter-bar'` 切回平铺筛选栏;`detailColumns={['title']}` 可把标题等指定列绑定为详情入口;`toolbarLeading` 用于注入左侧标题或说明,配合 `showHeader={false}` 可避免重复标题;`toolbarActions` 注入右侧业务操作,`card` 插槽可将列表项替换为卡片视图,`tableCell` 可按行和列 key 渲染交互式单元格内容(例如 Tooltip),`sortableColumns` 控制服务端排序字段。过滤工具栏支持 `select`、`boolean`、`number`、`date`、`date-range`、`text`。
344
344
  >
345
345
  > 页面根容器会占满父级可用高度。表格无论是否有数据都会撑满剩余空间,仅数据区域滚动;表头固定在数据区顶部,分页栏固定在列表卡片底部。
346
346
  >
@@ -355,7 +355,7 @@ components/
355
355
  > `CrudPage` 通过 `form` 配置新建/编辑的展示形式:
356
356
  >
357
357
  > - `form.variant`:`'drawer'`(抽屉,默认)或 `'modal'`(弹出窗口)。
358
- > - 抽屉:`form.drawerSize` 选预设尺寸,`form.drawerWidth` 传任意 CSS 宽度(优先级更高)。
358
+ > - 抽屉:`form.drawerSize` 选预设尺寸,`form.drawerWidth` 传任意 CSS 宽度(优先级更高);默认可拖动左侧边缘调整宽度,并按资源记忆,`form.drawerResizable=false` 可关闭。
359
359
  > - 弹窗:`form.modalSize` 选预设尺寸,`form.modalWidth` / `form.modalHeight` 传任意 CSS 尺寸。
360
360
  >
361
361
  > `pagination` 配置分页栏(始终显示):`showSizeChanger`、`pageSizeOptions`、`showJumper`、`showTotal` 默认开启;紧凑视图可用 `showPageInfo` 关闭页码文案、`showFirstLast` 关闭首页 / 末页按钮。
@@ -488,12 +488,13 @@ components/
488
488
  form={{ variant: 'modal', modalSize: 'lg' }}
489
489
  pagination={{ showSizeChanger: true, showJumper: true, pageSizeOptions: [10, 20, 50] }}
490
490
  density='compact'
491
+ detailColumns={['name']}
491
492
  sortableColumns={[{ key: 'createdAt', label: '创建时间' }]}
492
493
  {nav}
493
494
  />
494
495
  ```
495
496
 
496
- > 抽屉形式可改用 `form={{ variant: 'drawer', drawerWidth: '40rem' }}`。
497
+ > 抽屉形式可改用 `form={{ variant: 'drawer', drawerWidth: '40rem' }}`;用户拖动后的宽度会覆盖初始宽度并按资源记忆。
497
498
 
498
499
  ### 登录页面
499
500
 
@@ -8,6 +8,7 @@
8
8
  @prop {Snippet<[T, string]>} cell - 单元格内容插槽
9
9
  @prop {Snippet<[T]>} actions - 操作按钮插槽
10
10
  @prop {(item: T) => void} onrowclick - 行点击回调
11
+ @prop {(item: T) => void} columns[].onclick - 指定列的单元格点击回调
11
12
  @prop {Snippet} empty - 空状态插槽
12
13
  @prop {boolean} loading - 是否加载中
13
14
  @prop {boolean} striped - 是否斑马纹
@@ -54,7 +55,7 @@
54
55
  ...restProps
55
56
  }: {
56
57
  data: T[]
57
- columns: { key: keyof T | string, label: string, width?: string, align?: 'left' | 'center' | 'right', sortable?: boolean, render?: (item: T) => string }[]
58
+ columns: { key: keyof T | string, label: string, width?: string, align?: 'left' | 'center' | 'right', sortable?: boolean, render?: (item: T) => string, onclick?: (item: T) => void }[]
58
59
  keyField: keyof T
59
60
  /** 自定义单元格内容;参数依次为行记录和列 key。 */
60
61
  cell?: Snippet<[T, string]>
@@ -91,6 +92,8 @@
91
92
  sortable?: boolean
92
93
  /** 自定义单元格格式化函数。 */
93
94
  render?: (item: T) => string
95
+ /** 点击当前列单元格时的回调。 */
96
+ onclick?: (item: T) => void
94
97
  }
95
98
 
96
99
  // ─── 排序状态 ───
@@ -341,7 +344,23 @@
341
344
  <tr class='{rowClass} {onrowclick ? 'cursor-pointer' : ''}' onclick={() => onrowclick?.(item)}>
342
345
  {#each columns as col (String(col.key))}
343
346
  <td style={getColumnStyle(col)} class={getAlignClass(col.align)}>
344
- {#if cell}
347
+ {#if col.onclick}
348
+ <button
349
+ type='button'
350
+ class='block min-w-0 max-w-full cursor-pointer rounded-sm text-inherit hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
351
+ style={getContentStyle(col)}
352
+ onclick={(event) => {
353
+ event.stopPropagation()
354
+ col.onclick?.(item)
355
+ }}
356
+ >
357
+ {#if cell}
358
+ {@render cell(item, String(col.key))}
359
+ {:else}
360
+ <span class='block min-w-0 truncate' title={getValue(item, col)}>{getValue(item, col)}</span>
361
+ {/if}
362
+ </button>
363
+ {:else if cell}
345
364
  <div class='min-w-0' style={getContentStyle(col)}>
346
365
  {@render cell(item, String(col.key))}
347
366
  </div>
@@ -10,6 +10,7 @@ declare function $$render<T>(): {
10
10
  align?: "left" | "center" | "right";
11
11
  sortable?: boolean;
12
12
  render?: (item: T) => string;
13
+ onclick?: (item: T) => void;
13
14
  }[];
14
15
  keyField: keyof T;
15
16
  /** 自定义单元格内容;参数依次为行记录和列 key。 */
@@ -59,6 +60,7 @@ interface $$IsomorphicComponent {
59
60
  * @prop {Snippet<[T, string]>} cell - 单元格内容插槽
60
61
  * @prop {Snippet<[T]>} actions - 操作按钮插槽
61
62
  * @prop {(item: T) => void} onrowclick - 行点击回调
63
+ * @prop {(item: T) => void} columns[].onclick - 指定列的单元格点击回调
62
64
  * @prop {Snippet} empty - 空状态插槽
63
65
  * @prop {boolean} loading - 是否加载中
64
66
  * @prop {boolean} striped - 是否斑马纹
@@ -10,6 +10,8 @@
10
10
  -->
11
11
  <script lang='ts'>
12
12
  import type { DataAttributes, DrawerProps } from '../../types.js'
13
+ import { onDestroy, onMount } from 'svelte'
14
+ import { readStoredValue, writeStoredValue } from '../../internal/browser-safety.js'
13
15
  import { uiM } from '../../messages.js'
14
16
  import { cn, generateId, getDataAttributes } from '../../utils.js'
15
17
  import IconButton from '../primitives/IconButton.svelte'
@@ -21,6 +23,8 @@
21
23
  position = 'right',
22
24
  size = 'md',
23
25
  width,
26
+ resizable = false,
27
+ widthStorageKey,
24
28
  closeOnBackdrop = true,
25
29
  showClose = true,
26
30
  class: className = '',
@@ -31,6 +35,16 @@
31
35
 
32
36
  const dataAttributes = $derived(getDataAttributes(restProps))
33
37
  const id = generateId('drawer')
38
+ /** 用户拖动后的像素宽度;优先于 width 与 size。 */
39
+ let resizedWidth = $state<number | null>(null)
40
+ /** 与响应式渲染解耦的同步宽度,确保 mouseup 时持久化最后一次 mousemove 的值。 */
41
+ let resizeWidthForStorage: number | null = null
42
+ /** 当前拖动起点的指针横坐标。 */
43
+ let resizeStartX = 0
44
+ /** 当前拖动起点的抽屉宽度。 */
45
+ let resizeStartWidth = 0
46
+ /** 拖动期间临时保存页面原有的文本选择样式。 */
47
+ let previousUserSelect = ''
34
48
 
35
49
  const sizeMap = {
36
50
  'xs': 'w-60',
@@ -45,7 +59,15 @@
45
59
 
46
60
  // width 优先:传入自定义 CSS 宽度时通过 inline style 覆盖预设宽度类
47
61
  const hasCustomWidth = $derived(typeof width === 'string' && width.trim().length > 0)
48
- const widthStyle = $derived(hasCustomWidth ? `width: ${width!.trim()}; max-width: 100vw` : undefined)
62
+ const widthStyle = $derived(
63
+ resizedWidth !== null
64
+ ? `width: ${resizedWidth}px; max-width: calc(100vw - 1rem)`
65
+ : hasCustomWidth
66
+ ? `width: ${width!.trim()}; max-width: 100vw`
67
+ : undefined,
68
+ )
69
+ /** 纵向抽屉没有横向宽度拖动语义。 */
70
+ const canResize = $derived(resizable && (position === 'left' || position === 'right'))
49
71
 
50
72
  const drawerClass = $derived(
51
73
  cn(
@@ -62,7 +84,7 @@
62
84
 
63
85
  const contentClass = $derived(
64
86
  cn(
65
- 'menu bg-base-200 text-base-content min-h-full p-4',
87
+ 'menu relative z-10 bg-base-200 text-base-content min-h-full p-4',
66
88
  !hasCustomWidth && sizeMap[size],
67
89
  className,
68
90
  ),
@@ -78,6 +100,92 @@
78
100
  handleClose()
79
101
  }
80
102
  }
103
+
104
+ /** 将宽度限制在可视区域内,并保留移动端可关闭抽屉的边距。 */
105
+ function clampWidth(nextWidth: number): number {
106
+ const maximum = Math.max(0, globalThis.innerWidth - 16)
107
+ const minimum = Math.min(280, maximum)
108
+ return Math.round(Math.min(Math.max(nextWidth, minimum), maximum))
109
+ }
110
+
111
+ /** 根据抽屉方向计算拖动宽度。 */
112
+ function handleResizeMove(event: MouseEvent) {
113
+ const delta = position === 'right'
114
+ ? resizeStartX - event.clientX
115
+ : event.clientX - resizeStartX
116
+ const nextWidth = clampWidth(resizeStartWidth + delta)
117
+ resizeWidthForStorage = nextWidth
118
+ resizedWidth = nextWidth
119
+ }
120
+
121
+ /** 结束拖动并在配置了 key 时记忆最终宽度。 */
122
+ function finishResize() {
123
+ if (typeof document === 'undefined')
124
+ return
125
+
126
+ globalThis.removeEventListener('mousemove', handleResizeMove)
127
+ globalThis.removeEventListener('mouseup', finishResize)
128
+ document.documentElement.style.userSelect = previousUserSelect
129
+ if (widthStorageKey && resizeWidthForStorage !== null) {
130
+ writeStoredValue(widthStorageKey, String(resizeWidthForStorage))
131
+ }
132
+ }
133
+
134
+ /** 从抽屉可见边缘开始宽度拖动。 */
135
+ function startResize(event: MouseEvent) {
136
+ if (!canResize)
137
+ return
138
+
139
+ const drawer = event.currentTarget instanceof HTMLElement
140
+ ? event.currentTarget.parentElement
141
+ : null
142
+ if (!drawer)
143
+ return
144
+
145
+ event.preventDefault()
146
+ resizeStartX = event.clientX
147
+ resizeStartWidth = drawer.getBoundingClientRect().width
148
+ resizeWidthForStorage = resizeStartWidth
149
+ previousUserSelect = document.documentElement.style.userSelect
150
+ document.documentElement.style.userSelect = 'none'
151
+ globalThis.addEventListener('mousemove', handleResizeMove)
152
+ globalThis.addEventListener('mouseup', finishResize, { once: true })
153
+ }
154
+
155
+ /** 键盘左右键以固定步长调整宽度,并立即持久化。 */
156
+ function handleResizeKeydown(event: KeyboardEvent) {
157
+ if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')
158
+ return
159
+
160
+ const drawer = event.currentTarget instanceof HTMLElement
161
+ ? event.currentTarget.parentElement
162
+ : null
163
+ if (!drawer)
164
+ return
165
+
166
+ event.preventDefault()
167
+ const edgeDelta = event.key === 'ArrowLeft' ? -16 : 16
168
+ const nextWidth = clampWidth(drawer.getBoundingClientRect().width + (position === 'right' ? -edgeDelta : edgeDelta))
169
+ resizeWidthForStorage = nextWidth
170
+ resizedWidth = nextWidth
171
+ if (widthStorageKey) {
172
+ writeStoredValue(widthStorageKey, String(nextWidth))
173
+ }
174
+ }
175
+
176
+ onMount(() => {
177
+ if (!canResize || !widthStorageKey)
178
+ return
179
+
180
+ const storedWidth = Number(readStoredValue(widthStorageKey))
181
+ if (Number.isFinite(storedWidth) && storedWidth > 0) {
182
+ const nextWidth = clampWidth(storedWidth)
183
+ resizeWidthForStorage = nextWidth
184
+ resizedWidth = nextWidth
185
+ }
186
+ })
187
+
188
+ onDestroy(finishResize)
81
189
  </script>
82
190
 
83
191
  <div {...dataAttributes} class={drawerClass}>
@@ -94,6 +202,17 @@
94
202
  ></div>
95
203
 
96
204
  <div class={contentClass} style={widthStyle}>
205
+ {#if canResize}
206
+ <button
207
+ type='button'
208
+ aria-label={uiM('drawer_resize')}
209
+ data-drawer-resize-handle
210
+ class='absolute inset-y-0 z-20 hidden w-2 cursor-col-resize touch-none border-0 bg-transparent p-0 md:block {position === 'right' ? 'left-0' : 'right-0'}'
211
+ onmousedown={startResize}
212
+ onkeydown={handleResizeKeydown}
213
+ ></button>
214
+ {/if}
215
+
97
216
  <div class='flex items-center justify-between mb-4'>
98
217
  {#if title}
99
218
  <h3 class='text-lg font-bold'>{title}</h3>
@@ -35,6 +35,8 @@
35
35
  variant = 'drawer' as CrudFormVariant,
36
36
  size = '2xl' as Size,
37
37
  drawerWidth,
38
+ drawerResizable = true,
39
+ drawerWidthStorageKey,
38
40
  modalSize = '2xl' as Size | 'full',
39
41
  modalWidth,
40
42
  modalHeight,
@@ -53,6 +55,8 @@
53
55
  variant?: CrudFormVariant
54
56
  size?: Size
55
57
  drawerWidth?: string
58
+ drawerResizable?: boolean
59
+ drawerWidthStorageKey?: string
56
60
  modalSize?: Size | 'full'
57
61
  modalWidth?: string
58
62
  modalHeight?: string
@@ -195,7 +199,17 @@
195
199
  {/snippet}
196
200
  </Modal>
197
201
  {:else}
198
- <Drawer bind:open {title} class={className} position='right' {size} width={drawerWidth} onclose={handleClose}>
202
+ <Drawer
203
+ bind:open
204
+ {title}
205
+ class={className}
206
+ position='right'
207
+ {size}
208
+ width={drawerWidth}
209
+ resizable={drawerResizable}
210
+ widthStorageKey={drawerWidthStorageKey}
211
+ onclose={handleClose}
212
+ >
199
213
  {#if item}
200
214
  <div class={drawerBodyClass}>
201
215
  {@render detailBody()}
@@ -25,6 +25,8 @@ type $$ComponentProps = {
25
25
  variant?: CrudFormVariant;
26
26
  size?: Size;
27
27
  drawerWidth?: string;
28
+ drawerResizable?: boolean;
29
+ drawerWidthStorageKey?: string;
28
30
  modalSize?: Size | 'full';
29
31
  modalWidth?: string;
30
32
  modalHeight?: string;
@@ -41,6 +41,8 @@
41
41
  variant = 'drawer' as CrudFormVariant,
42
42
  size = '2xl' as Size,
43
43
  drawerWidth,
44
+ drawerResizable = true,
45
+ drawerWidthStorageKey,
44
46
  modalSize = '2xl' as Size | 'full',
45
47
  modalWidth,
46
48
  modalHeight,
@@ -62,6 +64,8 @@
62
64
  variant?: CrudFormVariant
63
65
  size?: Size
64
66
  drawerWidth?: string
67
+ drawerResizable?: boolean
68
+ drawerWidthStorageKey?: string
65
69
  modalSize?: Size | 'full'
66
70
  modalWidth?: string
67
71
  modalHeight?: string
@@ -273,7 +277,18 @@
273
277
  {/snippet}
274
278
  </Modal>
275
279
  {:else}
276
- <Drawer bind:open {title} class={className} position='right' {size} width={drawerWidth} onclose={handleClose} closeOnBackdrop={false}>
280
+ <Drawer
281
+ bind:open
282
+ {title}
283
+ class={className}
284
+ position='right'
285
+ {size}
286
+ width={drawerWidth}
287
+ resizable={drawerResizable}
288
+ widthStorageKey={drawerWidthStorageKey}
289
+ onclose={handleClose}
290
+ closeOnBackdrop={false}
291
+ >
277
292
  <form onsubmit={handleSubmit} class={drawerFormClass}>
278
293
  {@render formFields()}
279
294
  </form>
@@ -33,6 +33,8 @@ type $$ComponentProps = {
33
33
  variant?: CrudFormVariant;
34
34
  size?: Size;
35
35
  drawerWidth?: string;
36
+ drawerResizable?: boolean;
37
+ drawerWidthStorageKey?: string;
36
38
  modalSize?: Size | 'full';
37
39
  modalWidth?: string;
38
40
  modalHeight?: string;
@@ -140,6 +140,7 @@
140
140
  pagination = {},
141
141
  density = 'normal',
142
142
  rowClickDetail = true,
143
+ detailColumns = [],
143
144
  showHeader = true,
144
145
  listItemActions,
145
146
  tableCell,
@@ -175,6 +176,8 @@
175
176
  density?: CrudDensity
176
177
  /** 点击详情图标是否打开详情面板。 */
177
178
  rowClickDetail?: boolean
179
+ /** 点击后打开详情的列 key。 */
180
+ detailColumns?: string[]
178
181
  /** 是否显示默认页面标题栏;使用 toolbarLeading 承载标题时可设为 false。 */
179
182
  showHeader?: boolean
180
183
  /** 每行额外操作按钮插槽。 */
@@ -265,6 +268,10 @@
265
268
  const formDrawerSize = $derived(form.drawerSize ?? '2xl')
266
269
  /** 抽屉自定义宽度。 */
267
270
  const formDrawerWidth = $derived(form.drawerWidth)
271
+ /** 是否允许拖动并记忆抽屉宽度。 */
272
+ const formDrawerResizable = $derived(form.drawerResizable !== false)
273
+ /** 同一资源的详情与编辑抽屉共享宽度记忆。 */
274
+ const formDrawerWidthStorageKey = $derived(`hai-ui:crud:${crud.resource.name}:drawer-width`)
268
275
  /** 弹窗尺寸预设。 */
269
276
  const formModalSize = $derived(form.modalSize ?? '2xl')
270
277
  /** 弹窗自定义宽度。 */
@@ -622,6 +629,9 @@
622
629
  render: col.render
623
630
  ? (item: Record<string, unknown>) => col.render!(item)
624
631
  : undefined,
632
+ onclick: detailColumns.includes(col.key)
633
+ ? (item: Record<string, unknown>) => openDetail(item)
634
+ : undefined,
625
635
  })),
626
636
  )
627
637
  </script>
@@ -795,6 +805,8 @@
795
805
  variant={formVariant}
796
806
  size={formDrawerSize}
797
807
  drawerWidth={formDrawerWidth}
808
+ drawerResizable={formDrawerResizable}
809
+ drawerWidthStorageKey={formDrawerWidthStorageKey}
798
810
  modalSize={formModalSize}
799
811
  modalWidth={formModalWidth}
800
812
  modalHeight={formModalHeight}
@@ -815,6 +827,8 @@
815
827
  variant={formVariant}
816
828
  size={formDrawerSize}
817
829
  drawerWidth={formDrawerWidth}
830
+ drawerResizable={formDrawerResizable}
831
+ drawerWidthStorageKey={formDrawerWidthStorageKey}
818
832
  modalSize={formModalSize}
819
833
  modalWidth={formModalWidth}
820
834
  modalHeight={formModalHeight}
@@ -142,6 +142,8 @@ type $$ComponentProps = {
142
142
  density?: CrudDensity;
143
143
  /** 点击详情图标是否打开详情面板。 */
144
144
  rowClickDetail?: boolean;
145
+ /** 点击后打开详情的列 key。 */
146
+ detailColumns?: string[];
145
147
  /** 是否显示默认页面标题栏;使用 toolbarLeading 承载标题时可设为 false。 */
146
148
  showHeader?: boolean;
147
149
  /** 每行额外操作按钮插槽。 */
@@ -27,6 +27,8 @@ export interface CrudFormConfig {
27
27
  drawerSize?: Size;
28
28
  /** 抽屉自定义宽度 CSS 值,优先级高于 drawerSize(variant='drawer') */
29
29
  drawerWidth?: string;
30
+ /** 是否允许拖动并按资源记忆抽屉宽度(variant='drawer',默认 true) */
31
+ drawerResizable?: boolean;
30
32
  /** 弹窗尺寸预设(variant='modal',默认 '2xl') */
31
33
  modalSize?: Size | 'full';
32
34
  /** 弹窗自定义宽度 CSS 值(variant='modal') */
@@ -156,6 +158,8 @@ export interface CrudPageProps<T = Record<string, unknown>> extends DataAttribut
156
158
  density?: CrudDensity;
157
159
  /** 列表行点击是否打开详情(默认 true) */
158
160
  rowClickDetail?: boolean;
161
+ /** 点击后打开详情的列 key;只让标题等主标识列可点击时使用 */
162
+ detailColumns?: string[];
159
163
  /** 是否显示默认页面标题栏;需要把标题放进工具栏左侧时设为 false。 */
160
164
  showHeader?: boolean;
161
165
  /** 列表行操作按钮插槽 */
@@ -37,6 +37,7 @@
37
37
  "common_close": "Close",
38
38
  "modal_close": "Close modal",
39
39
  "drawer_close": "Close sidebar",
40
+ "drawer_resize": "Resize drawer",
40
41
  "toast_dismiss": "Dismiss",
41
42
  "language_switch_label": "Switch language",
42
43
  "theme_selector_label": "Select theme",
@@ -37,6 +37,7 @@
37
37
  "common_close": "关闭",
38
38
  "modal_close": "关闭模态框",
39
39
  "drawer_close": "关闭侧边栏",
40
+ "drawer_resize": "调整抽屉宽度",
40
41
  "toast_dismiss": "关闭",
41
42
  "language_switch_label": "切换语言",
42
43
  "theme_selector_label": "选择主题",
package/dist/types.d.ts CHANGED
@@ -487,6 +487,10 @@ export interface DrawerProps extends DataAttributes {
487
487
  size?: Size;
488
488
  /** 自定义宽度(任意合法 CSS 宽度值,优先级高于 size 预设) */
489
489
  width?: string;
490
+ /** 是否允许拖动抽屉边缘调整宽度;仅 left/right 位置生效 */
491
+ resizable?: boolean;
492
+ /** 宽度持久化 key;设置后会从 localStorage 恢复并记忆拖动后的像素宽度 */
493
+ widthStorageKey?: string;
490
494
  /** 是否可通过点击遮罩关闭 */
491
495
  closeOnBackdrop?: boolean;
492
496
  /** 是否显示关闭按钮 */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/ui",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.45",
4
+ "version": "0.1.0-alpha.46",
5
5
  "description": "Hai Framework UI component library for Svelte 5 Runes.",
6
6
  "license": "Apache-2.0",
7
7
  "svelte": "./dist/index.js",
@@ -52,7 +52,7 @@
52
52
  "mermaid": "^11.12.0",
53
53
  "shiki": "^4.0.2",
54
54
  "tailwind-merge": "^3.5.0",
55
- "@h-ai/core": "0.1.0-alpha.45"
55
+ "@h-ai/core": "0.1.0-alpha.46"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@sveltejs/package": "^2.5.7",