@digitalwalletcorp/vue-core 1.2.0 → 1.3.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.
Files changed (188) hide show
  1. package/README.md +95 -8
  2. package/lib/accordion-fieldset.d.cts +14 -4
  3. package/lib/accordion-fieldset.d.ts +14 -4
  4. package/lib/accordion-section.d.cts +20 -10
  5. package/lib/accordion-section.d.ts +20 -10
  6. package/lib/components-2zQ2SZuY.js +26 -0
  7. package/lib/components-HaiHcseF.cjs +1 -0
  8. package/lib/components.d.cts +10 -0
  9. package/lib/components.d.ts +10 -0
  10. package/lib/copy-button.d.cts +34 -0
  11. package/lib/copy-button.d.ts +34 -0
  12. package/lib/dark-mode-selector.d.cts +32 -0
  13. package/lib/dark-mode-selector.d.ts +32 -0
  14. package/lib/data-table.d.cts +30 -0
  15. package/lib/data-table.d.ts +30 -0
  16. package/lib/help-button.d.cts +39 -0
  17. package/lib/help-button.d.ts +39 -0
  18. package/lib/help-popper.d.cts +39 -0
  19. package/lib/help-popper.d.ts +39 -0
  20. package/lib/helpers/data-table-csv.d.cts +17 -0
  21. package/lib/helpers/data-table-csv.d.ts +17 -0
  22. package/lib/helpers/show-floating-component.d.cts +82 -0
  23. package/lib/helpers/show-floating-component.d.ts +82 -0
  24. package/lib/helpers/show-modal-dialog.d.cts +73 -0
  25. package/lib/helpers/show-modal-dialog.d.ts +73 -0
  26. package/lib/helpers/show-overlay-box.d.cts +53 -0
  27. package/lib/helpers/show-overlay-box.d.ts +53 -0
  28. package/lib/helpers/show-screen-loading.d.cts +20 -0
  29. package/lib/helpers/show-screen-loading.d.ts +20 -0
  30. package/lib/helpers/show-snackbar.d.cts +47 -0
  31. package/lib/helpers/show-snackbar.d.ts +47 -0
  32. package/lib/hourglass-loading.d.cts +1 -1
  33. package/lib/hourglass-loading.d.ts +1 -1
  34. package/lib/index.cjs +1 -1
  35. package/lib/index.d.cts +34 -0
  36. package/lib/index.d.ts +34 -0
  37. package/lib/index.js +445 -18
  38. package/lib/internal/anchored-popover.d.cts +35 -0
  39. package/lib/internal/anchored-popover.d.ts +35 -0
  40. package/lib/internal/anchored-position.d.cts +50 -0
  41. package/lib/internal/anchored-position.d.ts +50 -0
  42. package/lib/internal/data-table-controls.d.cts +42 -0
  43. package/lib/internal/data-table-controls.d.ts +42 -0
  44. package/lib/internal/data-table-value.d.cts +32 -0
  45. package/lib/internal/data-table-value.d.ts +32 -0
  46. package/lib/internal/floating-component.d.cts +52 -0
  47. package/lib/internal/floating-component.d.ts +52 -0
  48. package/lib/internal/icon-popper.d.cts +32 -0
  49. package/lib/internal/icon-popper.d.ts +32 -0
  50. package/lib/internal/mount-window.d.cts +39 -0
  51. package/lib/internal/mount-window.d.ts +39 -0
  52. package/lib/internal/movable-window.d.cts +55 -0
  53. package/lib/internal/movable-window.d.ts +55 -0
  54. package/lib/internal/opened-overlay-boxes.d.cts +2 -0
  55. package/lib/internal/opened-overlay-boxes.d.ts +2 -0
  56. package/lib/internal/pagination.d.cts +22 -0
  57. package/lib/internal/pagination.d.ts +22 -0
  58. package/lib/internal/snackbar-container.d.cts +20 -0
  59. package/lib/internal/snackbar-container.d.ts +20 -0
  60. package/lib/internal/snackbar-item.d.cts +17 -0
  61. package/lib/internal/snackbar-item.d.ts +17 -0
  62. package/lib/internal/tab-group-context.d.cts +4 -2
  63. package/lib/internal/tab-group-context.d.ts +4 -2
  64. package/lib/internal/use-movable-window.d.cts +44 -0
  65. package/lib/internal/use-movable-window.d.ts +44 -0
  66. package/lib/internal/window-order.d.cts +6 -0
  67. package/lib/internal/window-order.d.ts +6 -0
  68. package/lib/intersection-lazy-component.d.cts +32 -0
  69. package/lib/intersection-lazy-component.d.ts +32 -0
  70. package/lib/label-button.d.cts +2 -2
  71. package/lib/label-button.d.ts +2 -2
  72. package/lib/label-icon.d.cts +2 -2
  73. package/lib/label-icon.d.ts +2 -2
  74. package/lib/modal-dialog.d.cts +67 -0
  75. package/lib/modal-dialog.d.ts +67 -0
  76. package/lib/nuxt.cjs +1 -1
  77. package/lib/nuxt.js +1 -1
  78. package/lib/overlay-box.d.cts +52 -0
  79. package/lib/overlay-box.d.ts +52 -0
  80. package/lib/page-section.d.cts +2 -2
  81. package/lib/page-section.d.ts +2 -2
  82. package/lib/page-title.d.cts +2 -2
  83. package/lib/page-title.d.ts +2 -2
  84. package/lib/register.cjs +1 -1
  85. package/lib/register.js +1 -1
  86. package/lib/screen-loading.d.cts +7 -0
  87. package/lib/screen-loading.d.ts +7 -0
  88. package/lib/style.css +1 -1
  89. package/lib/tab-content.d.cts +5 -5
  90. package/lib/tab-content.d.ts +5 -5
  91. package/lib/tab-group.d.cts +7 -10
  92. package/lib/tab-group.d.ts +7 -10
  93. package/lib/types/data-table.d.cts +185 -0
  94. package/lib/types/data-table.d.ts +185 -0
  95. package/lib/types/modal-dialog.d.cts +13 -0
  96. package/lib/types/modal-dialog.d.ts +13 -0
  97. package/lib/types/movable-window.d.cts +16 -0
  98. package/lib/types/movable-window.d.ts +16 -0
  99. package/lib/warning-popper.d.cts +39 -0
  100. package/lib/warning-popper.d.ts +39 -0
  101. package/lib/warning-popper.vue_vue_type_style_index_0_lang-CxVzqsbn.cjs +1 -0
  102. package/lib/warning-popper.vue_vue_type_style_index_0_lang-zt-jCsvz.js +1833 -0
  103. package/package.json +43 -16
  104. package/src/accordion-fieldset.vue +29 -19
  105. package/src/accordion-section.vue +41 -26
  106. package/src/components.ts +10 -0
  107. package/src/copy-button.vue +89 -0
  108. package/src/dark-mode-selector.vue +212 -0
  109. package/src/data-table.vue +643 -0
  110. package/src/help-button.vue +128 -0
  111. package/src/help-popper.vue +70 -0
  112. package/src/helpers/data-table-csv.ts +55 -0
  113. package/src/helpers/show-floating-component.ts +169 -0
  114. package/src/helpers/show-modal-dialog.ts +121 -0
  115. package/src/helpers/show-overlay-box.ts +106 -0
  116. package/src/helpers/show-screen-loading.ts +37 -0
  117. package/src/helpers/show-snackbar.ts +153 -0
  118. package/src/hourglass-loading.vue +1 -1
  119. package/src/index.ts +34 -0
  120. package/src/internal/anchored-popover.vue +192 -0
  121. package/src/internal/anchored-position.ts +135 -0
  122. package/src/internal/data-table-controls.vue +203 -0
  123. package/src/internal/data-table-value.ts +99 -0
  124. package/src/internal/floating-component.vue +202 -0
  125. package/src/internal/icon-popper.vue +185 -0
  126. package/src/internal/mount-window.ts +110 -0
  127. package/src/internal/movable-window.vue +315 -0
  128. package/src/internal/opened-overlay-boxes.ts +2 -0
  129. package/src/internal/pagination.vue +325 -0
  130. package/src/internal/snackbar-container.vue +258 -0
  131. package/src/internal/snackbar-item.ts +20 -0
  132. package/src/internal/tab-group-context.ts +4 -2
  133. package/src/internal/use-movable-window.ts +208 -0
  134. package/src/internal/window-order.ts +11 -0
  135. package/src/intersection-lazy-component.vue +84 -0
  136. package/src/label-button.vue +37 -3
  137. package/src/label-icon.vue +2 -2
  138. package/src/modal-dialog.vue +287 -0
  139. package/src/overlay-box.vue +218 -0
  140. package/src/page-section.vue +7 -7
  141. package/src/page-title.vue +5 -5
  142. package/src/screen-loading.vue +90 -0
  143. package/src/tab-content.vue +17 -16
  144. package/src/tab-group.vue +55 -46
  145. package/src/types/data-table.ts +196 -0
  146. package/src/types/modal-dialog.ts +16 -0
  147. package/src/types/movable-window.ts +18 -0
  148. package/src/warning-popper.vue +70 -0
  149. package/lib/accordion-fieldset.cjs +0 -1
  150. package/lib/accordion-fieldset.js +0 -4
  151. package/lib/accordion-fieldset.vue_vue_type_style_index_0_lang-n-IZxUiu.js +0 -66
  152. package/lib/accordion-fieldset.vue_vue_type_style_index_0_lang-t9NqmoZW.cjs +0 -1
  153. package/lib/accordion-section.cjs +0 -1
  154. package/lib/accordion-section.js +0 -4
  155. package/lib/accordion-section.vue_vue_type_style_index_0_lang-CFDdUEnn.cjs +0 -1
  156. package/lib/accordion-section.vue_vue_type_style_index_0_lang-Gvjjx02P.js +0 -78
  157. package/lib/components-CNOeuZPu.cjs +0 -1
  158. package/lib/components-DBK8NUBh.js +0 -24
  159. package/lib/hourglass-loading.cjs +0 -1
  160. package/lib/hourglass-loading.js +0 -4
  161. package/lib/hourglass-loading.vue_vue_type_style_index_0_lang-C72UVA9W.cjs +0 -1
  162. package/lib/hourglass-loading.vue_vue_type_style_index_0_lang-DkUGIxQ2.js +0 -22
  163. package/lib/label-button.cjs +0 -1
  164. package/lib/label-button.js +0 -4
  165. package/lib/label-button.vue_vue_type_script_setup_true_lang-BwVOxaJP.cjs +0 -1
  166. package/lib/label-button.vue_vue_type_script_setup_true_lang-DorC4pKz.js +0 -44
  167. package/lib/label-icon.cjs +0 -1
  168. package/lib/label-icon.js +0 -4
  169. package/lib/label-icon.vue_vue_type_style_index_0_lang-D9Cimu20.cjs +0 -1
  170. package/lib/label-icon.vue_vue_type_style_index_0_lang-me52HE2V.js +0 -38
  171. package/lib/page-section.cjs +0 -1
  172. package/lib/page-section.js +0 -4
  173. package/lib/page-section.vue_vue_type_style_index_0_lang-DEAkTLxd.cjs +0 -1
  174. package/lib/page-section.vue_vue_type_style_index_0_lang-DpVQe_rV.js +0 -54
  175. package/lib/page-title.cjs +0 -1
  176. package/lib/page-title.js +0 -4
  177. package/lib/page-title.vue_vue_type_style_index_0_lang-BqIsIks5.cjs +0 -1
  178. package/lib/page-title.vue_vue_type_style_index_0_lang-ChZot85K.js +0 -36
  179. package/lib/tab-content.cjs +0 -1
  180. package/lib/tab-content.js +0 -4
  181. package/lib/tab-content.vue_vue_type_style_index_0_lang-BeCi7Xkp.cjs +0 -1
  182. package/lib/tab-content.vue_vue_type_style_index_0_lang-CcQLHmIF.js +0 -44
  183. package/lib/tab-group.cjs +0 -1
  184. package/lib/tab-group.js +0 -4
  185. package/lib/tab-group.vue_vue_type_style_index_0_lang-C84mDEOC.js +0 -103
  186. package/lib/tab-group.vue_vue_type_style_index_0_lang-CMaufRJV.cjs +0 -1
  187. package/lib/use-shrinkable-CS2bmsJJ.js +0 -17
  188. package/lib/use-shrinkable-Cu9JQZll.cjs +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalwalletcorp/vue-core",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Basic UI components for Vue 3",
