@digitalwalletcorp/vue-core 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalwalletcorp/vue-core",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "description": "Basic UI components for Vue 3",
6
6
  "main": "lib/index.cjs",
@@ -19,12 +19,12 @@
19
19
  <template>
20
20
  <section
21
21
  class="dwui-data-table"
22
- :class="{ 'dwui-selectable': props.selectable }"
22
+ :class="{ 'dwui-selectable': settings.selectable }"
23
23
  >
24
24
  <DataTableControls
25
- v-if="props.controls === 'both' || props.controls === 'top'"
25
+ v-if="settings.controls === 'both' || settings.controls === 'top'"
26
26
  v-bind="controlProps"
27
- :pagination="props.pagination === 'both' || props.pagination === 'top'"
27
+ :pagination="settings.pagination === 'both' || settings.pagination === 'top'"
28
28
  @emit:change-page="emit('emit:changePage', $event)"
29
29
  @emit:change-limit="emit('emit:changeLimit', $event)"
30
30
  @emit:download-page="emit('emit:downloadPage')"
@@ -33,14 +33,14 @@
33
33
  <div
34
34
  ref="refContent"
35
35
  class="dwui-data-table-content"
36
- :class="[{ 'dwui-loading': props.loading }, props.contentClass]"
37
- :style="props.contentStyle"
36
+ :class="[{ 'dwui-loading': props.loading }, settings.contentClass]"
37
+ :style="settings.contentStyle"
38
38
  tabindex="-1"
39
39
  @keydown="onKeyDown"
40
40
  >
41
41
  <table
42
- :class="props.tableClass"
43
- :style="props.tableStyle"
42
+ :class="settings.tableClass"
43
+ :style="settings.tableStyle"
44
44
  >
45
45
  <thead ref="refThead">
46
46
  <tr>
@@ -57,8 +57,8 @@
57
57
  <div class="dwui-data-table-header">
58
58
  <span
59
59
  class="dwui-data-table-sort"
60
- :role="props.sortable ? 'button' : undefined"
61
- :tabindex="props.sortable ? 0 : undefined"
60
+ :role="settings.sortable ? 'button' : undefined"
61
+ :tabindex="settings.sortable ? 0 : undefined"
62
62
  @click="onClickSort(header)"
63
63
  @keydown.enter.prevent="onClickSort(header)"
64
64
  @keydown.space.prevent="onClickSort(header)"
@@ -76,7 +76,7 @@
76
76
  </span>
77
77
  </div>
78
78
  <div
79
- v-if="props.resizable && index < innerHeaders.length - 1"
79
+ v-if="settings.resizable && index < innerHeaders.length - 1"
80
80
  class="dwui-data-table-resizer"
81
81
  @pointerdown="onResizeStart($event, index)"
82
82
  @pointermove="onResizing"
@@ -89,13 +89,14 @@
89
89
  <tbody ref="refTbody">
90
90
  <template v-if="!props.loading">
91
91
  <slot
92
+ :rows="rows"
92
93
  :click-row="clickRow"
93
94
  :dblclick-row="dblclickRow"
94
95
  :row-number="rowNumber"
95
96
  :selected-index="selectedIndex"
96
97
  />
97
98
  <tr
98
- v-if="props.dataList.length === 0 && props.showNoData"
99
+ v-if="props.dataList.length === 0 && settings.showNoData"
99
100
  class="dwui-data-table-no-data"
100
101
  >
101
102
  <td :colspan="totalColSize">
@@ -115,9 +116,9 @@
115
116
  </div>
116
117
  </div>
117
118
  <DataTableControls
118
- v-if="props.controls === 'both' || props.controls === 'bottom'"
119
+ v-if="settings.controls === 'both' || settings.controls === 'bottom'"
119
120
  v-bind="controlProps"
120
- :pagination="props.pagination === 'both' || props.pagination === 'bottom'"
121
+ :pagination="settings.pagination === 'both' || settings.pagination === 'bottom'"
121
122
  @emit:change-page="emit('emit:changePage', $event)"
