@reni-corp/reni-2c-ui 0.3.28 → 0.3.291

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 (101) hide show
  1. package/README.md +0 -196
  2. package/dist/components/elements/Alert.vue.d.ts +21 -3
  3. package/dist/components/elements/Alert.vue.d.ts.map +1 -1
  4. package/dist/components/elements/CheckBox.vue.d.ts +42 -2
  5. package/dist/components/elements/CheckBox.vue.d.ts.map +1 -1
  6. package/dist/components/elements/ComboBox.vue.d.ts +153 -0
  7. package/dist/components/elements/ComboBox.vue.d.ts.map +1 -0
  8. package/dist/components/elements/Icon.vue.d.ts.map +1 -1
  9. package/dist/components/elements/PasswordField.vue.d.ts +75 -25
  10. package/dist/components/elements/PasswordField.vue.d.ts.map +1 -1
  11. package/dist/components/elements/Progress.vue.d.ts +45 -0
  12. package/dist/components/elements/Progress.vue.d.ts.map +1 -0
  13. package/dist/components/elements/SelectBox.vue.d.ts +30 -10
  14. package/dist/components/elements/SelectBox.vue.d.ts.map +1 -1
  15. package/dist/components/elements/SkeletonLoader.vue.d.ts +30 -0
  16. package/dist/components/elements/SkeletonLoader.vue.d.ts.map +1 -0
  17. package/dist/components/elements/SpinButton.vue.d.ts +4 -2
  18. package/dist/components/elements/SpinButton.vue.d.ts.map +1 -1
  19. package/dist/components/elements/TextField.vue.d.ts +21 -6
  20. package/dist/components/elements/TextField.vue.d.ts.map +1 -1
  21. package/dist/components/features/ProductList.vue.d.ts +4 -0
  22. package/dist/components/features/ProductList.vue.d.ts.map +1 -1
  23. package/dist/components/features/ProductListItem.vue.d.ts +4 -0
  24. package/dist/components/features/ProductListItem.vue.d.ts.map +1 -1
  25. package/dist/components/features/ProductPurchase.vue.d.ts +0 -4
  26. package/dist/components/features/ProductPurchase.vue.d.ts.map +1 -1
  27. package/dist/components/foundation/AppBar.vue.d.ts +28 -3
  28. package/dist/components/foundation/AppBar.vue.d.ts.map +1 -1
  29. package/dist/components/foundation/AppFooter.vue.d.ts +51 -1
  30. package/dist/components/foundation/AppFooter.vue.d.ts.map +1 -1
  31. package/dist/components/interactive/Disclosure.vue.d.ts +54 -0
  32. package/dist/components/interactive/Disclosure.vue.d.ts.map +1 -0
  33. package/dist/components/layouts/Page.vue.d.ts +2 -0
  34. package/dist/components/layouts/Page.vue.d.ts.map +1 -1
  35. package/dist/components/renderless/Form.vue.d.ts +27 -1
  36. package/dist/components/renderless/Form.vue.d.ts.map +1 -1
  37. package/dist/index.d.ts +9 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.es.js +7161 -5941
  40. package/dist/script.es.js +8364 -7146
  41. package/dist/script.umd.js +26 -26
  42. package/dist/style.css +1 -1
  43. package/dist/types.d.ts +7 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/utils.d.ts.map +1 -1
  46. package/package.json +18 -13
  47. package/src/stories/Alert.stories.ts +260 -0
  48. package/src/stories/AnnounceBar.stories.ts +138 -0
  49. package/src/stories/AppBar.stories.ts +277 -0
  50. package/src/stories/AppFooter.stories.ts +274 -0
  51. package/src/stories/AppFrame.stories.ts +46 -0
  52. package/src/stories/AppLayout.stories.ts +870 -0
  53. package/src/stories/Button.stories.ts +101 -0
  54. package/src/stories/Card.stories.ts +152 -0
  55. package/src/stories/Carousel.stories.ts +62 -0
  56. package/src/stories/CarouselBanner.stories.ts +103 -0
  57. package/src/stories/CheckBox.stories.ts +76 -0
  58. package/src/stories/ComboBox.stories.ts +524 -0
  59. package/src/stories/Dialog.stories.ts +174 -0
  60. package/src/stories/Disclosure.stories.ts +217 -0
  61. package/src/stories/Divider.stories.ts +68 -0
  62. package/src/stories/Drawer.stories.ts +135 -0
  63. package/src/stories/DropDown.stories.ts +195 -0
  64. package/src/stories/FloatingBanner.stories.ts +79 -0
  65. package/src/stories/Form.stories.ts +704 -0
  66. package/src/stories/Gallery.stories.ts +78 -0
  67. package/src/stories/Grid.stories.ts +357 -0
  68. package/src/stories/Hero.stories.ts +52 -0
  69. package/src/stories/Html.stories.ts +178 -0
  70. package/src/stories/Icon.stories.ts +176 -0
  71. package/src/stories/Image.stories.ts +613 -0
  72. package/src/stories/Label.stories.ts +54 -0
  73. package/src/stories/List.stories.ts +112 -0
  74. package/src/stories/Modal.stories.ts +123 -0
  75. package/src/stories/Notification.stories.ts +82 -0
  76. package/src/stories/Page.stories.ts +414 -0
  77. package/src/stories/PasswordField.stories.ts +304 -0
  78. package/src/stories/ProductLabels.stories.ts +65 -0
  79. package/src/stories/ProductList.stories.ts +679 -0
  80. package/src/stories/ProductPurchase.stories.ts +807 -0
  81. package/src/stories/Progress.stories.ts +192 -0
  82. package/src/stories/Radio.stories.ts +81 -0
  83. package/src/stories/Section.stories.ts +244 -0
  84. package/src/stories/SelectBox.stories.ts +377 -0
  85. package/src/stories/SkeletonLoader.stories.ts +170 -0
  86. package/src/stories/Slider.stories.ts +79 -0
  87. package/src/stories/SnsLink.stories.ts +259 -0
  88. package/src/stories/SoldStacker.stories.ts +68 -0
  89. package/src/stories/SpinButton.stories.ts +134 -0
  90. package/src/stories/Spinner.stories.ts +58 -0
  91. package/src/stories/Stack.stories.ts +104 -0
  92. package/src/stories/Switch.stories.ts +68 -0
  93. package/src/stories/Tab.stories.ts +52 -0
  94. package/src/stories/TabPanels.stories.ts +67 -0
  95. package/src/stories/Tabs.stories.ts +68 -0
  96. package/src/stories/Text.stories.ts +69 -0
  97. package/src/stories/TextArea.stories.ts +78 -0
  98. package/src/stories/TextField.stories.ts +97 -0
  99. package/src/stories/ToolChip.stories.ts +125 -0
  100. package/dist/components/elements/SkeltonLoader.vue.d.ts +0 -7
  101. package/dist/components/elements/SkeltonLoader.vue.d.ts.map +0 -1