6
6
  "main": "lib/index.cjs",
@@ -37,22 +37,15 @@
37
37
  "./global": {
38
38
  "types": "./lib/global.d.ts"
39
39
  },
40
- "./style.css": "./lib/style.css",
41
- "./*": {
42
- "import": {
43
- "types": "./lib/*.d.ts",
44
- "default": "./lib/*.js"
45
- },
46
- "require": {
47
- "types": "./lib/*.d.cts",
48
- "default": "./lib/*.cjs"
49
- }
50
- }
40
+ "./style.css": "./lib/style.css"
51
41
  },
52
42
  "scripts": {
53
43
  "build": "vite build",
44
+ "yarn:dedupe": "npx yarn-deduplicate --strategy highest yarn.lock",
54
45
  "install:dev": "rm -rf lib dest && find node_modules -mindepth 1 -delete && yarn install",
55
- "typecheck": "vue-tsc --noEmit -p tsconfig.json",
46
+ "tsc": "vue-tsc --noEmit -p tsconfig.json",
47
+ "lint": "eslint . && stylelint \"src/**/*.vue\" \"src/**/*.css\"",
48
+ "lint:fix": "eslint --fix . && stylelint --fix \"**/*.vue\" \"**/*.css\"",
56
49
  "version:patch": "npm version patch",
57
50
  "version:minor": "npm version minor",
58
51
  "version:major": "npm version major",
@@ -68,7 +61,7 @@
68
61
  "components",
69
62
  "ui"
70
63
  ],