122
123
  @emit:change-limit="emit('emit:changeLimit', $event)"
123
124
  @emit:download-page="emit('emit:downloadPage')"
@@ -127,32 +128,130 @@
127
128
  </template>
128
129
 
129
130
  <script setup lang="ts" generic="T">
130
- import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
131
- import type { StyleValue } from 'vue';
131
+ import { computed, inject, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
132
+ import type { ClassValue, StyleValue } from 'vue';
132
133
  import { SvgPointingTriangle } from '@digitalwalletcorp/vue-svg-icons';
133
134
  import DataTableControls from '@/internal/data-table-controls.vue';
134
135
  import { compareValues, getValueByPath, resolveLabel, stringifyValue } from '@/internal/data-table-value';
135
- import type { DataTableEmits, DataTableHeader, DataTableLabels, DataTableProps, DataTableResolvedLabels, DataTableSlots, DataTableSortContext } from '@/types/data-table';
136
+ import { vueCoreOptionsKey } from '@/helpers/vue-core-options';
137
+ import type { DataTableOptions } from '@/helpers/vue-core-options';
138
+ import type { DataTableControlsPlacement, DataTableHeader, DataTableLabels, DataTableResolvedLabels, DataTableSortContext, DataTableSortMode } from '@/types/data-table';
136
139
 
137
- const props = withDefaults(defineProps<DataTableProps<T>>(), {
140
+ /**
141
+ * DataTable の props
142
+ */
143
+ export interface Props<T = unknown> {
144
+ /** 表示している行のデータ。スロットで描画する行と同じ順に並んでいること */
145
+ dataList: T[];
146
+ /** ヘッダ。id は一意であること */
147
+ headers: DataTableHeader<T>[];
148
+ /** 読み込み中。true の間は行を描画せず読み込み中の表示を出す */
149
+ loading?: boolean;
150
+ /** 総件数。未指定なら dataList の件数 */
151
+ totalCount?: number;
152
+ /** 現在ページ(1起点) */
153
+ current?: number;
154
+ /** 1ページあたりの表示件数 */
155
+ limit?: number;
156
+ /** 1ページあたりの表示件数の上限 */
157
+ maxLimit?: number;
158
+ /** ページネーションに並べるページ番号の最大数 */
159
+ maxPagination?: number;
160
+ /** 行をクリックで選択できるか */
161
+ selectable?: boolean;
162
+ /** コントロールを出す位置 */
163
+ controls?: DataTableControlsPlacement;
164
+ /** ページネーションを出す位置。controls が無い側には出ない */
165
+ pagination?: DataTableControlsPlacement;
166
+ /** ページダウンロードボタンを表示するか */
167
+ showDownloadPage?: boolean;
168
+ /** 全件ダウンロードボタンを表示するか */
169
+ showDownloadAll?: boolean;
170
+ /** 総件数を表示するか */
171
+ showTotal?: boolean;
172
+ /** 1ページあたりの表示件数の入力を表示するか */
173
+ showLimit?: boolean;
174
+ /** ページネーションにページ番号の選択を表示するか */
175
+ showPageSelect?: boolean;
176
+ /** dataList が空のときにデータが無い旨の行を表示するか */
177
+ showNoData?: boolean;
178
+ /** ヘッダの境界のドラッグで列幅を変えられるか */
179
+ resizable?: boolean;
180
+ /** ヘッダのクリックでソートできるか */
181
+ sortable?: boolean;
182
+ /** ソートの方式。未指定なら remote */
183
+ sortMode?: DataTableSortMode;
184
+ /** テーブルをスクロールする領域に付ける class */
185
+ contentClass?: ClassValue;
186
+ /** テーブルをスクロールする領域に付ける style */
187
+ contentStyle?: StyleValue;
188
+ /** table に付ける class */
189
+ tableClass?: ClassValue;
190
+ /** table に付ける style */
191
+ tableStyle?: StyleValue;
192
+ /** 表示する文言。未指定の項目は英語 */
193
+ labels?: DataTableLabels;
194
+ /** 総件数の整形。未指定なら en-US の桁区切り。SSR とクライアントで同じ文字列を返すこと */
195
+ formatTotalCount?: (count: number) => string;
196
+ }
197
+
198
+ /**
199
+ * DataTable が発行するイベント
200
+ */
201
+ export interface Emits<T = unknown> {
202
+ /** 選択が変わった。クリックでの選択・解除、カーソルキーでの移動、読み込み完了時の解除で発行する。解除は undefined と -1 */
203
+ (e: 'emit:selectRow', value: T | undefined, index: number): void;
204
+ /** ソートの条件が変わった。sortMode に関わらず発行する */
205
+ (e: 'emit:changeSort', context: DataTableSortContext): void;
206
+ (e: 'emit:updateHeader', headers: DataTableHeader<T>[]): void;
207
+ (e: 'emit:dblclickRow', value: T, index: number): void;
208
+ (e: 'emit:changeLimit', limit: number): void;
209
+ (e: 'emit:changePage', page: number): void;
210
+ (e: 'emit:downloadPage'): void;
211
+ (e: 'emit:downloadAll'): void;
212
+ }
213
+
214
+ /**
215
+ * DataTable のスロット
216
+ */
217
+ export interface Slots<T = unknown> {
218
+ /** 行。tr を rows と同じ順に並べ、tr の click で clickRow(row, index) を呼ぶ */
219
+ default?(props: {
220
+ /** 表示する行。sortMode が page のときはソートした順、それ以外は dataList そのもの */
221
+ rows: T[];
222
+ /** 行を選択する。選択している行なら選択を解除する */
223
+ clickRow: (row: T, index: number) => void;
224
+ /** 行を選択してダブルクリックを通知する */
225
+ dblclickRow: (row: T, index: number) => void;
226
+ /** 行のインデックスから、ページをまたいだ1起点の行番号を返す */
227
+ rowNumber: (index: number) => number;
228
+ /** 選択している行のインデックス。選択していなければ -1 */
229
+ selectedIndex: number;
230
+ }): unknown;
231
+ /** 読み込み中の表示。未指定なら既定のスピナー */
232
+ loading?(): unknown;
233
+ }
234
+
235
+ // 設定の props は未指定を undefined のまま受け、vueCoreOptionsKey の値とライブラリ既定で埋める(boolean が false にならないよう既定を明示する)
236
+ const props = withDefaults(defineProps<Props<T>>(), {
138
237
  loading: false,
139
238
  totalCount: undefined,
140
239
  current: 1,
141
240
  limit: 100,
142
- maxLimit: 9999,
143
- maxPagination: 7,
144
- selectable: true,
145
- controls: 'both',
146
- pagination: 'both',
147
- showDownloadPage: true,
148
- showDownloadAll: true,
149
- showTotal: true,
150
- showLimit: true,
151
- showPageSelect: false,
152
- showNoData: true,
153
- resizable: true,
154
- sortable: true,
155
- sortMode: 'remote',
241
+ maxLimit: undefined,
242
+ maxPagination: undefined,
243
+ selectable: undefined,
244
+ controls: undefined,
245
+ pagination: undefined,
246
+ showDownloadPage: undefined,
247
+ showDownloadAll: undefined,
248
+ showTotal: undefined,
249
+ showLimit: undefined,
250
+ showPageSelect: undefined,
251
+ showNoData: undefined,
252
+ resizable: undefined,
253
+ sortable: undefined,
254
+ sortMode: undefined,
156
255
  formatTotalCount: undefined,
157
256
  contentClass: undefined,
158
257
  contentStyle: undefined,
@@ -161,9 +260,11 @@ const props = withDefaults(defineProps<DataTableProps<T>>(), {
161
260
  labels: undefined
162
261
  });
163
262
 
164
- const emit = defineEmits<DataTableEmits<T>>();
263
+ const emit = defineEmits<Emits<T>>();
264
+
265
+ defineSlots<Slots<T>>();
165
266
 
166
- defineSlots<DataTableSlots<T>>();
267
+ const appOptions = inject(vueCoreOptionsKey, {});
167
268
 
168
269
  const refContent = ref<HTMLDivElement>();
169
270
  const refThead = ref<HTMLTableSectionElement>();
@@ -185,6 +286,45 @@ const innerHeaders = computed((): DataTableHeader<T>[] => {
185
286
  return props.headers.map(header => widths.value[header.id] == null ? header : { ...header, width: widths.value[header.id] });
186
287
  });
187
288
 
289
+ /** 設定のライブラリ既定。labels は項目ごとに合わせるため別に持つ */
290
+ const DEFAULT_SETTINGS: Omit<DataTableOptions, 'labels'> = {
291
+ maxLimit: 9999,
292
+ maxPagination: 7,
293
+ selectable: true,
294
+ controls: 'both',
295
+ pagination: 'both',
296
+ showDownloadPage: true,
297
+ showDownloadAll: true,
298
+ showTotal: true,
299
+ showLimit: true,
300
+ showPageSelect: false,
301
+ showNoData: true,
302
+ resizable: true,
303
+ sortable: true,
304
+ sortMode: 'remote',
305
+ contentClass: undefined,
306
+ contentStyle: undefined,
307
+ tableClass: undefined,
308
+ tableStyle: undefined,
309
+ formatTotalCount: undefined
310
+ };
311
+
312
+ type OptionalSettingKey = 'contentClass' | 'contentStyle' | 'tableClass' | 'tableStyle' | 'formatTotalCount';
313
+ type DataTableSettings = Required<Omit<DataTableOptions, 'labels' | OptionalSettingKey>> & Pick<DataTableOptions, OptionalSettingKey>;
314
+
315
+ /** 設定。props、vueCoreOptionsKey の dataTable、ライブラリ既定の順に、指定のあるものを使う */
316
+ const settings = computed((): DataTableSettings => {
317
+ const resolved = { ...DEFAULT_SETTINGS } as DataTableSettings;
318
+ for (const source of [appOptions.dataTable ?? {}, props]) {
319
+ for (const key of Object.keys(DEFAULT_SETTINGS) as (keyof typeof DEFAULT_SETTINGS)[]) {
320
+ if (source[key] !== undefined) {
321
+ Object.assign(resolved, { [key]: source[key] });
322
+ }
323
+ }
324
+ }
325
+ return resolved;
326
+ });
327
+
188
328
  const labels = computed((): DataTableResolvedLabels => {
189
329
  const merged: Required<DataTableLabels> = {
190
330
  totalCount: 'Total',
@@ -200,6 +340,7 @@ const labels = computed((): DataTableResolvedLabels => {
200
340
  nextPages: 'Next pages',
201
341
  nextPage: 'Next page',
202
342
  lastPage: 'Last page',
343
+ ...appOptions.dataTable?.labels,
203
344
  ...props.labels
204
345
  };
205
346
  const resolved = {} as DataTableResolvedLabels;
@@ -211,22 +352,22 @@ const labels = computed((): DataTableResolvedLabels => {
211
352
  });
212
353
 
213
354
  const limit = computed((): number => {
214
- return Math.min(props.limit, props.maxLimit);
355
+ return Math.min(props.limit, settings.value.maxLimit);
215
356
  });
216
357
 
217
358
  const controlProps = computed(() => {
218
359
  return {
219
- showTotal: props.showTotal,
220
- showLimit: props.showLimit,
221
- showPageSelect: props.showPageSelect,
222
- showDownloadPage: props.showDownloadPage,
223
- showDownloadAll: props.showDownloadAll,
360
+ showTotal: settings.value.showTotal,
361
+ showLimit: settings.value.showLimit,
362
+ showPageSelect: settings.value.showPageSelect,
363
+ showDownloadPage: settings.value.showDownloadPage,
364
+ showDownloadAll: settings.value.showDownloadAll,
224
365
  current: props.current,
225
366
  limit: limit.value,
226
- maxLimit: props.maxLimit,
227
- maxPagination: props.maxPagination,
367
+ maxLimit: settings.value.maxLimit,
368
+ maxPagination: settings.value.maxPagination,
228
369
  totalCount: props.totalCount ?? props.dataList.length,
229
- formatTotalCount: props.formatTotalCount,
370
+ formatTotalCount: settings.value.formatTotalCount,
230
371
  labels: labels.value
231
372
  };
232
373
  });
@@ -234,13 +375,26 @@ const controlProps = computed(() => {
234
375
  /** 選択している行。行のオブジェクトが差し替わっても内容が同じなら選択を保てるよう、行そのものを持つ */
235
376
  const selectedRow = shallowRef<T | undefined>(undefined);
236
377
 
237
- /** 選択している行のインデックス。同じ参照が無ければ内容で探す */
378
+ /** 表示する行。ページ内ソート中は dataList を並べ替えたコピー、それ以外は dataList そのもの */
379
+ const rows = computed((): T[] => {
380
+ const header = settings.value.sortMode === 'page' ? props.headers.find(header => header.id === sortKey.value) : undefined;
381
+ if (!header) {
382
+ return props.dataList;
383
+ }
384
+ const compare = header.compare ?? ((a: T, b: T): number => {
385
+ const key = header.sortKey ?? header.id;
386
+ return compareValues(getValueByPath(a, key), getValueByPath(b, key), header.type ?? 'string');
387
+ });
388
+ return [...props.dataList].sort((a, b) => descending.value ? compare(b, a) : compare(a, b));
389
+ });
390
+
391
+ /** 選択している行の、表示順でのインデックス。同じ参照が無ければ内容で探す */
238
392
  const selectedIndex = computed((): number => {
239
393
  const selected = selectedRow.value;
240
394
  if (selected == null) {
241
395
  return -1;
242
396
  }
243
- const index = props.dataList.indexOf(selected);
397
+ const index = rows.value.indexOf(selected);
244
398
  if (index !== -1) {
245
399
  return index;
246
400
  }
@@ -248,7 +402,7 @@ const selectedIndex = computed((): number => {
248
402
  if (json === '') {
249
403
  return -1;
250
404
  }
251
- return props.dataList.findIndex(row => stringifyValue(row) === json);
405
+ return rows.value.findIndex(row => stringifyValue(row) === json);
252
406
  });
253
407
 
254
408
  /** データが無い行の colspan。colspan を持つヘッダはその数で数える */
@@ -279,7 +433,7 @@ const ariaSort = (id: string): 'ascending' | 'descending' | undefined => {
279
433
  };
280
434
 
281
435
  const caretDirection = (id: string): 'up' | 'down' | undefined => {
282
- if (!props.sortable || sortKey.value !== id) {
436
+ if (!settings.value.sortable || sortKey.value !== id) {
283
437
  return undefined;
284
438
  }
285
439
  return descending.value ? 'down' : 'up';
@@ -289,8 +443,8 @@ const rowNumber = (index: number): number => {
289
443
  return (props.current - 1) * limit.value + index + 1;
290
444
  };
291
445
 
292
- /** スロットが描画した行。データが無い行は含めない */
293
- const rows = (): HTMLTableRowElement[] => {
446
+ /** スロットが描画した行の要素。データが無い行は含めない */
447
+ const rowElements = (): HTMLTableRowElement[] => {
294
448
  return Array.from(refTbody.value?.children ?? [])
295
449
  .filter((el): el is HTMLTableRowElement => el.tagName === 'TR' && !el.classList.contains('dwui-data-table-no-data'));
296
450
  };
@@ -308,7 +462,7 @@ const emitHeaders = (): void => {
308
462
  * 行はスロットで描画されるため、描画後の DOM に対してここで反映する
309
463
  */
310
464
  const syncRows = (): void => {
311
- rows().forEach((tr, index) => tr.classList.toggle('dwui-selected', index === selectedIndex.value));
465
+ rowElements().forEach((tr, index) => tr.classList.toggle('dwui-selected', index === selectedIndex.value));
312
466
  };
313
467
 
314
468
  /**
@@ -316,7 +470,7 @@ const syncRows = (): void => {
316
470
  */
317
471
  const scrollToSelectedRow = (): void => {
318
472
  const pane = refContent.value;
319
- const tr = rows()[selectedIndex.value];
473
+ const tr = rowElements()[selectedIndex.value];
320
474
  if (!pane || !tr) {
321
475
  return;
322
476
  }
@@ -342,7 +496,7 @@ const select = (row: T | undefined, index: number): void => {
342
496
  * 行を選択する。選択している行なら選択を解除する
343
497
  */
344
498
  const clickRow = (row: T, index: number): void => {
345
- if (!props.selectable) {
499
+ if (!settings.value.selectable) {
346
500
  return;
347
501
  }
348
502
  if (index === selectedIndex.value) {
@@ -373,15 +527,15 @@ const onKeyDown = (event: KeyboardEvent): void => {
373
527
  return;
374
528
  }
375
529
  const index = selectedIndex.value + (event.key === 'ArrowUp' ? -1 : 1);
376
- if (index < 0 || props.dataList.length <= index) {
530
+ if (index < 0 || rows.value.length <= index) {
377
531
  return;
378
532
  }
379
533
  event.preventDefault();
380
- select(props.dataList[index], index);
534
+ select(rows.value[index], index);
381
535
  };
382
536
 
383
537
  const onClickSort = (header: DataTableHeader<T>): void => {
384
- if (!props.sortable || props.dataList.length === 0) {
538
+ if (!settings.value.sortable || props.dataList.length === 0) {
385
539
  return;
386
540
  }
387
541
  descending.value = sortKey.value === header.id ? !descending.value : false;
@@ -390,22 +544,13 @@ const onClickSort = (header: DataTableHeader<T>): void => {
390
544
  sortKey: sortKey.value,
391
545
  descending: descending.value
392
546
  });
393
- if (props.sortMode !== 'page') {
394
- return;
395
- }
396
- const compare = header.compare ?? ((a: T, b: T): number => {
397
- const key = header.sortKey ?? header.id;
398
- return compareValues(getValueByPath(a, key), getValueByPath(b, key), header.type ?? 'string');
399
- });
400
- const sorted = [...props.dataList].sort((a, b) => descending.value ? compare(b, a) : compare(a, b));
401
- emit('emit:sortData', sorted);
402
547
  };
403
548
 
404
549
  /**
405
550
  * 列幅の変更を始める。すべての列の幅を今の表示幅で固定してから変更する
406
551
  */
407
552
  const onResizeStart = (event: PointerEvent, index: number): void => {
408
- if (!props.resizable) {
553
+ if (!settings.value.resizable) {
409
554
  return;
410
555
  }
411
556
  event.preventDefault();
@@ -473,7 +618,7 @@ watch(() => props.loading, (newValue, oldValue) => {
473
618
  // 読み込みが終わったら、前のデータに対する選択は引き継がない。
474
619
  // ページ内ソートも同様だが、remote のソート条件は再取得後も有効なので保つ
475
620
  if (oldValue && !newValue) {
476
- if (props.sortMode === 'page') {
621
+ if (settings.value.sortMode === 'page') {
477
622
  sortKey.value = '';
478
623
  descending.value = false;
479
624
  }
@@ -49,6 +49,11 @@ export interface ShowFloatingComponentOptions {
49
49
  onClose?: () => void;
50
50
  }
51
51
 
52
+ export interface ShowFloatingMessageOptions extends ShowFloatingComponentOptions {
53
+ /** trueを指定した場合、文字列をHTMLとして表示する。オブジェクトには影響しない */
54
+ html?: boolean;
55
+ }
56
+
52
57
  const openedHandles = new Set<FloatingComponentHandle>();
53
58
 
54
59
  /**
@@ -88,8 +93,11 @@ const toMessageText = (message: string | object): string => {
88
93
  return typeof message === 'string' ? message : JSON.stringify(message, null, 2);
89
94
  };
90
95
 
91
- const renderMessage = (text: string) => (): VNode => {
92
- return h('div', { class: 'dwui-floating-component-message' }, text);
96
+ const renderMessage = (message: string | object, html: boolean | undefined) => (): VNode => {
97
+ if (html && typeof message === 'string') {
98
+ return h('div', { class: 'dwui-floating-component-message', innerHTML: message });
99
+ }
100
+ return h('div', { class: 'dwui-floating-component-message' }, toMessageText(message));
93
101
  };
94
102
 
95
103
  /**
@@ -108,19 +116,19 @@ export function showFloatingComponent(component: Component, props?: Record<strin
108
116
 
109
117
  /**
110
118
  * FloatingComponent を body の末尾に描画し、中身に文字列を表示する。
111
- * 文字列は HTML として解釈せず、改行や空白をそのまま表示する。オブジェクトは整形した JSON で表示する。
112
- * 見出しのコピーボタンは、copyValue を指定しなければ表示した文字列をコピーする。
119
+ * 文字列は改行や空白をそのまま表示し、html を指定すれば HTML として表示する。オブジェクトは整形した JSON で表示する。
120
+ * 見出しのコピーボタンは、copyValue を指定しなければ渡した文字列をコピーする。
113
121
  * ブラウザでのみ動作する
114
122
  *
115
123
  * @param {string | object} message 表示する文字列またはオブジェクト
116
- * @param {ShowFloatingComponentOptions} [options]
124
+ * @param {ShowFloatingMessageOptions} [options]
117
125
  * @returns {FloatingComponentHandle}
118
126
  */
119
- export function showFloatingMessage(message: string | object, options?: ShowFloatingComponentOptions): FloatingComponentHandle {
120
- const text = toMessageText(message);
121
- return mountFloatingComponent(renderMessage(text), {
122
- copyValue: text,
123
- ...options
127
+ export function showFloatingMessage(message: string | object, options?: ShowFloatingMessageOptions): FloatingComponentHandle {
128
+ const { html, ...windowOptions } = options ?? {};
129
+ return mountFloatingComponent(renderMessage(message, html), {
130
+ copyValue: toMessageText(message),
131
+ ...windowOptions
124
132
  });
125
133
  }
126
134
 
@@ -159,10 +167,10 @@ export function useFloatingComponent(): {
159
167
  return owner.own((onClosed) => mountFloatingComponent(renderComponent(component, props), withOwner(options), onClosed));
160
168
  },
161
169
  showFloatingMessage: (message, options) => {
162
- const text = toMessageText(message);
163
- return owner.own((onClosed) => mountFloatingComponent(renderMessage(text), withOwner({
164
- copyValue: text,
165
- ...options
170
+ const { html, ...windowOptions } = options ?? {};
171
+ return owner.own((onClosed) => mountFloatingComponent(renderMessage(message, html), withOwner({
172
+ copyValue: toMessageText(message),
173
+ ...windowOptions
166
174
  }), onClosed));
167
175
  }
168
176
  };
@@ -0,0 +1,20 @@
1
+ import type { InjectionKey } from 'vue';
2
+ import type { Props } from '@/data-table.vue';
3
+
4
+ /**
5
+ * DataTable のアプリ既定の設定。vueCoreOptionsKey の dataTable で渡す。
6
+ * props のうちデータと表示の状態を除いた項目を持ち、DataTable の props に同じ名前の指定があればそちらが優先される
7
+ */
8
+ export type DataTableOptions = Partial<Omit<Props, 'dataList' | 'headers' | 'loading' | 'totalCount' | 'current' | 'limit'>>;
9
+
10
+ /** アプリ全体の設定 */
11
+ export type VueCoreOptions = {
12
+ /** DataTable のアプリ既定の設定 */
13
+ dataTable?: DataTableOptions;
14
+ };
15
+
16
+ /**
17
+ * アプリ全体の設定を provide/inject するためのキー
18
+ * provide は任意。画面の上位または app.provide で使う
19
+ */
20
+ export const vueCoreOptionsKey: InjectionKey<VueCoreOptions> = Symbol('dwui-vue-core-options');
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ export * from '@/components';
2
2
  export type { Props as AccordionFieldsetProps, Slots as AccordionFieldsetSlots } from '@/accordion-fieldset.vue';
3
3
  export type { Props as AccordionSectionProps, Slots as AccordionSectionSlots } from '@/accordion-section.vue';
4
4
  export type { Props as CopyButtonProps, Emits as CopyButtonEmits, Slots as CopyButtonSlots } from '@/copy-button.vue';
5
+ export type { Props as DataTableProps, Emits as DataTableEmits, Slots as DataTableSlots } from '@/data-table.vue';
5
6
  export type { Props as DarkModeSelectorProps, Emits as DarkModeSelectorEmits, Slots as DarkModeSelectorSlots } from '@/dark-mode-selector.vue';
6
7
  export type { Props as HelpButtonProps, Emits as HelpButtonEmits, Slots as HelpButtonSlots } from '@/help-button.vue';
7
8
  export type { Props as HelpPopperProps, Emits as HelpPopperEmits, Slots as HelpPopperSlots } from '@/help-popper.vue';
@@ -18,9 +19,9 @@ export type { Props as TabContentProps, Slots as TabContentSlots } from '@/tab-c
18
19
  export type { Props as TabGroupProps, Slots as TabGroupSlots } from '@/tab-group.vue';
19
20
  export type { Props as WarningPopperProps, Emits as WarningPopperEmits, Slots as WarningPopperSlots } from '@/warning-popper.vue';
20
21
  export { headersToCsvColumns, rowToCsvColumns } from '@/helpers/data-table-csv';
21
- export type { DataTableControlsPlacement, DataTableCsvColumn, DataTableEmits, DataTableHeader, DataTableLabels, DataTableProps, DataTableResolvedLabels, DataTableSlots, DataTableSortContext, DataTableSortMode, DataTableValueType } from '@/types/data-table';
22
+ export type { DataTableControlsPlacement, DataTableCsvColumn, DataTableHeader, DataTableLabels, DataTableResolvedLabels, DataTableSortContext, DataTableSortMode, DataTableValueType } from '@/types/data-table';
22
23
  export { closeAllFloatingComponents, showFloatingComponent, showFloatingMessage, useFloatingComponent } from '@/helpers/show-floating-component';
23
- export type { FloatingComponentContentProps, FloatingComponentHandle, ShowFloatingComponentOptions } from '@/helpers/show-floating-component';
24
+ export type { FloatingComponentContentProps, FloatingComponentHandle, ShowFloatingComponentOptions, ShowFloatingMessageOptions } from '@/helpers/show-floating-component';
24
25
  export { showConfirmationDialog, showErrorDialog, showInformationDialog, showModalDialog, showWarningDialog } from '@/helpers/show-modal-dialog';
25
26
  export type { ShowModalDialogOptions } from '@/helpers/show-modal-dialog';
26
27
  export type { ModalDialogButton, ModalDialogImage, ModalDialogResult, ModalDialogType } from '@/types/modal-dialog';
@@ -34,4 +35,6 @@ export type { SnackbarDirection, SnackbarPlacement } from '@/internal/snackbar-i
34
35
  export type { LabelPreset } from '@/types/label-preset';
35
36
  export type { MovableWindowContentProps, MovableWindowDragMode, MovableWindowHandle } from '@/types/movable-window';
36
37
  export type { TabContentStatus } from '@/internal/tab-group-context';
38
+ export { vueCoreOptionsKey } from '@/helpers/vue-core-options';
39
+ export type { DataTableOptions, VueCoreOptions } from '@/helpers/vue-core-options';
37
40
  export type { PopoverPlacement } from '@/internal/anchored-position';