@ticatec/uniface-element 5.0.4 → 5.0.5

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 (36) hide show
  1. package/README.md +10 -10
  2. package/README_CN.md +13 -12
  3. package/dist/base-calendar/DateContext.d.ts +1 -0
  4. package/dist/base-calendar/DateContext.js +5 -8
  5. package/dist/composite/pagination_panel/PaginationPanel.svelte +5 -2
  6. package/dist/composite/transfer/Transfer.svelte +0 -1
  7. package/dist/data_table/DataTable.svelte +31 -1
  8. package/dist/data_table/DataTable.svelte.d.ts +7 -0
  9. package/dist/data_table/lib/virtualWindow.d.ts +37 -0
  10. package/dist/data_table/lib/virtualWindow.js +41 -0
  11. package/dist/data_table/parts/ActionsPanel.svelte +17 -2
  12. package/dist/data_table/parts/ActionsPanel.svelte.d.ts +3 -0
  13. package/dist/data_table/parts/ContentPanel.svelte +26 -1
  14. package/dist/data_table/parts/ContentPanel.svelte.d.ts +6 -1
  15. package/dist/data_table/parts/FixedColumnsPanel.svelte +17 -1
  16. package/dist/data_table/parts/FixedColumnsPanel.svelte.d.ts +4 -1
  17. package/dist/data_table/parts/FixedHeaderPanel.svelte +1 -2
  18. package/dist/form/attachment_files/FileUploadBar.svelte +3 -3
  19. package/dist/form/attachment_files/FileUploadPanel.svelte +1 -1
  20. package/dist/form/color_picker/ColorPickerPanel.svelte +1 -1
  21. package/dist/form/date_picker/DateTimePicker.svelte +1 -1
  22. package/dist/form/options_select/OptionsSelect.svelte +2 -2
  23. package/dist/i18nRes/i18nRes.d.ts +41 -2
  24. package/dist/i18nRes/i18nRes.js +2 -1
  25. package/dist/overlay/dialog/CommonDialog.svelte +1 -1
  26. package/dist/overlay/dialog/Dialog.svelte +1 -1
  27. package/dist/overlay/message-box/MessageBox.svelte.js +3 -3
  28. package/package.json +1 -1
  29. package/dist/layout/flex/FlexBlock.svelte +0 -17
  30. package/dist/layout/flex/FlexBlock.svelte.d.ts +0 -38
  31. package/dist/layout/flex/README.md +0 -148
  32. package/dist/layout/flex/README_CN.md +0 -148
  33. package/dist/layout/flex/index.d.ts +0 -2
  34. package/dist/layout/flex/index.js +0 -2
  35. package/dist/layout/index.d.ts +0 -0
  36. package/dist/layout/index.js +0 -1
package/README.md CHANGED
@@ -185,9 +185,9 @@ The library uses CSS custom properties for theming. You can customize the appear
185
185
 
186
186
  The library includes built-in internationalization support:
187
187
 
188
- default language is english. to load other languages:
188
+ The default language is English. Other languages are loaded dynamically at runtime, so adding a new language never requires a code change.
189
189
 
190
- create a language resource json file in the static web directory, example: `/assets/uniface_zh-CN.json`
190
+ Create a language resource json file in the static web directory, example: `/assets/uniface_zh-CN.json` (this is the complete key list):
191
191
  ```json
192
192
  {
193
193
  "uniface": {
@@ -195,7 +195,9 @@ create a language resource json file in the static web directory, example: `/ass
195
195
  "btnClose": "关闭",
196
196
  "btnCancel": "取消",
197
197
  "btnConfirm": "确定",
198
- "textMore": "加载更多"
198
+ "textMore": "加载更多",
199
+ "filterPlaceholder": "搜索...",
200
+ "noResultsText": "无匹配结果..."
199
201
  },
200
202
  "colorPicker": "选择颜色",
201
203
  "calendar": {
@@ -218,14 +220,12 @@ create a language resource json file in the static web directory, example: `/ass
218
220
  "btnRemove": "删除",
219
221
  "btnPickup": "选择文件"
220
222
  },
221
- "propertyEditor": {
222
- "colName": "属性",
223
- "colValue": "值"
224
- },
225
223
  "dataTable": {
226
224
  "rowNo": "#",
227
225
  "actions": "操作",
228
- "emptyDataSet": "暂无数据"
226
+ "emptyDataSet": "暂无数据",
227
+ "totalInfo": "共 <b>{{total}}</b> 条",
228
+ "rowsPerPage": "行/页"
229
229
  },
230
230
  "transfer": {
231
231
  "selectIndicator": "已选择: {{selected}}/{{total}}"
@@ -234,14 +234,14 @@ create a language resource json file in the static web directory, example: `/ass
234
234
  }
235
235
  ```
236
236
 
237
- You can load the resource file in the `+page.svelte` or other startup page.
237
+ Load the resource file in `+page.svelte` or another startup page:
238
238
 
239
239
  ```typescript
240
240
  import i18n, {i18nUtils} from "@ticatec/i18n";
241
241
 
242
242
  // 设置语言
243
243
  i18n.setLanguage('zh-CN'); // 或 'en'
244
- i18nUtils.loadResources('/assets/uniface.json'); //the language code will insert before .json
244
+ i18nUtils.loadResources('/assets/uniface.json'); // the language code will insert before .json => /assets/uniface_zh-CN.json
245
245
  ```
246
246
 
247
247
  ## Available Imports
package/README_CN.md CHANGED
@@ -261,10 +261,11 @@ await messageBox.showInfo("操作完成", { title: "提示" });
261
261
 
262
262
  ## 国际化
263
263
 
264
- 组件库内置国际化支持:
264
+ 组件库内置国际化支持:
265
265
 
266
- 中文资源文件:
267
- 可以将文件放置在静态网站的目录下,比如`/assets/uniface_zh-CN.json`
266
+ 默认语言为英文。其他语言在运行时动态加载,未来新增语言无需修改代码。
267
+
268
+ 在静态资源目录下创建语言资源 json 文件,例如 `/assets/uniface_zh-CN.json`(以下为完整键列表):
268
269
  ```json