71
- "author": "satoshi kotaki",
64
+ "author": "Digital Wallet Corporation",
72
65
  "license": "MIT",
73
66
  "repository": {
74
67
  "type": "git",
@@ -83,24 +76,58 @@
83
76
  "src/**/*"
84
77
  ],
85
78
  "peerDependencies": {
86
- "@digitalwalletcorp/vue-svg-icons": "^1.13.0",
79
+ "@digitalwalletcorp/vue-svg-icons": "^1.15.3",
80
+ "@nuxt/kit": "^3.17.5",
87
81
  "vue": "^3.5.29"
88
82
  },
83
+ "peerDependenciesMeta": {
84
+ "@nuxt/kit": {
85
+ "optional": true
86
+ }
87
+ },
89
88
  "devDependencies": {
90
- "@digitalwalletcorp/vue-svg-icons": "^1.13.0",
89
+ "@digitalwalletcorp/vue-svg-icons": "^1.15.3",
91
90
  "@nuxt/kit": "^3.17.5",
91
+ "@stylistic/eslint-plugin": "5.10.0",
92
+ "@stylistic/stylelint-plugin": "5.3.1",
92
93
  "@types/node": "24.13.6",
93
94
  "@vitejs/plugin-vue": "^6.0.0",
94
95
  "@vitest/coverage-v8": "3.2.4",
96
+ "@vue/compiler-core": "3.5.43",
95
97
  "@vue/test-utils": "2.4.6",
98
+ "eslint": "10.11.0",
99
+ "eslint-plugin-vue": "10.11.1",
100
+ "globals": "17.13.0",
96
101
  "happy-dom": "20.14.5",
97
102
  "nuxt": "^3.17.5",
103
+ "postcss-html": "2.0.0",
104
+ "stylelint": "17.16.0",
105
+ "stylelint-config-recommended-vue": "2.0.0",
106
+ "stylelint-config-standard": "40.0.0",
98
107
  "typescript": "6.0.3",
108
+ "typescript-eslint": "8.71.0",
99
109
  "vite": "^7.0.0",
100
110
  "vite-plugin-dts": "5.1.0",
101
111
  "vite-tsconfig-paths": "6.1.1",
102
112
  "vitest": "3.2.4",
103
113
  "vue": "^3.5.29",
114
+ "vue-eslint-parser": "10.4.1",
104
115
  "vue-tsc": "3.1.5"
116
+ },
117
+ "resolutions": {
118
+ "eslint": "10.11.0",
119
+ "eslint-plugin-vue": "10.11.1",
120
+ "typescript-eslint": "8.71.0",
121
+ "@stylistic/eslint-plugin": "5.10.0",
122
+ "globals": "17.13.0",
123
+ "vue-eslint-parser": "10.4.1",
124
+ "stylelint": "17.16.0",
125
+ "@stylistic/stylelint-plugin": "5.3.1",
126
+ "stylelint-config-standard": "40.0.0",
127
+ "stylelint-config-recommended-vue": "2.0.0",
128
+ "stylelint-config-recommended": "18.0.0",
129
+ "stylelint-config-html": "2.0.0",
130
+ "postcss-html": "2.0.0",
131
+ "postcss": "8.5.28"
105
132
  }