@@ -0,0 +1,704 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import Form from '@/components/renderless/Form.vue'
3
+ import TextField from '@/components/elements/TextField.vue'
4
+ import SelectBox from '@/components/elements/SelectBox.vue'
5
+ import TextArea from '@/components/elements/TextArea.vue'
6
+ import PasswordField from '@/components/elements/PasswordField.vue'
7
+ import CheckBox from '@/components/elements/CheckBox.vue'
8
+ import Button from '@/components/elements/Button.vue'
9
+ import Stack from '@/components/layouts/Stack.vue'
10
+ import { ref } from 'vue'
11
+
12
+ const meta: Meta<typeof Form> = {
13
+ title: 'Renderless/Form',
14
+ component: Form,
15
+ tags: ['autodocs'],
16
+ argTypes: {
17
+ default: {
18
+ description: `
19
+ **フォーム状態変数**
20
+ | 名称 | 型 | 説明 |
21
+ |---|---|---|
22
+ | \`invalid\` | boolean | フォームが無効かどうか。<br>ボタンの \`disabled\` 制御などに使用。 |
23
+ | \`isValidating\` | boolean | バリデーション実行中かどうか。<br>非同期検証中のスピナー制御に便利。 |
24
+ | \`hasErrors\` | boolean | いずれかのエラーが存在するか。<br>\`Object.keys(errors).length > 0\` 相当。 |
25
+ | \`touched\` | boolean | いずれかのフィールドがタッチ済みか。<br>初回表示でエラーを出さない制御に使用。 |
26
+ | \`dirty\` | boolean | いずれかの値が変更されているか。<br>変更がある時のみ保存ボタンを活性化。 |
27
+ | \`canSubmit\` | boolean | 送信可能状態かどうか。<br>\`valid && !isValidating && dirty\` を簡略化。 |
28
+ <br><br>
29
+ **メタ情報 / エラー情報**
30
+ | 名称 | 型 | 説明 |
31
+ |---|---|---|
32
+ | \`meta\` | object | フォームのメタデータ(vee-validate の \`meta\`)。<br>\`meta.valid\` や \`meta.initialValues\` などを含む。 |
33
+ | \`errors\` | object | 各フィールドのエラー情報オブジェクト。<br>例: \`{ email: '必須です' }\`。 |
34
+ <br><br>
35
+
36
+ **アクション関数**
37
+ | 名称 | 型 | 説明 |
38
+ |---|---|---|
39
+ | \`validate(): Promise<{ valid: boolean }>\` | function | 手動で全体バリデーションを実行。<br>送信前チェックに使用。 |
40
+ | \`resetForm(state?)\` | function | フォームを初期化。<br>送信後のクリアや「リセット」ボタンに使用。 |
41
+ | \`setFieldValue(field, value)\` | function | 特定フィールドの値を設定。<br>ウィザード形式で初期値を配る際に使用。 |
42
+ | \`setErrors(errors)\` | function | カスタムエラーをまとめて設定。<br>サーバー検証エラー反映時に使用。 |
43
+ | \`setTouched(fields)\` | function | タッチ状態をまとめて設定。<br>全フィールドのエラー表示を有効化。 |
44
+ `,
45
+ table: {
46
+ category: 'slots',
47
+ type: {
48
+ summary:
49
+ '{ invalid, isValidating, hasErrors, touched, dirty, canSubmit, meta, errors, validate, resetForm, setFieldValue, setErrors, setTouched }',
50
+ },
51
+ },
52
+ control: false,
53
+ },
54
+ },
55
+
56
+ args: {},
57
+ }
58
+
59
+ export default meta
60
+ type StoryArgs = {}
61
+ type Story = StoryObj<StoryArgs>
62
+ type OverridesStory = Omit<Story, 'argTypes'> & {
63
+ argTypes?: Record<string, any>
64
+ }
65
+
66
+ export const 基本: OverridesStory = {
67
+ args: {},
68
+ render: (args: StoryArgs) => ({
69
+ components: {
70
+ 'rn-form': Form,
71
+ 'rn-text-field': TextField,
72
+ 'rn-select-box': SelectBox,
73
+ 'rn-text-area': TextArea,
74
+ 'rn-password-field': PasswordField,
75
+ 'rn-checkbox': CheckBox,
76
+ 'rn-button': Button,
77
+ 'rn-stack': Stack,
78
+ },
79
+ setup() {
80
+ const field1 = ref('テキスト')
81
+ const field2 = ref('最小文字数制限')
82
+ const field3 = ref('mailaddress@reni.ci.jp')
83
+ const field4 = ref('option1') // SelectBox用
84
+ const field5 = ref(
85
+ '長文テキスト短くなるとエラーになります。長文テキスト短くなるとエラーになります。長文テキスト短くなるとエラーになります。',
86
+ ) // TextArea用(バリデーションエラー想定)
87
+ const password = ref('Zzaq12wsx@??') // PasswordField用
88
+ const agreement = ref(false) // CheckBox用(利用規約同意)
89
+ const newsletter = ref(true) // CheckBox用(任意項目)
90
+ const submissionResult = ref('')
91
+
92
+ // SelectBox用のオプション
93
+ const selectOptions = [
94
+ { value: 'option1', label: 'オプション1' },
95
+ { value: 'option2', label: 'オプション2' },
96
+ { value: 'option3', label: 'オプション3' },
97
+ { value: 'option4', label: 'オプション4' },
98
+ ]
99
+
100
+ const handleSubmit = () => {
101
+ alert('フォームが送信されました!')
102
+ submissionResult.value = 'フォームが正常に送信されました'
103
+ }
104
+
105
+ const handleManualValidation = async (validate: () => Promise<any>) => {
106
+ const result = await validate()
107
+ console.log('バリデーション結果:', result)
108
+ submissionResult.value = result.valid
109
+ ? 'バリデーション成功'
110
+ : 'バリデーションエラーがあります'
111
+ }
112
+
113
+ const handleReset = (resetForm: (state?: any) => void) => {
114
+ resetForm()
115
+ field1.value = ''
116
+ field2.value = ''
117
+ field3.value = ''
118
+ field4.value = ''
119
+ field5.value = ''
120
+ password.value = '' // PasswordFieldもリセット
121
+ agreement.value = false // CheckBoxもリセット
122
+ newsletter.value = false
123
+ submissionResult.value = 'フォームがリセットされました'
124
+ }
125
+
126
+ const setInitialValues = (
127
+ setFieldValue: (field: string, value: any) => void,
128
+ ) => {
129
+ setFieldValue('field1', '初期値1')
130
+ setFieldValue('field2', 'initial value 2')
131
+ setFieldValue('field3', 'sample@example.com')
132
+ setFieldValue('field4', 'option2')
133
+ setFieldValue('field5', 'セットされたテキストエリアの初期値です。')
134
+ setFieldValue('password', 'SamplePass123!') // PasswordFieldの初期値設定
135
+ setFieldValue('agreement', true) // CheckBoxの初期値設定
136
+ setFieldValue('newsletter', false) // CheckBoxの初期値設定
137
+ field1.value = '初期値1'
138
+ field2.value = 'initial value 2'
139
+ field3.value = 'sample@example.com'
140
+ field4.value = 'option2'
141
+ field5.value = 'セットされたテキストエリアの初期値です。'
142
+ password.value = 'SamplePass123!' // PasswordFieldの初期値
143
+ agreement.value = true // CheckBoxの初期値
144
+ newsletter.value = false
145
+ submissionResult.value = '初期値が設定されました'
146
+ }
147
+
148
+ const setCustomErrors = (
149
+ setErrors: (errors: Record<string, string>) => void,
150
+ ) => {
151
+ setErrors({
152
+ field1: 'カスタムエラー: この値は使用できません',
153
+ field2: 'カスタムエラー: サーバー側でエラーが発生しました',
154
+ password: 'カスタムエラー: このパスワードは既に使用されています',
155
+ agreement: 'カスタムエラー: 利用規約に同意する必要があります',
156
+ })
157
+ submissionResult.value = 'カスタムエラーが設定されました'
158
+ }
159
+
160
+ const markFieldsAsTouched = (
161
+ setTouched: (fields: Record<string, boolean>) => void,
162
+ ) => {
163
+ setTouched({
164
+ field1: true,
165
+ field2: true,
166
+ field3: true,
167
+ password: true, // PasswordFieldもタッチ済みに
168
+ agreement: true, // CheckBoxもタッチ済みに
169
+ newsletter: true,
170
+ })
171
+ submissionResult.value = '全てのフィールドがタッチ済みに設定されました'
172
+ }
173
+
174
+ return {
175
+ args,
176
+ field1,
177
+ field2,
178
+ field3,
179
+ field4,
180
+ field5,
181
+ password, // PasswordField用
182
+ agreement, // CheckBox用
183
+ newsletter, // CheckBox用
184
+ selectOptions,
185
+ submissionResult,
186
+ handleSubmit,
187
+ handleManualValidation,
188
+ handleReset,
189
+ setInitialValues,
190
+ setCustomErrors,
191
+ markFieldsAsTouched,
192
+ }
193
+ },
194
+ template: /* html */ `
195
+ <div class='sb-canvas'>
196
+ <rn-form>
197
+ <template #default="{
198
+ invalid,
199
+ isValidating,
200
+ hasErrors,
201
+ touched,
202
+ dirty,
203
+ canSubmit,
204
+ meta,
205
+ errors,
206
+ validate,
207
+ resetForm,
208
+ setFieldValue,
209
+ setErrors,
210
+ setTouched
211
+ }">
212
+ <rn-stack direction="vertical" gap="lg">
213
+ <!-- フォーム状態表示 -->
214
+ <div style="padding: 16px; background: #f5f5f5; border-radius: 8px; font-size: 14px;">
215
+ <h4 style="margin: 0 0 12px 0; color: #333;">フォーム状態</h4>
216
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
217
+ <div><strong>Invalid:</strong> {{ invalid }}</div>
218
+ <div><strong>Validating:</strong> {{ isValidating }}</div>
219
+ <div><strong>Has Errors:</strong> {{ hasErrors }}</div>
220
+ <div><strong>Touched:</strong> {{ touched }}</div>
221
+ <div><strong>Dirty:</strong> {{ dirty }}</div>
222
+ <div><strong>Can Submit:</strong> {{ canSubmit }}</div>
223
+ </div>
224
+ <div style="margin-top: 8px;">
225
+ <strong>Errors:</strong> {{ Object.keys(errors).length > 0 ? JSON.stringify(errors) : '(なし)' }}
226
+ </div>
227
+ <div v-if="submissionResult" style="margin-top: 8px; padding: 8px; background: #e8f5e8; border-radius: 4px;">
228
+ <strong>結果:</strong> {{ submissionResult }}
229
+ </div>
230
+ </div>
231
+
232
+ <!-- フォームフィールド -->
233
+ <rn-stack direction="vertical" gap="md">
234
+ <rn-text-field
235
+ name="field1"
236
+ v-model="field1"
237
+ label="必須フィールド"
238
+ rules="required"
239
+ help-text="この項目は必須です"
240
+ />
241
+ <rn-text-field
242
+ v-model="field2"
243
+ name="field2"
244
+ label="最小文字数フィールド(6文字以上)"
245
+ rules="required|min:6"
246
+ help-text="6文字以上で入力してください"
247
+ />
248
+ <rn-text-field
249
+ v-model="field3"
250
+ name="field3"
251
+ label="メールアドレス"
252
+ rules="required|email"
253
+ help-text="有効なメールアドレスを入力してください"
254
+ />
255
+ <rn-select-box
256
+ v-model="field4"
257
+ name="field4"
258
+ label="選択フィールド(必須)"
259
+ :items="selectOptions"
260
+ rules="required"
261
+ placeholder="オプションを選択してください"
262
+ clearable
263
+ />
264
+ <rn-text-area
265
+ v-model="field5"
266
+ name="field5"
267
+ label="テキストエリア(最低20文字)"
268
+ rules="required|min:20"
269
+ placeholder="20文字以上で入力してください"
270
+ :rows="4"
271
+ dynamic-rows
272
+ />
273
+ <rn-password-field
274
+ v-model="password"
275
+ name="password"
276
+ label="パスワード(必須・強度チェック)"
277
+ required
278
+ requireStrongPassword
279
+ showStrengthIndicator
280
+ requiredErrorMessage="パスワードは必須です"
281
+ customErrorMessage="8文字以上で英大文字・小文字・数字・特殊文字を含む強いパスワードが必要です"
282
+ />
283
+ <rn-checkbox
284
+ v-model="agreement"
285
+ name="agreement"
286
+ label="利用規約に同意する"
287
+ required
288
+ requiredErrorMessage="利用規約への同意は必須です"
289
+ />
290
+ <rn-checkbox
291
+ v-model="newsletter"
292
+ name="newsletter"
293
+ label="メールマガジンの配信を希望する(任意)"
294
+ />
295
+ </rn-stack>
296
+
297
+ <!-- 操作ボタン群 -->
298
+ <rn-stack direction="vertical" gap="md">
299
+ <!-- 基本操作 -->
300
+ <rn-stack horizontal-re-size="fill" horizontal-align="between" gap="sm">
301
+ <rn-button
302
+ @click="handleManualValidation(validate)"
303
+ :disabled="isValidating"
304
+ >
305
+ 手動バリデーション
306
+ </rn-button>
307
+ <rn-button
308
+ @click="handleSubmit()"
309
+ :disabled="invalid"
310
+ color="primary"
311
+ >
312
+ 送信 (無効時は非活性)
313
+ </rn-button>
314
+ </rn-stack>
315
+
316
+ <!-- フォーム制御操作 -->
317
+ <rn-stack horizontal-re-size="fill" horizontal-align="between" gap="sm">
318
+ <rn-button
319
+ @click="handleReset(resetForm)"
320
+ color="secondary"
321
+ >
322
+ フォームリセット
323
+ </rn-button>
324
+ <rn-button
325
+ @click="setInitialValues(setFieldValue)"
326
+ color="secondary"
327
+ >
328
+ 初期値設定
329
+ </rn-button>
330
+ </rn-stack>
331
+
332
+ <!-- 高度な操作 -->
333
+ <rn-stack horizontal-re-size="fill" horizontal-align="between" gap="sm">
334
+ <rn-button
335
+ @click="setCustomErrors(setErrors)"
336
+ color="danger"
337
+ >
338
+ カスタムエラー設定
339
+ </rn-button>
340
+ <rn-button
341
+ @click="markFieldsAsTouched(setTouched)"
342
+ color="secondary"
343
+ >
344
+ 全フィールドをタッチ済みに
345
+ </rn-button>
346
+ </rn-stack>
347
+
348
+ <!-- 条件付きボタン -->
349
+ <rn-stack horizontal-re-size="fill" horizontal-align="center" gap="sm">
350
+ <rn-button
351
+ v-if="canSubmit"
352
+ @click="handleSubmit()"
353
+ color="success"
354
+ >
355
+ 送信可能 (dirty & valid)
356
+ </rn-button>
357
+ <rn-button
358
+ v-if="dirty && hasErrors"
359
+ @click="handleManualValidation(validate)"
360
+ color="warning"
361
+ >
362
+ エラーを再確認
363
+ </rn-button>
364
+ </rn-stack>
365
+ </rn-stack>
366
+ </rn-stack>
367
+ </template>
368
+ </rn-form>
369
+ </div>
370
+ `,
371
+ }),
372
+ }
373
+
374
+ export const チェックボックス付きフォーム: OverridesStory = {
375
+ args: {},
376
+ render: (args: StoryArgs) => ({
377
+ components: {
378
+ 'rn-form': Form,
379
+ 'rn-text-field': TextField,
380
+ 'rn-checkbox': CheckBox,
381
+ 'rn-button': Button,
382
+ 'rn-stack': Stack,
383
+ },
384
+ setup() {
385
+ const name = ref('')
386
+ const email = ref('')
387
+ const agreement = ref(false) // 必須
388
+ const newsletter = ref(false) // 任意
389
+ const updates = ref(false) // 任意
390
+ const privacy = ref(false) // 必須
391
+ const submissionResult = ref('')
392
+
393
+ const handleSubmit = () => {
394
+ alert('登録が完了しました!')
395
+ submissionResult.value = '登録が正常に完了しました'
396
+ }
397
+
398
+ const handleReset = (resetForm: (state?: any) => void) => {
399
+ resetForm()
400
+ name.value = ''
401
+ email.value = ''
402
+ agreement.value = false
403
+ newsletter.value = false
404
+ updates.value = false
405
+ privacy.value = false
406
+ submissionResult.value = 'フォームがリセットされました'
407
+ }
408
+
409
+ return {
410
+ args,
411
+ name,
412
+ email,
413
+ agreement,
414
+ newsletter,
415
+ updates,
416
+ privacy,
417
+ submissionResult,
418
+ handleSubmit,
419
+ handleReset,
420
+ }
421
+ },
422
+ template: /* html */ `
423
+ <div class='sb-canvas'>
424
+ <rn-form>
425
+ <template #default="{
426
+ invalid,
427
+ isValidating,
428
+ hasErrors,
429
+ touched,
430
+ dirty,
431
+ canSubmit,
432
+ meta,
433
+ errors,
434
+ validate,
435
+ resetForm
436
+ }">
437
+ <rn-stack direction="vertical" gap="lg">
438
+ <!-- フォーム状態表示 -->
439
+ <div style="padding: 16px; background: #f5f5f5; border-radius: 8px; font-size: 14px;">
440
+ <h4 style="margin: 0 0 12px 0; color: #333;">チェックボックス付きフォーム状態</h4>
441
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
442
+ <div><strong>Invalid:</strong> {{ invalid }}</div>
443
+ <div><strong>Validating:</strong> {{ isValidating }}</div>
444
+ <div><strong>Has Errors:</strong> {{ hasErrors }}</div>
445
+ <div><strong>Touched:</strong> {{ touched }}</div>
446
+ <div><strong>Dirty:</strong> {{ dirty }}</div>
447
+ <div><strong>Can Submit:</strong> {{ canSubmit }}</div>
448
+ </div>
449
+ <div style="margin-top: 8px;">
450
+ <strong>Errors:</strong> {{ Object.keys(errors).length > 0 ? JSON.stringify(errors) : '(なし)' }}
451
+ </div>
452
+ <div v-if="submissionResult" style="margin-top: 8px; padding: 8px; background: #e8f5e8; border-radius: 4px;">
453
+ <strong>結果:</strong> {{ submissionResult }}
454
+ </div>
455
+ </div>
456
+
457
+ <!-- 登録フォーム -->
458
+ <div style="max-width: 500px; margin: 0 auto; padding: 24px; border: 1px solid #ddd; border-radius: 8px;">
459
+ <h3 style="text-align: center; margin-bottom: 24px;">サービス登録</h3>
460
+ <rn-stack direction="vertical" gap="md">
461
+ <rn-text-field
462
+ v-model="name"
463
+ name="name"
464
+ label="お名前"
465
+ rules="required"
466
+ placeholder="山田太郎"
467
+ />
468
+
469
+ <rn-text-field
470
+ v-model="email"
471
+ name="email"
472
+ label="メールアドレス"
473
+ rules="required|email"
474
+ placeholder="example@domain.com"
475
+ />
476
+
477
+ <!-- 必須チェックボックス群 -->
478
+ <div style="padding: 16px; background: #fafafa; border-radius: 8px;">
479
+ <h4 style="margin: 0 0 12px 0; color: #333; font-size: 16px;">必須項目</h4>
480
+ <rn-stack direction="vertical" gap="sm">
481
+ <rn-checkbox
482
+ v-model="agreement"
483
+ name="agreement"
484
+ label="利用規約に同意します"
485
+ required
486
+ requiredErrorMessage="利用規約への同意は必須です"
487
+ color="primary"
488
+ />
489
+ <rn-checkbox
490
+ v-model="privacy"
491
+ name="privacy"
492
+ label="プライバシーポリシーに同意します"
493
+ required
494
+ requiredErrorMessage="プライバシーポリシーへの同意は必須です"
495
+ color="primary"
496
+ />
497
+ </rn-stack>
498
+ </div>
499
+
500
+ <!-- 任意チェックボックス群 -->
501
+ <div style="padding: 16px; background: #f0f8ff; border-radius: 8px;">
502
+ <h4 style="margin: 0 0 12px 0; color: #333; font-size: 16px;">オプション(任意)</h4>
503
+ <rn-stack direction="vertical" gap="sm">
504
+ <rn-checkbox
505
+ v-model="newsletter"
506
+ name="newsletter"
507
+ label="メールマガジンの配信を希望します"
508
+ color="info"
509
+ />
510
+ <rn-checkbox
511
+ v-model="updates"
512
+ name="updates"
513
+ label="新機能やアップデート情報を受け取ります"
514
+ color="info"
515
+ />
516
+ </rn-stack>
517
+ </div>
518
+ </rn-stack>
519
+ </div>
520
+
521
+ <!-- 操作ボタン -->
522
+ <rn-stack horizontal-re-size="fill" horizontal-align="center" gap="md">
523
+ <rn-button
524
+ @click="handleReset(resetForm)"
525
+ color="secondary"
526
+ >
527
+ リセット
528
+ </rn-button>
529
+ <rn-button
530
+ @click="handleSubmit()"
531
+ :disabled="invalid"
532
+ color="primary"
533
+ >
534
+ 登録する
535
+ </rn-button>
536
+ </rn-stack>
537
+
538
+ <!-- 値の表示 -->
539
+ <div style="padding: 16px; background: #f9f9f9; border-radius: 8px; font-size: 14px;">
540
+ <h4 style="margin: 0 0 8px 0;">現在の値:</h4>
541
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 4px;">
542
+ <div><strong>名前:</strong> {{ name || '(未入力)' }}</div>
543
+ <div><strong>メール:</strong> {{ email || '(未入力)' }}</div>
544
+ <div><strong>利用規約:</strong> {{ agreement ? '同意済み' : '未同意' }}</div>
545
+ <div><strong>プライバシー:</strong> {{ privacy ? '同意済み' : '未同意' }}</div>
546
+ <div><strong>メルマガ:</strong> {{ newsletter ? '希望' : '不要' }}</div>
547
+ <div><strong>アップデート:</strong> {{ updates ? '希望' : '不要' }}</div>
548
+ </div>
549
+ </div>
550
+ </rn-stack>
551
+ </template>
552
+ </rn-form>
553
+ </div>
554
+ `,
555
+ }),
556
+ }
557
+
558
+ export const パスワードフィールド付き: OverridesStory = {
559
+ args: {},
560
+ render: (args: StoryArgs) => ({
561
+ components: {
562
+ 'rn-form': Form,
563
+ 'rn-text-field': TextField,
564
+ 'rn-password-field': PasswordField,
565
+ 'rn-button': Button,
566
+ 'rn-stack': Stack,
567
+ },
568
+ setup() {
569
+ const email = ref('')
570
+ const password = ref('')
571
+ const confirmPassword = ref('')
572
+ const submissionResult = ref('')
573
+
574
+ // パスワード確認のバリデーション
575
+ const validateConfirmPassword = (value: string) => {
576
+ if (!value) {
577
+ return 'パスワード確認は必須です'
578
+ }
579
+ if (value !== password.value) {
580
+ return 'パスワードが一致しません'
581
+ }
582
+ return true
583
+ }
584
+
585
+ const handleSubmit = () => {
586
+ alert('アカウントが作成されました!')
587
+ submissionResult.value = 'アカウントが正常に作成されました'
588
+ }
589
+
590
+ const handleReset = (resetForm: (state?: any) => void) => {
591
+ resetForm()
592
+ email.value = ''
593
+ password.value = ''
594
+ confirmPassword.value = ''
595
+ submissionResult.value = 'フォームがリセットされました'
596
+ }
597
+
598
+ return {
599
+ args,
600
+ email,
601
+ password,
602
+ confirmPassword,
603
+ submissionResult,
604
+ validateConfirmPassword,
605
+ handleSubmit,
606
+ handleReset,
607
+ }
608
+ },
609
+ template: /* html */ `
610
+ <div class='sb-canvas'>
611
+ <rn-form>
612
+ <template #default="{
613
+ invalid,
614
+ isValidating,
615
+ hasErrors,
616
+ touched,
617
+ dirty,
618
+ canSubmit,
619
+ meta,
620
+ errors,
621
+ validate,
622
+ resetForm
623
+ }">
624
+ <rn-stack direction="vertical" gap="lg">
625
+ <!-- フォーム状態表示 -->
626
+ <div style="padding: 16px; background: #f5f5f5; border-radius: 8px; font-size: 14px;">
627
+ <h4 style="margin: 0 0 12px 0; color: #333;">アカウント作成フォーム状態</h4>
628
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
629
+ <div><strong>Invalid:</strong> {{ invalid }}</div>
630
+ <div><strong>Validating:</strong> {{ isValidating }}</div>
631
+ <div><strong>Has Errors:</strong> {{ hasErrors }}</div>
632
+ <div><strong>Touched:</strong> {{ touched }}</div>
633
+ <div><strong>Dirty:</strong> {{ dirty }}</div>
634
+ <div><strong>Can Submit:</strong> {{ canSubmit }}</div>
635
+ </div>
636
+ <div style="margin-top: 8px;">
637
+ <strong>Errors:</strong> {{ Object.keys(errors).length > 0 ? JSON.stringify(errors) : '(なし)' }}
638
+ </div>
639
+ <div v-if="submissionResult" style="margin-top: 8px; padding: 8px; background: #e8f5e8; border-radius: 4px;">
640
+ <strong>結果:</strong> {{ submissionResult }}
641
+ </div>
642
+ </div>
643
+
644
+ <!-- アカウント作成フォーム -->
645
+ <div style="max-width: 400px; margin: 0 auto; padding: 24px; border: 1px solid #ddd; border-radius: 8px;">
646
+ <h3 style="text-align: center; margin-bottom: 24px;">新規アカウント作成</h3>
647
+ <rn-stack direction="vertical" gap="md">
648
+ <rn-text-field
649
+ v-model="email"
650
+ name="email"
651
+ label="メールアドレス"
652
+ rules="required|email"
653
+ placeholder="example@domain.com"
654
+ help-text="ログイン時に使用します"
655
+ />
656
+
657
+ <rn-password-field
658
+ v-model="password"
659
+ name="password"
660
+ label="パスワード"
661
+ required
662
+ requireStrongPassword
663
+ showStrengthIndicator
664
+ requiredErrorMessage="パスワードを入力してください"
665
+ customErrorMessage="より強力なパスワードが必要です(8文字以上、英数字、特殊文字、大文字小文字を含む)"
666
+ />
667
+
668
+ <rn-password-field
669
+ v-model="confirmPassword"
670
+ name="confirmPassword"
671
+ label="パスワード確認"
672
+ :rules="validateConfirmPassword"
673
+ />
674
+ </rn-stack>
675
+ </div>
676
+
677
+ <!-- 操作ボタン -->
678
+ <rn-stack horizontal-re-size="fill" horizontal-align="center" gap="md">
679
+ <rn-button
680
+ @click="handleReset(resetForm)"
681
+ color="secondary"
682
+ >
683
+ リセット
684
+ </rn-button>
685
+ <rn-button
686
+ @click="handleSubmit()"
687
+ :disabled="invalid"
688
+ color="primary"
689
+ >
690
+ アカウント作成
691
+ </rn-button>
692
+ </rn-stack>
693
+
694
+ <!-- 条件付き表示 -->
695
+ <div v-if="canSubmit" style="text-align: center; color: #28a745; font-weight: bold;">
696
+ ✓ すべての項目が正しく入力されています
697
+ </div>
698
+ </rn-stack>
699
+ </template>
700
+ </rn-form>
701
+ </div>
702
+ `,
703
+ }),
704
+ }