269
270
  {
270
271
  "uniface": {
@@ -272,7 +273,9 @@ await messageBox.showInfo("操作完成", { title: "提示" });
272
273
  "btnClose": "关闭",
273
274
  "btnCancel": "取消",
274
275
  "btnConfirm": "确定",
275
- "textMore": "加载更多"
276
+ "textMore": "加载更多",
277
+ "filterPlaceholder": "搜索...",
278
+ "noResultsText": "无匹配结果..."
276
279
  },
277
280
  "colorPicker": "选择颜色",
278
281
  "calendar": {
@@ -295,14 +298,12 @@ await messageBox.showInfo("操作完成", { title: "提示" });
295
298
  "btnRemove": "删除",
296
299
  "btnPickup": "选择文件"
297
300
  },
298
- "propertyEditor": {
299
- "colName": "属性",
300
- "colValue": "值"
301
- },
302
301
  "dataTable": {
303
302
  "rowNo": "#",
304
303
  "actions": "操作",
305
- "emptyDataSet": "暂无数据"
304
+ "emptyDataSet": "暂无数据",
305
+ "totalInfo": "共 <b>{{total}}</b> 条",
306
+ "rowsPerPage": "行/页"
306
307
  },
307
308
  "transfer": {
308
309
  "selectIndicator": "已选择: {{selected}}/{{total}}"
@@ -311,16 +312,16 @@ await messageBox.showInfo("操作完成", { title: "提示" });
311
312
  }
312
313
  ```
313
314
 
315
+ 在 `+page.svelte` 或其他启动页加载资源文件:
316
+
314
317
  ```typescript
315
318
  import i18n, {i18nUtils} from "@ticatec/i18n";
316
319
 
317
320
  // 设置语言
318
321
  i18n.setLanguage('zh-CN'); // 或 'en'
319
- i18nUtils.loadResources('/assets/uniface.json');
322
+ i18nUtils.loadResources('/assets/uniface.json'); // 语言代码会插入到 .json 之前 => /assets/uniface_zh-CN.json
320
323
  ```
321
324
 
322
-
323
-
324
325
  ## 可用导入
325
326
 
326
327
  ### 模块化导入
@@ -1,3 +1,4 @@
1
+ import "../i18nRes";
1
2
  export default class DateContext {
2
3
  private _weekBegin;
3
4
  private _dateFmt;
@@ -1,5 +1,5 @@
1
- import i18nRes from "../i18nRes";
2
1
  import i18n from "@ticatec/i18n";
2
+ import "../i18nRes"; // side effect: seeds the uniface namespace defaults used by i18n.get() below
3
3
  export default class DateContext {
4
4
  _weekBegin = 1;
5
5
  _dateFmt = "YYYY-MM-DD";
@@ -14,19 +14,16 @@ export default class DateContext {
14
14
  return DateContext.instance;
15
15
  }
16
16
  get months() {
17
- return i18nRes.calendar.months;
17
+ return i18n.get('uniface.calendar.months');
18
18
  }
19
19
  get monthsAbbr() {
20
- return i18nRes.calendar.monthsAbbr;
21
- ;
20
+ return i18n.get('uniface.calendar.monthsAbbr');
22
21
  }
23
22
  get weekTitle() {
24
- return i18nRes.calendar.weekTitle;
25
- ;
23
+ return i18n.get('uniface.calendar.weekTitle');
26
24
  }
27
25
  get weekTitleAbbr() {
28
- return i18nRes.calendar.weekTitleAbbr;
29
- ;
26
+ return i18n.get('uniface.calendar.weekTitleAbbr');
30
27
  }
31
28
  set weekBegin(value) {
32
29
  if (value < 0 || value > 6) {
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import Pagination from "../../navigation/pagination";
3
3
  import OptionsSelect from "../../form/options_select";
4
+ import i18nRes from "../../i18nRes";
4
5
  import type {GenerateTotalInfo, OnPageChange, OnPageSizeChange} from "../../types";
5
6
 
6
7
  interface Props {
@@ -29,7 +30,7 @@
29
30
  total = 0,
30
31
  generateInfo,
31
32
  rowCount = $bindable(25),
32
- rowCountLabel = 'Rows/Page',
33
+ rowCountLabel = i18nRes.dataTable.rowsPerPage(),
33
34
  align = '',
34
35
  onPageSizeChange,
35
36
  onchange,
@@ -40,10 +41,12 @@
40
41
  );
41
42
  // Rendered via {@html} below: generateInfo is a trusted, caller-supplied
42
43
  // renderer (not user input) — same opt-in-HTML boundary as DataColumn#renderHTML.
44
+ // The default text comes from the i18n resources (dataTable.totalInfo), so it
45
+ // follows the active language instead of being hardcoded English.
43
46
  let totalText = $derived(
44
47
  generateInfo
45
48
  ? generateInfo(total, pageCount, pageNo, rowCount)
46
- : `Total: <b>${total}</b> entities.`
49
+ : i18nRes.dataTable.totalInfo({total})
47
50
  );
48
51
 
49
52
  const handleRowCountChange = () => {
@@ -5,7 +5,6 @@
5
5
  import type {FunFilter} from "../../data_display/list_box";
6
6
  import type {TransferData} from "../../types";
7
7
  import i18n, {i18nUtils} from "@ticatec/i18n";
8
- import i18nRes from "../../i18nRes";
9
8
 
10
9
 
11
10
  interface Props {
@@ -11,6 +11,7 @@
11
11
  import { OrderDirection } from "./lib/OrderDirection";
12
12
  import type { CompareFunction } from "./lib/CompareFunction";
13
13
  import type { GetRowFontStyle } from "../types";
14
+ import { computeVirtualWindow } from "./lib/virtualWindow";
14
15
 
15
16
  interface Props {
16
17
  columns: Array<DataColumn>;
@@ -26,6 +27,13 @@
26
27
  emptyIndicator?: string;
27
28
  getRowFontStyle?: GetRowFontStyle<T>;
28
29
  style?: string;
30
+ /**
31
+ * 行虚拟化(窗口化渲染):仅渲染可视区域附近的行,大数据集(数千行以上)
32
+ * 建议开启。要求所有行高度一致(即统一的 rowHeight),因此默认关闭。
33
+ */
34
+ virtual?: boolean;
35
+ /** 虚拟化时可视区上下额外渲染的行数。 */
36
+ overscan?: number;
29
37
  }
30
38
 
31
39
  let {
@@ -42,6 +50,8 @@
42
50
  emptyIndicator = undefined,
43
51
  getRowFontStyle = undefined,
44
52
  style = '',
53
+ virtual = false,
54
+ overscan = 8,
45
55
  }: Props = $props();
46
56
 
47
57
  const id: string = Math.round(new Date().getTime() + Math.random() * 1000).toString(36);
@@ -59,6 +69,23 @@
59
69
  let scrollTop = $state(0);
60
70
  let inlineComponent = $state<any>(null);
61
71
 
72
+ // Measured by ContentPanel (its scrollable data area); drives the virtual window.
73
+ let viewportHeight = $state(0);
74
+ let expandIndex = $derived(expandRow ? rows.indexOf(expandRow) : -1);
75
+ // One shared window for all three panels (frozen / content / actions), so the
76
+ // slices they render always cover the same absolute row indices.
77
+ let vwindow = $derived(virtual
78
+ ? computeVirtualWindow({
79
+ scrollTop,
80
+ viewportHeight,
81
+ rowHeight,
82
+ count: rows.length,
83
+ overscan,
84
+ expandIndex,
85
+ inlineRowHeight
86
+ })
87
+ : null);
88
+
62
89
  let orderColumn = $state<DataColumn | null>(null);
63
90
  let orderDirection = $state<OrderDirection>(OrderDirection.Ascending);
64
91
 
@@ -162,6 +189,7 @@
162
189
  {handleWidthChange}
163
190
  width={frozenWidth}
164
191
  bind:scrollTop
192
+ {vwindow}
165
193
  />
166
194
  {/if}
167
195
 
@@ -185,10 +213,12 @@
185
213
  displayHorizontalScroll={actionsColumn != null || indicatorColumn != null || fixedCols.length > 0}
186
214
  showVerticalScroll={actionsColumn == null}
187
215
  {getRowFontStyle}
216
+ {vwindow}
217
+ bind:viewportHeight
188
218
  />
189
219
  {/if}
190
220
 
191
221
  {#if actionsColumn}
192
- <ActionPanel {expandRow} {rowHeight} {headerHeight} {actionsColumn} {rows} {inlineRowHeight} bind:scrollTop />
222
+ <ActionPanel {expandRow} {rowHeight} {headerHeight} {actionsColumn} {rows} {inlineRowHeight} bind:scrollTop {vwindow} />
193
223
  {/if}
194
224
  </div>
@@ -17,6 +17,13 @@ declare function $$render<T = any>(): {
17
17
  emptyIndicator?: string;
18
18
  getRowFontStyle?: GetRowFontStyle<T>;
19
19
  style?: string;
20
+ /**
21
+ * 行虚拟化(窗口化渲染):仅渲染可视区域附近的行,大数据集(数千行以上)
22
+ * 建议开启。要求所有行高度一致(即统一的 rowHeight),因此默认关闭。
23
+ */
24
+ virtual?: boolean;
25
+ /** 虚拟化时可视区上下额外渲染的行数。 */
26
+ overscan?: number;
20
27
  };
21
28
  exports: {};
22
29
  bindings: "inlineRowHeight" | "selectedRows";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Row-windowing math for the DataTable's opt-in virtual scrolling.
3
+ *
4
+ * The table renders three vertically synced panels (frozen columns, data
5
+ * content, actions). Each panel renders only the rows inside the computed
6
+ * window, with a top and bottom spacer so the scroll container keeps its
7
+ * full natural scrollHeight (the native scrollbar and the existing
8
+ * wheel/scroll sync logic keep working unchanged).
9
+ *
10
+ * All rows share one fixed `rowHeight`; at most one row (`expandIndex`) has
11
+ * an inline expansion panel of `inlineRowHeight` rendered directly below it.
12
+ */
13
+ export interface VirtualWindow {
14
+ /** Index of the first rendered row (inclusive). */
15
+ start: number;
16
+ /** Index just past the last rendered row (exclusive). */
17
+ end: number;
18
+ /** Height (px) of the spacer above the rendered rows. */
19
+ topPad: number;
20
+ /** Height (px) of the spacer below the rendered rows. */
21
+ bottomPad: number;
22
+ }
23
+ export interface VirtualWindowInput {
24
+ scrollTop: number;
25
+ /** Height (px) of the scrollable viewport. 0 is tolerated (renders overscan only). */
26
+ viewportHeight: number;
27
+ rowHeight: number;
28
+ /** Total number of rows. */
29
+ count: number;
30
+ /** Extra rows rendered above and below the visible range. */
31
+ overscan?: number;
32
+ /** Index of the expanded row, or -1 when none. */
33
+ expandIndex?: number;
34
+ /** Height (px) of the inline expansion panel below the expanded row. */
35
+ inlineRowHeight?: number;
36
+ }
37
+ export declare function computeVirtualWindow(input: VirtualWindowInput): VirtualWindow;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Row-windowing math for the DataTable's opt-in virtual scrolling.
3
+ *
4
+ * The table renders three vertically synced panels (frozen columns, data
5
+ * content, actions). Each panel renders only the rows inside the computed
6
+ * window, with a top and bottom spacer so the scroll container keeps its
7
+ * full natural scrollHeight (the native scrollbar and the existing
8
+ * wheel/scroll sync logic keep working unchanged).
9
+ *
10
+ * All rows share one fixed `rowHeight`; at most one row (`expandIndex`) has
11
+ * an inline expansion panel of `inlineRowHeight` rendered directly below it.
12
+ */
13
+ /** Top offset (px) of row `i`, accounting for an expansion panel above it. */
14
+ const rowTop = (i, rowHeight, expandIndex, inlineRowHeight) => i * rowHeight + (expandIndex >= 0 && i > expandIndex ? inlineRowHeight : 0);
15
+ export function computeVirtualWindow(input) {
16
+ const { scrollTop, viewportHeight, rowHeight, count } = input;
17
+ const overscan = input.overscan ?? 8;
18
+ const expandIndex = input.expandIndex ?? -1;
19
+ const inlineRowHeight = expandIndex >= 0 ? (input.inlineRowHeight ?? 0) : 0;
20
+ if (count === 0 || rowHeight <= 0) {
21
+ return { start: 0, end: 0, topPad: 0, bottomPad: 0 };
22
+ }
23
+ const st = Math.max(0, scrollTop);
24
+ // First row whose bottom edge is below the viewport top.
25
+ let start = Math.floor(st / rowHeight);
26
+ if (expandIndex >= 0 && start > expandIndex) {
27
+ // The viewport top sits below the expanded row: shift the estimate by the
28
+ // expansion panel's height. Clamping to expandIndex keeps the expanded
29
+ // row (and with it the inline panel, which is rendered inside the #each
30
+ // body next to its row) in the window while any part of the panel is
31
+ // visible.
32
+ start = Math.max(expandIndex, Math.floor((st - inlineRowHeight) / rowHeight));
33
+ }
34
+ start = Math.max(0, Math.min(start, count - 1) - overscan);
35
+ const visibleCount = Math.ceil(viewportHeight / rowHeight) + 1;
36
+ let end = Math.min(count, start + visibleCount + overscan * 2);
37
+ const totalHeight = count * rowHeight + inlineRowHeight;
38
+ const topPad = rowTop(start, rowHeight, expandIndex, inlineRowHeight);
39
+ const bottomPad = totalHeight - rowTop(end, rowHeight, expandIndex, inlineRowHeight);
40
+ return { start, end, topPad, bottomPad };
41
+ }
@@ -4,6 +4,7 @@
4
4
  import type TableRow from "./TableRow";
5
5
  import { onDestroy, onMount } from "svelte";
6
6
  import i18nRes from "../../i18nRes";
7
+ import type { VirtualWindow } from "../lib/virtualWindow";
7
8
 
8
9
  interface Props {
9
10
  actionsColumn: ActionsColumn;
@@ -13,6 +14,8 @@
13
14
  expandRow?: TableRow | null;
14
15
  rowHeight?: number;
15
16
  headerHeight?: number;
17
+ /** Row window when virtual scrolling is on; null renders every row. */
18
+ vwindow?: VirtualWindow | null;
16
19
  }
17
20
 
18
21
  let {
@@ -23,8 +26,13 @@
23
26
  expandRow = null,
24
27
  rowHeight,
25
28
  headerHeight,
29
+ vwindow = null,
26
30
  }: Props = $props();
27
31
 
32
+ // Virtual scrolling: same shared window as the other panels.
33
+ let visibleRows = $derived(vwindow ? rows.slice(vwindow.start, vwindow.end) : rows);
34
+ let startIndex = $derived(vwindow ? vwindow.start : 0);
35
+
28
36
  const handleActionPanelScroll = (e: Event) => {
29
37
  const target = e.target as HTMLDivElement;
30
38
  scrollTop = target.scrollTop;
@@ -54,7 +62,7 @@
54
62
  resizeObserver?.disconnect();
55
63
  });
56
64
 
57
- let actionText = i18nRes.dataTable.actions;
65
+ let actionText = i18nRes.dataTable.actions();
58
66
  </script>
59
67
 
60
68
  <div class="action-panel" bind:this={panel} style="user-select: none; width: {actionsColumn.width}px;">
@@ -65,7 +73,11 @@
65
73
  </div>
66
74
  <div bind:this={scrollPanel} class="rows-container" style="overflow-y: auto" onscroll={handleActionPanelScroll}>
67
75
  <div>
68
- {#each rows as row, idx (idx)}
76
+ {#if vwindow}
77
+ <div style="height: {vwindow.topPad}px"></div>
78
+ {/if}
79
+ {#each visibleRows as row, i (startIndex + i)}
80
+ {@const idx = startIndex + i}
69
81
  <ActionsRow
70
82
  {row}
71
83
  {rowHeight}
@@ -79,6 +91,9 @@
79
91
  <div class="inline-panel" style="height: {inlineRowHeight ?? 0}px"> </div>
80
92
  {/if}
81
93
  {/each}
94
+ {#if vwindow}
95
+ <div style="height: {vwindow.bottomPad}px"></div>
96
+ {/if}
82
97
  </div>
83
98
  </div>
84
99
  <div class="bottom-mask-overlay">
@@ -1,5 +1,6 @@
1
1
  import type ActionsColumn from "../lib/ActionsColumn";
2
2
  import type TableRow from "./TableRow";
3
+ import type { VirtualWindow } from "../lib/virtualWindow";
3
4
  interface Props {
4
5
  actionsColumn: ActionsColumn;
5
6
  scrollTop?: number;
@@ -8,6 +9,8 @@ interface Props {
8
9
  expandRow?: TableRow | null;
9
10
  rowHeight?: number;
10
11
  headerHeight?: number;
12
+ /** Row window when virtual scrolling is on; null renders every row. */
13
+ vwindow?: VirtualWindow | null;
11
14
  }
12
15
  declare const ActionsPanel: import("svelte").Component<Props, {}, "scrollTop">;
13
16
  type ActionsPanel = ReturnType<typeof ActionsPanel>;
@@ -7,6 +7,7 @@
7
7
  import UniDataTable, { type TableEventHandler } from "../UniDataTable";
8
8
  import { OrderDirection } from "../lib/OrderDirection";
9
9
  import type { GetRowFontStyle } from "../../types";
10
+ import type { VirtualWindow } from "../lib/virtualWindow";
10
11
 
11
12
  interface Props {
12
13
  columns: Array<DataColumn>;
@@ -28,6 +29,10 @@
28
29
  handleWidthChange?: TableEventHandler;
29
30
  handleCellClick?: (col: DataColumn) => any;
30
31
  tabWidth: number;
32
+ /** Row window when virtual scrolling is on; null renders every row. */
33
+ vwindow?: VirtualWindow | null;
34
+ /** Measured height of the scrollable data area (drives the virtual window). */
35
+ viewportHeight?: number;
31
36
  }
32
37
 
33
38
  let {
@@ -50,6 +55,8 @@
50
55
  handleWidthChange,
51
56
  handleCellClick,
52
57
  tabWidth = $bindable(0),
58
+ vwindow = null,
59
+ viewportHeight = $bindable(0),
53
60
  }: Props = $props();
54
61
 
55
62
  let scrollLeft = $state(0);
@@ -62,8 +69,13 @@
62
69
  onMount(() => {
63
70
  resizeObserver = new ResizeObserver(() => {
64
71
  viewWidth = Math.round(viewPanel?.clientWidth) - 1;
72
+ viewportHeight = dataPanel?.clientHeight ?? 0;
65
73
  });
66
74
  resizeObserver.observe(viewPanel);
75
+ if (dataPanel) {
76
+ resizeObserver.observe(dataPanel);
77
+ viewportHeight = dataPanel.clientHeight ?? 0;
78
+ }
67
79
  });
68
80
 
69
81
  onDestroy(() => {
@@ -117,6 +129,12 @@
117
129
 
118
130
  let rowWidth = $derived(Math.max(viewWidth, tabWidth));
119
131
  let hasWhitespace = $derived(viewWidth > tabWidth);
132
+
133
+ // Virtual scrolling: render only the windowed slice, padded by spacers so the
134
+ // scroll container keeps its full natural scrollHeight. `startIndex` keeps the
135
+ // rendered rows' absolute indices (striping, keys) stable while scrolling.
136
+ let visibleRows = $derived(vwindow ? rows.slice(vwindow.start, vwindow.end) : rows);
137
+ let startIndex = $derived(vwindow ? vwindow.start : 0);
120
138
  </script>
121
139
 
122
140
  <div class="data-content-panel" bind:this={viewPanel}>
@@ -140,7 +158,11 @@
140
158
  >
141
159
  {#if rows && rows.length > 0}
142
160
  <div style="box-sizing: border-box; width: {rowWidth}px; top: 0px">
143
- {#each rows as row, idx (idx)}
161
+ {#if vwindow}
162
+ <div style="height: {vwindow.topPad}px"></div>
163
+ {/if}
164
+ {#each visibleRows as row, i (startIndex + i)}
165
+ {@const idx = startIndex + i}
144
166
  <DataRow
145
167
  fontStyle={getRowFontStyle?.(row.data)}
146
168
  {rowHeight}
@@ -161,6 +183,9 @@
161
183
  </div>
162
184
  {/if}
163
185
  {/each}
186
+ {#if vwindow}
187
+ <div style="height: {vwindow.bottomPad}px"></div>
188
+ {/if}
164
189
  </div>
165
190
  {:else}
166
191
  <div class="empty-content-board" style="width: 100%">
@@ -3,6 +3,7 @@ import type TableRow from "./TableRow";
3
3
  import UniDataTable, { type TableEventHandler } from "../UniDataTable";
4
4
  import { OrderDirection } from "../lib/OrderDirection";
5
5
  import type { GetRowFontStyle } from "../../types";
6
+ import type { VirtualWindow } from "../lib/virtualWindow";
6
7
  interface Props {
7
8
  columns: Array<DataColumn>;
8
9
  rows: Array<TableRow>;
@@ -23,7 +24,11 @@ interface Props {
23
24
  handleWidthChange?: TableEventHandler;
24
25
  handleCellClick?: (col: DataColumn) => any;
25
26
  tabWidth: number;
27
+ /** Row window when virtual scrolling is on; null renders every row. */
28
+ vwindow?: VirtualWindow | null;
29
+ /** Measured height of the scrollable data area (drives the virtual window). */
30
+ viewportHeight?: number;
26
31
  }
27
- declare const ContentPanel: import("svelte").Component<Props, {}, "rows" | "scrollTop" | "inlineRowHeight" | "tabWidth">;
32
+ declare const ContentPanel: import("svelte").Component<Props, {}, "scrollTop" | "viewportHeight" | "rows" | "inlineRowHeight" | "tabWidth">;
28
33
  type ContentPanel = ReturnType<typeof ContentPanel>;
29
34
  export default ContentPanel;
@@ -10,6 +10,7 @@
10
10
  import type DataColumn from "../lib/DataColumn";
11
11
  import FixedHeaderPanel from "./FixedHeaderPanel.svelte";
12
12
  import { OrderDirection } from "../lib/OrderDirection";
13
+ import type { VirtualWindow } from "../lib/virtualWindow";
13
14
 
14
15
  interface Props {
15
16
  fixedCols?: Array<DataColumn>;
@@ -27,6 +28,8 @@
27
28
  rowHeight?: number;
28
29
  headerHeight?: number;
29
30
  inlineRowHeight?: number;
31
+ /** Row window when virtual scrolling is on; null renders every row. */
32
+ vwindow?: VirtualWindow | null;
30
33
  }
31
34
 
32
35
  let {
@@ -45,8 +48,14 @@
45
48
  rowHeight,
46
49
  headerHeight,
47
50
  inlineRowHeight = 0,
51
+ vwindow = null,
48
52
  }: Props = $props();
49
53
 
54
+ // Virtual scrolling: same shared window as the other panels, so all three
55
+ // render the same absolute row indices (selection state and striping use them).
56
+ let visibleRows = $derived(vwindow ? (rows ?? []).slice(vwindow.start, vwindow.end) : (rows ?? []));
57
+ let startIndex = $derived(vwindow ? vwindow.start : 0);
58
+
50
59
  let selectionMode = $state<SelectionMode>(SelectionMode.None);
51
60
 
52
61
  let selectable = $derived(indicatorColumn?.selectable == true);
@@ -133,7 +142,11 @@
133
142
  />
134
143
  <div class="rows-container" bind:this={viewPanel} onwheel={handleWheelEvent}>
135
144
  <div>
136
- {#each rows ?? [] as row, idx (idx)}
145
+ {#if vwindow}
146
+ <div style="height: {vwindow.topPad}px"></div>
147
+ {/if}
148
+ {#each visibleRows as row, i (startIndex + i)}
149
+ {@const idx = startIndex + i}
137
150
  <FixedRow
138
151
  rowNo={idx + 1}
139
152
  {row}
@@ -153,6 +166,9 @@
153
166
  <div class="inline-panel" style="height: {inlineRowHeight}px"> </div>
154
167
  {/if}
155
168
  {/each}
169
+ {#if vwindow}
170
+ <div style="height: {vwindow.bottomPad}px"></div>
171
+ {/if}
156
172
  </div>
157
173
  </div>
158
174
  <div class="bottom-mask-overlay">
@@ -3,6 +3,7 @@ import type { IndicatorColumn } from "..";
3
3
  import { type RowEventHandler, type TableEventHandler } from "../UniDataTable";
4
4
  import type DataColumn from "../lib/DataColumn";
5
5
  import { OrderDirection } from "../lib/OrderDirection";
6
+ import type { VirtualWindow } from "../lib/virtualWindow";
6
7
  interface Props {
7
8
  fixedCols?: Array<DataColumn>;
8
9
  indicatorColumn?: IndicatorColumn | null;
@@ -19,7 +20,9 @@ interface Props {
19
20
  rowHeight?: number;
20
21
  headerHeight?: number;
21
22
  inlineRowHeight?: number;
23
+ /** Row window when virtual scrolling is on; null renders every row. */
24
+ vwindow?: VirtualWindow | null;
22
25
  }
23
- declare const FixedColumnsPanel: import("svelte").Component<Props, {}, "expandRow" | "scrollTop" | "selectedRows">;
26
+ declare const FixedColumnsPanel: import("svelte").Component<Props, {}, "scrollTop" | "expandRow" | "selectedRows">;
24
27
  type FixedColumnsPanel = ReturnType<typeof FixedColumnsPanel>;
25
28
  export default FixedColumnsPanel;
@@ -2,7 +2,6 @@
2
2
  import type DataColumn from "../lib/DataColumn";
3
3
  import type { IndicatorColumn } from "..";
4
4
  import { type SelectionEventHandler, SelectionMode, type TableEventHandler } from "../UniDataTable";
5
- import utils from "../../overlay/common/utils";
6
5
  import { OrderDirection } from "../lib/OrderDirection";
7
6
  import i18nRes from "../../i18nRes";
8
7
 
@@ -107,7 +106,7 @@
107
106
  {:else}
108
107
  <div style="text-align: center" class="vertical-center">
109
108
  {#if indicatorColumn.displayNo}
110
- <span>{i18nRes.dataTable.rowNo}</span>
109
+ <span>{i18nRes.dataTable.rowNo()}</span>
111
110
  {/if}
112
111
  </div>
113
112
  {/if}
@@ -18,9 +18,9 @@
18
18
  let status: ProgressStatus = $state(getInitialStatus());
19
19
  let progress: number = $state(0);
20
20
 
21
- let cancelLabel: string = i18nRes.common.btnCancel.toString();
22
- let retryLabel: string = i18nRes.upload.btnRetry.toString();
23
- let removeLabel: string = i18nRes.upload.btnRemove.toString();
21
+ let cancelLabel: string = i18nRes.common.btnCancel();
22
+ let retryLabel: string = i18nRes.upload.btnRetry();
23
+ let removeLabel: string = i18nRes.upload.btnRemove();
24
24
 
25
25
  const cancelUpload = async () => {
26
26
  if (await file.cancel()) {
@@ -26,7 +26,7 @@
26
26
  visible?: boolean;
27
27
  } = $props();
28
28
 
29
- let btnPickup = i18nRes.upload.btnPickup.toString();
29
+ let btnPickup = i18nRes.upload.btnPickup();
30
30
 
31
31
  let files: Array<UploadFile> = $state([]);
32
32
  let fileInput: any;
@@ -47,7 +47,7 @@
47
47
 
48
48
  <div class="color-picker">
49
49
  <label for="color-input">
50
- {i18nRes.pickupColor}
50
+ {i18nRes.colorPicker()}
51
51
  </label>
52
52
 
53
53
  <div class="color-input-group">
@@ -44,7 +44,7 @@
44
44
  };
45
45
 
46
46
  const format = $derived(dateFmts[precision]);
47
- const confirmText = $derived(i18nRes.calendar.confirmText as any as string);
47
+ const confirmText = $derived(i18nRes.calendar.confirmText());
48
48
 
49
49
  let isOpen = $state(false);
50
50
  let currentValue = $state<dayjs.Dayjs>(dayjs());
@@ -42,9 +42,9 @@
42
42
  onchange,
43
43
  onfocus,
44
44
  onblur,
45
- filterPlaceholder = i18nRes.common.filterPlaceholder.toString(),
45
+ filterPlaceholder = i18nRes.common.filterPlaceholder(),
46
46
  filterFunction,
47
- noResultsText = i18nRes.common.noResultsText.toString(),
47
+ noResultsText = i18nRes.common.noResultsText(),
48
48
  itemRender,
49
49
  }: Props = $props();
50
50
 
@@ -1,3 +1,42 @@
1
- import { i18nUtils } from "@ticatec/i18n";
2
- declare const i18nRes: ReturnType<typeof i18nUtils.createResourceProxy>;
1
+ import type { ResourceProxy } from "@ticatec/i18n";
2
+ declare const langRes: {
3
+ common: {
4
+ btnClose: string;
5
+ btnCancel: string;
6
+ btnConfirm: string;
7
+ textMore: string;
8
+ filterPlaceholder: string;
9
+ noResultsText: string;
10
+ };
11
+ colorPicker: string;
12
+ calendar: {
13
+ months: string[];
14
+ monthsAbbr: string[];
15
+ weekTitle: string[];
16
+ weekTitleAbbr: string[];
17
+ confirmText: string;
18
+ };
19
+ upload: {
20
+ btnRetry: string;
21
+ btnRemove: string;
22
+ btnPickup: string;
23
+ };
24
+ dataTable: {
25
+ rowNo: string;
26
+ actions: string;
27
+ emptyDataSet: string;
28
+ totalInfo: string;
29
+ rowsPerPage: string;
30
+ };
31
+ transfer: {
32
+ selectIndicator: string;
33
+ };
34
+ };
35
+ /**
36
+ * The shape of this package's translation resources. A language resource file
37
+ * loaded at runtime (e.g. /assets/uniface_zh-CN.json) should provide every key
38
+ * of this structure, wrapped in the `uniface` namespace.
39
+ */
40
+ export type UnifaceLangRes = typeof langRes;
41
+ declare const i18nRes: ResourceProxy<UnifaceLangRes>;
3
42
  export default i18nRes;
@@ -33,7 +33,8 @@ const langRes = {
33
33
  rowNo: "#",
34
34
  actions: "Actions",
35
35
  emptyDataSet: 'Empty dataset',
36
- totalInfo: "Total: <b>{{total}}</b>"
36
+ totalInfo: "Total: <b>{{total}}</b>",
37
+ rowsPerPage: "Rows/Page"
37
38
  },
38
39
  transfer: {
39
40
  selectIndicator: "Selected: {{selected}}/{{total}}"
@@ -35,7 +35,7 @@
35
35
  let dialog: Dialog;
36
36
 
37
37
  const confirmAction = $derived<ButtonAction>({
38
- label: confirmText ?? i18nRes.common.btnConfirm.toString(),
38
+ label: confirmText ?? i18nRes.common.btnConfirm(),
39
39
  type: "primary",
40
40
  disabled: confirmHandler == null || !enableConfirm,
41
41
  handler: async () => {
@@ -34,7 +34,7 @@
34
34
  closeConfirm = null,
35
35
  content$style = '',
36
36
  hideClose = false,
37
- closeButton = i18nRes.common.btnClose,
37
+ closeButton = i18nRes.common.btnClose(),
38
38
  onClose = null,
39
39
  closable = true,
40
40
  dialog$style = "",
@@ -43,14 +43,14 @@ class MessageBoxController {
43
43
  }
44
44
  showInfo = (_message, options = { type: 'info' }) => {
45
45
  const infoButtons = [
46
- { label: i18nRes.common.btnClose, type: 'primary', result: ModalResult.MR_OK }
46
+ { label: i18nRes.common.btnClose(), type: 'primary', result: ModalResult.MR_OK }
47
47
  ];
48
48
  return this.showMessageBox(_message, infoButtons, options);
49
49
  };
50
50
  showConfirm = (_message, options = { type: 'question' }) => {
51
51
  const confirmButtons = [
52
- { label: i18nRes.common.btnConfirm, type: 'primary', result: ModalResult.MR_OK },
53
- { label: i18nRes.common.btnCancel, type: 'secondary', result: ModalResult.MR_CANCEL }
52
+ { label: i18nRes.common.btnConfirm(), type: 'primary', result: ModalResult.MR_OK },
53
+ { label: i18nRes.common.btnCancel(), type: 'secondary', result: ModalResult.MR_CANCEL }
54
54
  ];
55
55
  return this.showMessageBox(_message, confirmButtons, options);
56
56
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticatec/uniface-element",
3
- "version": "5.0.4",
3
+ "version": "5.0.5",
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",
@@ -1,17 +0,0 @@
1
- <script lang="ts">
2
-
3
- export let fixed: boolean = false;
4
- export let style: string = '';
5
- export {className as class };
6
- export let direction: 'horizontal' | 'vertical' = 'horizontal';
7
- export let flex: boolean = false;
8
- export let autoFit: boolean = false;
9
- export let grow: boolean = false;
10
- export let shrink: boolean = false;
11
-
12
- let className:string = '';
13
-
14
- </script>
15
- <div class:flex class="flex-block {className} {flex ? direction : ''}" class:fixed class:grow class:shrink class:auto_fit={autoFit} {style}>
16
- <slot/>
17
- </div>
@@ -1,38 +0,0 @@
1
- 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> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: Props & {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
15
- default: any;
16
- } ? Props extends Record<string, never> ? any : {
17
- children?: any;
18
- } : {});
19
- declare const FlexBlock: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
20
- fixed?: boolean;
21
- style?: string;
22
- class?: string;
23
- direction?: "horizontal" | "vertical";
24
- flex?: boolean;
25
- autoFit?: boolean;
26
- grow?: boolean;
27
- shrink?: boolean;
28
- }, {
29
- default: {};
30
- }>, {
31
- [evt: string]: CustomEvent<any>;
32
- }, {
33
- default: {};
34
- }, {
35
- class: string;
36
- }, string>;
37
- type FlexBlock = InstanceType<typeof FlexBlock>;
38
- export default FlexBlock;
@@ -1,148 +0,0 @@
1
- # FlexBlock
2
-
3
- A flexible flexbox layout component with support for horizontal/vertical directions, resizing, and growth/shrink options.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @ticatec/uniface-element
9
- ```
10
-
11
- ## Import
12
-
13
- ```typescript
14
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
15
- ```
16
-
17
- ## Basic Usage
18
-
19
- ```svelte
20
- <script>
21
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
22
- </script>
23
-
24
- <FlexBlock>
25
- <div>Item 1</div>
26
- <div>Item 2</div>
27
- <div>Item 3</div>
28
- </FlexBlock>
29
- ```
30
-
31
- ## Props
32
-
33
- | Prop | Type | Default | Description |
34
- |------|------|---------|-------------|
35
- | `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Flex direction |
36
- | `flex` | `boolean` | `false` | Apply flex styling based on direction |
37
- | `fixed` | `boolean` | `false` | Enable fixed positioning |
38
- | `grow` | `boolean` | `false` | Enable flex-grow |
39
- | `shrink` | `boolean` | `false` | Enable flex-shrink |
40
- | `autoFit` | `boolean` | `false` | Enable auto-fit to content |
41
- | `style` | `string` | `''` | Custom CSS styles for the container |
42
- | `class` | `string` | `''` | Custom CSS class name |
43
-
44
- ## Slots
45
-
46
- | Slot | Description |
47
- |------|-------------|
48
- | `default` | Main content area |
49
-
50
- ## Events
51
-
52
- No custom events.
53
-
54
- ## Examples
55
-
56
- ### Horizontal Layout
57
-
58
- ```svelte
59
- <script>
60
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
61
- </script>
62
-
63
- <FlexBlock direction="horizontal" style="gap: 16px;">
64
- <div>Item 1</div>
65
- <div>Item 2</div>
66
- <div>Item 3</div>
67
- </FlexBlock>
68
- ```
69
-
70
- ### Vertical Layout
71
-
72
- ```svelte
73
- <script>
74
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
75
- </script>
76
-
77
- <FlexBlock direction="vertical" style="gap: 16px;">
78
- <div>Header</div>
79
- <div>Main Content</div>
80
- <div>Footer</div>
81
- </FlexBlock>
82
- ```
83
-
84
- ### Flex Grow and Shrink
85
-
86
- ```svelte
87
- <script>
88
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
89
- </script>
90
-
91
- <FlexBlock direction="horizontal" flex grow shrink>
92
- <div style="flex: 1;">Flexible Item</div>
93
- <div style="width: 200px;">Fixed Item</div>
94
- </FlexBlock>
95
- ```
96
-
97
- ### Auto-Fit Layout
98
-
99
- ```svelte
100
- <script>
101
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
102
- </script>
103
-
104
- <FlexBlock autoFit>
105
- {#each items as item}
106
- <div>{item}</div>
107
- {/each}
108
- </FlexBlock>
109
- ```
110
-
111
- ### Card Grid
112
-
113
- ```svelte
114
- <script>
115
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
116
- </script>
117
-
118
- <FlexBlock direction="horizontal" autoFit style="gap: 16px;">
119
- {#each cards as card}
120
- <div style="border: 1px solid #ddd; padding: 16px; border-radius: 8px;">
121
- <h3>{card.title}</h3>
122
- <p>{card.description}</p>
123
- </div>
124
- {/each}
125
- </FlexBlock>
126
- ```
127
-
128
- ### Fixed Positioning
129
-
130
- ```svelte
131
- <script>
132
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
133
- </script>
134
-
135
- <FlexBlock fixed direction="vertical" style="position: fixed; top: 0; left: 0; right: 0; height: 100vh;">
136
- <div style="height: 60px;">Header</div>
137
- <div style="flex: 1; overflow: auto;">Main Content</div>
138
- <div style="height: 40px;">Footer</div>
139
- </FlexBlock>
140
- ```
141
-
142
- ## Best Practices
143
-
144
- 1. **Use direction prop** - Choose horizontal or vertical based on your layout needs
145
- 2. **Combine with flex** - Use `flex` prop for responsive layouts that adapt to container size
146
- 3. **Use autoFit** - Enable auto-fit for dynamic content that should wrap
147
- 4. **Use grow/shrink** - Control how items expand or contract within the flex container
148
- 5. **Fixed positioning** - Use `fixed` for sticky layouts that don't scroll with content
@@ -1,148 +0,0 @@
1
- # FlexBlock 弹性布局组件
2
-
3
- 一个灵活的 flexbox 布局组件,支持水平/垂直方向、调整大小和增长/收缩选项。
4
-
5
- ## 安装
6
-
7
- ```bash
8
- npm install @ticatec/uniface-element
9
- ```
10
-
11
- ## 导入
12
-
13
- ```typescript
14
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
15
- ```
16
-
17
- ## 基本用法
18
-
19
- ```svelte
20
- <script>
21
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
22
- </script>
23
-
24
- <FlexBlock>
25
- <div>项目 1</div>
26
- <div>项目 2</div>
27
- <div>项目 3</div>
28
- </FlexBlock>
29
- ```
30
-
31
- ## 属性
32
-
33
- | 属性 | 类型 | 默认值 | 说明 |
34
- |------|------|---------|------|
35
- | `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Flex 方向 |
36
- | `flex` | `boolean` | `false` | 根据方向应用 flex 样式 |
37
- | `fixed` | `boolean` | `false` | 启用固定定位 |
38
- | `grow` | `boolean` | `false` | 启用 flex-grow |
39
- | `shrink` | `boolean` | `false` | 启用 flex-shrink |
40
- | `autoFit` | `boolean` | `false` | 启用自动适应内容 |
41
- | `style` | `string` | `''` | 容器的自定义 CSS 样式 |
42
- | `class` | `string` | `''` | 自定义 CSS 类名 |
43
-
44
- ## 插槽
45
-
46
- | 插槽 | 说明 |
47
- |------|------|
48
- | `default` | 主要内容区域 |
49
-
50
- ## 事件
51
-
52
- 无自定义事件。
53
-
54
- ## 示例
55
-
56
- ### 水平布局
57
-
58
- ```svelte
59
- <script>
60
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
61
- </script>
62
-
63
- <FlexBlock direction="horizontal" style="gap: 16px;">
64
- <div>项目 1</div>
65
- <div>项目 2</div>
66
- <div>项目 3</div>
67
- </FlexBlock>
68
- ```
69
-
70
- ### 垂直布局
71
-
72
- ```svelte
73
- <script>
74
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
75
- </script>
76
-
77
- <FlexBlock direction="vertical" style="gap: 16px;">
78
- <div>头部</div>
79
- <div>主要内容</div>
80
- <div>页脚</div>
81
- </FlexBlock>
82
- ```
83
-
84
- ### 弹性增长和收缩
85
-
86
- ```svelte
87
- <script>
88
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
89
- </script>
90
-
91
- <FlexBlock direction="horizontal" flex grow shrink>
92
- <div style="flex: 1;">弹性项目</div>
93
- <div style="width: 200px;">固定项目</div>
94
- </FlexBlock>
95
- ```
96
-
97
- ### 自动适应布局
98
-
99
- ```svelte
100
- <script>
101
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
102
- </script>
103
-
104
- <FlexBlock autoFit>
105
- {#each items as item}
106
- <div>{item}</div>
107
- {/each}
108
- </FlexBlock>
109
- ```
110
-
111
- ### 卡片网格
112
-
113
- ```svelte
114
- <script>
115
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
116
- </script>
117
-
118
- <FlexBlock direction="horizontal" autoFit style="gap: 16px;">
119
- {#each cards as card}
120
- <div style="border: 1px solid #ddd; padding: 16px; border-radius: 8px;">
121
- <h3>{card.title}</h3>
122
- <p>{card.description}</p>
123
- </div>
124
- {/each}
125
- </FlexBlock>
126
- ```
127
-
128
- ### 固定定位
129
-
130
- ```svelte
131
- <script>
132
- import FlexBlock from '@ticatec/uniface-element/FlexBlock';
133
- </script>
134
-
135
- <FlexBlock fixed direction="vertical" style="position: fixed; top: 0; left: 0; right: 0; height: 100vh;">
136
- <div style="height: 60px;">头部</div>
137
- <div style="flex: 1; overflow: auto;">主要内容</div>
138
- <div style="height: 40px;">页脚</div>
139
- </FlexBlock>
140
- ```
141
-
142
- ## 最佳实践
143
-
144
- 1. **使用 direction 属性** - 根据布局需求选择水平或垂直方向
145
- 2. **结合 flex 使用** - 使用 `flex` 属性实现响应式布局,适应容器大小
146
- 3. **使用 autoFit** - 动态内容启用自动适应换行
147
- 4. **使用 grow/shrink** - 控制项目在 flex 容器中的扩展或收缩
148
- 5. **固定定位** - 使用 `fixed` 实现不随内容滚动的粘性布局
@@ -1,2 +0,0 @@
1
- import FlexBlock from "./FlexBlock.svelte";
2
- export default FlexBlock;
@@ -1,2 +0,0 @@
1
- import FlexBlock from "./FlexBlock.svelte";
2
- export default FlexBlock;
File without changes
@@ -1 +0,0 @@
1
- "use strict";