106
133
  }
@@ -6,14 +6,14 @@
6
6
  class="dwui-accordion-fieldset"
7
7
  :disabled="props.disabled"
8
8
  >
9
- <legend>
9
+ <legend class="dwui-accordion-legend">
10
10
  <button
11
11
  type="button"
12
12
  class="dwui-accordion-toggle"
13
- :aria-expanded="pageContext.expanded"
13
+ :aria-expanded="expanded"
14
14
  @click="onClickToggle"
15
15
  >
16
- <SvgTriangleButton :direction="pageContext.expanded ? 'up' : 'down'" />
16
+ <SvgTriangleButton :direction="expanded ? 'up' : 'down'" />
17
17
  <LabelIcon v-if="props.legend != null || slots.legend != null || slots.icon != null">
18
18
  <!-- 渡されたスロットだけを中継する。空のラベルを渡すとアイコンの後ろに間隔が空くため -->
19
19
  <template
@@ -33,7 +33,7 @@
33
33
  </legend>
34
34
  <div
35
35
  ref="refContents"
36
- :class="[ 'dwui-accordion-contents', shrinkable ? pageContext.expanded ? 'dwui-expanded' : 'dwui-shrunk' : '' ]"
36
+ :class="[ 'dwui-accordion-contents', shrinkable ? expanded ? 'dwui-expanded' : 'dwui-shrunk' : '' ]"
37
37
  >
38
38
  <slot />
39
39
  </div>
@@ -41,22 +41,22 @@
41
41
  </template>
42
42
 
43
43
  <script setup lang="ts">
44
- import { onMounted, reactive, ref } from 'vue';
44
+ import { computed, ref } from 'vue';
45
45
  import { SvgTriangleButton } from '@digitalwalletcorp/vue-svg-icons';
46
46
  import LabelIcon from '@/label-icon.vue';
47
47
  import { useShrinkable } from '@/internal/use-shrinkable';
48
48
 
49
- interface Props {
49
+ export interface Props {
50
50
  /** 凡例の文字。HTMLを含める場合は#legendスロットを使う */
51
51
  legend?: string;
52
52
  /** trueの場合、fieldsetを無効にする */
53
53
  disabled?: boolean;
54
- /** trueの場合、開いた状態で表示する デフォルト false */
54
+ /** trueの場合、開いた状態で表示する デフォルト false。v-model:expanded で束縛しないときの初期値 */
55
55
  initialExpand?: boolean;
56
56
  }
57
57
  const props = defineProps<Props>();
58
58
 
59
- interface Slots {
59
+ export interface Slots {
60
60
  /** 凡例の文字の前に表示するアイコン */
61
61
  icon?(): unknown;
62
62
  /** 凡例。legendより優先する */
@@ -66,18 +66,23 @@ interface Slots {
66
66
  const slots = defineSlots<Slots>();
67
67
 
68
68
  const refContents = ref<HTMLElement>();
69
- const pageContext = reactive({
70
- expanded: true
69
+ // 真偽値の prop は未指定だと false に変換されるため、未指定を undefined のまま受けるよう既定値を明示する
70
+ /** 親が v-model:expanded で束縛したときの開閉状態。束縛しなければ undefined */
71
+ const model = defineModel<boolean | undefined>('expanded', { default: undefined });
72
+ /** 束縛しないときの開閉状態 */
73
+ const localExpanded = ref(!!props.initialExpand);
74
+ const expanded = computed({
75
+ get: (): boolean => model.value ?? localExpanded.value,
76
+ set: (value: boolean): void => {
77
+ localExpanded.value = value;
78
+ model.value = value;
79
+ }
71
80
  });
72
81
  const shrinkable = useShrinkable(refContents);
73
82
 
74
83
  const onClickToggle = () => {
75
- pageContext.expanded = !pageContext.expanded;
84
+ expanded.value = !expanded.value;
76
85
  };
77
-
78
- onMounted(() => {
79
- pageContext.expanded = !!props.initialExpand;
80
- });
81
86
  </script>
82
87
 
83
88
  <style>
@@ -85,12 +90,12 @@ onMounted(() => {
85
90
  position: relative;
86
91
  }
87
92
 
88
- /* 詳細度0のため、利用側がbutton要素に装飾を当てている場合はそちらが優先される。打ち消しは利用側のCSSで行う */
93
+ /* 開閉ボタンは押しボタンではなく凡例と一体の操作として見せるため、既定では枠・背景を持たない */
89
94
  :where(.dwui-accordion-toggle) {
90
95
  display: inline-flex;
91
96
  align-items: center;
92
- gap: 4px;
93
- margin-left: 4px;
97
+ gap: 0.33em;
98
+ margin-left: 0.33em;
94
99
  padding: 0;
95
100
  border: none;
96
101
  background: none;
@@ -103,10 +108,15 @@ onMounted(() => {
103
108
  cursor: not-allowed;
104
109
  }
105
110
 
111
+ :where(.dwui-accordion-toggle:focus-visible) {
112
+ outline: var(--dwui-outline-width-focus-ring, 2px) solid var(--dwui-outline-color-focus-ring, Highlight);
113
+ outline-offset: var(--dwui-outline-offset-focus-ring, 2px);
114
+ }
115
+
106
116
  :where(.dwui-accordion-contents) {
107
117
  position: relative;
108
118
  width: fit-content;
109
- padding: 2px;
119
+ padding: 0.17em;
110
120
  overflow: hidden;
111
121
  opacity: 1;
112
122
  }
@@ -7,7 +7,7 @@
7
7
  ref="refContents"
8
8
  class="dwui-accordion-section-contents"
9
9
  :class="[
10
- shrinkable ? pageContext.expanded ? 'dwui-expanded' : 'dwui-shrunk' : '',
10
+ shrinkable ? expanded ? 'dwui-expanded' : 'dwui-shrunk' : '',
11
11
  { 'dwui-no-gradient': props.noGradient }
12
12
  ]"
13
13
  >
@@ -21,7 +21,7 @@
21
21
  type="button"
22
22
  class="dwui-accordion-section-toggle"
23
23
  :disabled="props.disabled"
24
- :aria-expanded="pageContext.expanded"
24
+ :aria-expanded="expanded"
25
25
  @click.stop="onClickToggle"
26
26
  >
27
27
  <LabelIcon v-if="toggleIcon() != null || toggleLabel != null">
@@ -43,7 +43,7 @@
43
43
  </LabelIcon>
44
44
  <SvgTriangleButton
45
45
  v-else
46
- :direction="pageContext.expanded ? 'up' : 'down'"
46
+ :direction="expanded ? 'up' : 'down'"
47
47
  />
48
48
  </button>
49
49
  </div>
@@ -51,7 +51,7 @@
51
51
  </template>
52
52
 
53
53
  <script setup lang="ts">
54
- import { computed, onMounted, reactive, ref } from 'vue';
54
+ import { computed, ref } from 'vue';
55
55
  import type { Component, Slot } from 'vue';
56
56
  import { SvgTriangleButton } from '@digitalwalletcorp/vue-svg-icons';
57
57
  import LabelIcon from '@/label-icon.vue';
@@ -60,40 +60,49 @@ import type { LabelPreset } from '@/types/label-preset';
60
60
 
61
61
  // 開閉ボタンのアイコンとラベルは別々に決める。どちらも、スロット/ラベルの指定 > プリセット の順で使う
62
62
  // 何も指定が無い場合だけ、既定の三角を表示する
63
- interface Props {
63
+ export interface Props {
64
64
  /** trueの場合、開閉ボタンを無効にする */
65
65
  disabled?: boolean;
66
- /** trueの場合、開いた状態で表示する デフォルト false */
66
+ /** trueの場合、開いた状態で表示する デフォルト false。v-model:expanded で束縛しないときの初期値 */
67
67
  initialExpand?: boolean;
68
68
  /** trueの場合、縮めたときに中身の下側をぼかさない */
69
69
  noGradient?: boolean;
70
70
  /** 縮めているときの開閉ボタン(開く)のラベル */
71
- openLabel?: string;
71
+ expandLabel?: string;
72
72
  /** 開いているときの開閉ボタン(閉じる)のラベル */
73
- closeLabel?: string;
73
+ collapseLabel?: string;
74
74
  /** 縮めているときの開閉ボタン(開く)のアイコンとラベル */
75
- openPreset?: LabelPreset;
75
+ expandPreset?: LabelPreset;
76
76
  /** 開いているときの開閉ボタン(閉じる)のアイコンとラベル */
77
- closePreset?: LabelPreset;
77
+ collapsePreset?: LabelPreset;
78
78
  }
79
79
  const props = defineProps<Props>();
80
80
 
81
- interface Slots {
81
+ export interface Slots {
82
82
  /** 縮めているときの開閉ボタン(開く)のアイコン */
83
- 'open-icon'?(): unknown;
83
+ 'expand-icon'?(): unknown;
84
84
  /** 開いているときの開閉ボタン(閉じる)のアイコン */
85
- 'close-icon'?(): unknown;
85
+ 'collapse-icon'?(): unknown;
86
86
  default?(): unknown;
87
87
  }
88
88
  const slots = defineSlots<Slots>();
89
89
 
90
90
  const refContents = ref<HTMLElement>();
91
- const pageContext = reactive({
92
- expanded: true
91
+ // 真偽値の prop は未指定だと false に変換されるため、未指定を undefined のまま受けるよう既定値を明示する
92
+ /** 親が v-model:expanded で束縛したときの開閉状態。束縛しなければ undefined */
93
+ const model = defineModel<boolean | undefined>('expanded', { default: undefined });
94
+ /** 束縛しないときの開閉状態 */
95
+ const localExpanded = ref(!!props.initialExpand);
96
+ const expanded = computed({
97
+ get: (): boolean => model.value ?? localExpanded.value,
98
+ set: (value: boolean): void => {
99
+ localExpanded.value = value;
100
+ model.value = value;
101
+ }
93
102
  });
94
103
  const shrinkable = useShrinkable(refContents);
95
104
 
96
- const togglePreset = computed((): LabelPreset | undefined => pageContext.expanded ? props.closePreset : props.openPreset);
105
+ const togglePreset = computed((): LabelPreset | undefined => expanded.value ? props.collapsePreset : props.expandPreset);
97
106
 
98
107
  /**
99
108
  * 開閉ボタンに表示するアイコン。スロットは関数のままコンポーネントとして描画する
@@ -102,7 +111,7 @@ const togglePreset = computed((): LabelPreset | undefined => pageContext.expande
102
111
  * @returns {Slot | Component | undefined}
103
112
  */
104
113
  const toggleIcon = (): Slot | Component | undefined => {
105
- const slot = pageContext.expanded ? slots['close-icon'] : slots['open-icon'];
114
+ const slot = expanded.value ? slots['collapse-icon'] : slots['expand-icon'];
106
115
  return (slot as Slot | undefined) ?? togglePreset.value?.icon;
107
116
  };
108
117
 
@@ -112,12 +121,12 @@ const toggleIcon = (): Slot | Component | undefined => {
112
121
  * @returns {Record<string, unknown> | undefined}
113
122
  */
114
123
  const toggleIconProps = (): Record<string, unknown> | undefined => {
115
- const slot = pageContext.expanded ? slots['close-icon'] : slots['open-icon'];
124
+ const slot = expanded.value ? slots['collapse-icon'] : slots['expand-icon'];
116
125
  return slot == null ? togglePreset.value?.iconProps : undefined;
117
126
  };
118
127
 
119
128
  const toggleLabel = computed((): string | undefined => {
120
- const label = pageContext.expanded ? props.closeLabel : props.openLabel;
129
+ const label = expanded.value ? props.collapseLabel : props.expandLabel;
121
130
  if (label != null) {
122
131
  return label;
123
132
  }
@@ -126,12 +135,8 @@ const toggleLabel = computed((): string | undefined => {
126
135
  });
127
136
 
128
137
  const onClickToggle = () => {
129
- pageContext.expanded = !pageContext.expanded;
138
+ expanded.value = !expanded.value;
130
139
  };
131
-
132
- onMounted(() => {
133
- pageContext.expanded = !!props.initialExpand;
134
- });
135
140
  </script>
136
141
 
137
142
  <style>
@@ -172,9 +177,14 @@ onMounted(() => {
172
177
  width: 100%;
173
178
  }
174
179
 
180
+ /* 開閉ボタンは押しボタンではなくアイコンとラベルの操作として見せるため、既定では枠・背景を持たない */
175
181
  :where(.dwui-accordion-section-toggle) {
176
182
  display: inline-flex;
177
183
  align-items: center;
184
+ padding: 0;
185
+ border: none;
186
+ background: none;
187
+ color: inherit;
178
188
  cursor: pointer;
179
189
  }
180
190
 
@@ -183,12 +193,17 @@ onMounted(() => {
183
193
  cursor: not-allowed;
184
194
  }
185
195
 
196
+ :where(.dwui-accordion-section-toggle:focus-visible) {
197
+ outline: var(--dwui-outline-width-focus-ring, 2px) solid var(--dwui-outline-color-focus-ring, Highlight);
198
+ outline-offset: var(--dwui-outline-offset-focus-ring, 2px);
199
+ }
200
+
186
201
  :where(.dwui-accordion-section-toggle[aria-expanded='true']) {
187
- margin-top: 8px;
202
+ margin-top: 0.67em;
188
203
  }
189
204
 
190
205
  /* 縮めたときはボタンを中身のぼかしに少し重ね、中身とボタンの間を詰める */
191
206
  :where(.dwui-accordion-section-toggle[aria-expanded='false']) {
192
- margin-top: -10px;
207
+ margin-top: -0.83em;
193
208
  }
194
209
  </style>
package/src/components.ts CHANGED
@@ -1,10 +1,20 @@
1
1
  // 公開するコンポーネントをここに列挙する。index/register/nuxtはここから読む
2
2
  export { default as AccordionFieldset } from '@/accordion-fieldset.vue';
3
3
  export { default as AccordionSection } from '@/accordion-section.vue';
4
+ export { default as CopyButton } from '@/copy-button.vue';
5
+ export { default as DarkModeSelector } from '@/dark-mode-selector.vue';
6
+ export { default as DataTable } from '@/data-table.vue';
7
+ export { default as HelpButton } from '@/help-button.vue';
8
+ export { default as HelpPopper } from '@/help-popper.vue';
4
9
  export { default as HourglassLoading } from '@/hourglass-loading.vue';
10
+ export { default as IntersectionLazyComponent } from '@/intersection-lazy-component.vue';
5
11
  export { default as LabelButton } from '@/label-button.vue';
6
12
  export { default as LabelIcon } from '@/label-icon.vue';
13
+ export { default as ModalDialog } from '@/modal-dialog.vue';
14
+ export { default as OverlayBox } from '@/overlay-box.vue';
7
15
  export { default as PageSection } from '@/page-section.vue';
8
16
  export { default as PageTitle } from '@/page-title.vue';
17
+ export { default as ScreenLoading } from '@/screen-loading.vue';
9
18
  export { default as TabContent } from '@/tab-content.vue';
10
19
  export { default as TabGroup } from '@/tab-group.vue';
20
+ export { default as WarningPopper } from '@/warning-popper.vue';
@@ -0,0 +1,89 @@
1
+ <!--
2
+ 値をクリップボードにコピーするボタン
3
+ -->
4
+ <template>
5
+ <button
6
+ type="button"
7
+ class="dwui-copy-button"
8
+ :aria-label="slots.default ? undefined : props.label"
9
+ @click="onClickCopy"
10
+ >
11
+ <slot>
12
+ <SvgMemo />
13
+ </slot>
14
+ </button>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { SvgMemo } from '@digitalwalletcorp/vue-svg-icons';
19
+
20
+ export interface Props {
21
+ /** コピーする値。関数を渡すとクリック時に呼び出して値を得る */
22
+ value: string | number | (() => string | number);
23
+ /** ボタンの読み上げ名。スロットで文字を渡したときは使わない */
24
+ label?: string;
25
+ }
26
+ const props = withDefaults(defineProps<Props>(), {
27
+ label: 'Copy'
28
+ });
29
+
30
+ export interface Emits {
31
+ /** コピーに成功したときに、コピーした文字列を発行する */
32
+ (e: 'emit:copy', value: string): void;
33
+ /** コピーに失敗したときに、原因のエラーを発行する */
34
+ (e: 'emit:error', error: unknown): void;
35
+ }
36
+ const emit = defineEmits<Emits>();
37
+
38
+ export interface Slots {
39
+ /** ボタンの中身。未指定ならメモのアイコン */
40
+ default?(): unknown;
41
+ }
42
+ const slots = defineSlots<Slots>();
43
+
44
+ const onClickCopy = async () => {
45
+ const value = String(typeof props.value === 'function' ? props.value() : props.value);
46
+ try {
47
+ await navigator.clipboard.writeText(value);
48
+ } catch (error) {
49
+ emit('emit:error', error);
50
+ return;
51
+ }
52
+ emit('emit:copy', value);
53
+ };
54
+ </script>
55
+
56
+ <style>
57
+ :where(.dwui-copy-button) {
58
+ display: inline-flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ padding: 0.17em;
62
+ border: none;
63
+ border-radius: 4px;
64
+ background-color: transparent;
65
+ background-image: none;
66
+ color: inherit;
67
+ font: inherit;
68
+ line-height: 0;
69
+ cursor: pointer;
70
+ }
71
+
72
+ :where(.dwui-copy-button:not(:disabled):hover) {
73
+ background: var(--dwui-background-copy-button-hover, light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.12)));
74
+ }
75
+
76
+ :where(.dwui-copy-button:not(:disabled):active) {
77
+ transform: translate(1px, 1px);
78
+ }
79
+
80
+ :where(.dwui-copy-button:disabled) {
81
+ opacity: 0.4;
82
+ cursor: not-allowed;
83
+ }
84
+
85
+ :where(.dwui-copy-button:focus-visible) {
86
+ outline: var(--dwui-outline-width-focus-ring, 2px) solid var(--dwui-outline-color-focus-ring, Highlight);
87
+ outline-offset: var(--dwui-outline-offset-focus-ring, 2px);
88
+ }
89
+ </style>