@reni-corp/reni-2c-ui 0.3.27 → 0.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -16
- package/dist/components/elements/Alert.vue.d.ts +21 -3
- package/dist/components/elements/Alert.vue.d.ts.map +1 -1
- package/dist/components/elements/CheckBox.vue.d.ts +42 -2
- package/dist/components/elements/CheckBox.vue.d.ts.map +1 -1
- package/dist/components/elements/ComboBox.vue.d.ts +153 -0
- package/dist/components/elements/ComboBox.vue.d.ts.map +1 -0
- package/dist/components/elements/Icon.vue.d.ts.map +1 -1
- package/dist/components/elements/PasswordField.vue.d.ts +75 -25
- package/dist/components/elements/PasswordField.vue.d.ts.map +1 -1
- package/dist/components/elements/Progress.vue.d.ts +45 -0
- package/dist/components/elements/Progress.vue.d.ts.map +1 -0
- package/dist/components/elements/SelectBox.vue.d.ts +30 -10
- package/dist/components/elements/SelectBox.vue.d.ts.map +1 -1
- package/dist/components/elements/SkeletonLoader.vue.d.ts +30 -0
- package/dist/components/elements/SkeletonLoader.vue.d.ts.map +1 -0
- package/dist/components/elements/SpinButton.vue.d.ts +4 -2
- package/dist/components/elements/SpinButton.vue.d.ts.map +1 -1
- package/dist/components/elements/TextField.vue.d.ts +21 -6
- package/dist/components/elements/TextField.vue.d.ts.map +1 -1
- package/dist/components/features/ProductList.vue.d.ts +4 -0
- package/dist/components/features/ProductList.vue.d.ts.map +1 -1
- package/dist/components/features/ProductListItem.vue.d.ts +4 -0
- package/dist/components/features/ProductListItem.vue.d.ts.map +1 -1
- package/dist/components/features/ProductPurchase.vue.d.ts +102 -0
- package/dist/components/features/ProductPurchase.vue.d.ts.map +1 -0
- package/dist/components/foundation/AppBar.vue.d.ts +28 -3
- package/dist/components/foundation/AppBar.vue.d.ts.map +1 -1
- package/dist/components/foundation/AppFooter.vue.d.ts +51 -1
- package/dist/components/foundation/AppFooter.vue.d.ts.map +1 -1
- package/dist/components/interactive/Disclosure.vue.d.ts +54 -0
- package/dist/components/interactive/Disclosure.vue.d.ts.map +1 -0
- package/dist/components/layouts/Page.vue.d.ts +2 -0
- package/dist/components/layouts/Page.vue.d.ts.map +1 -1
- package/dist/components/renderless/Form.vue.d.ts +27 -1
- package/dist/components/renderless/Form.vue.d.ts.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +7442 -6048
- package/dist/script.es.js +8881 -7489
- package/dist/script.umd.js +26 -26
- package/dist/style.css +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/package.json +22 -13
- package/src/stories/Alert.stories.ts +260 -0
- package/src/stories/AnnounceBar.stories.ts +138 -0
- package/src/stories/AppBar.stories.ts +277 -0
- package/src/stories/AppFooter.stories.ts +274 -0
- package/src/stories/AppFrame.stories.ts +46 -0
- package/src/stories/AppLayout.stories.ts +870 -0
- package/src/stories/Button.stories.ts +101 -0
- package/src/stories/Card.stories.ts +152 -0
- package/src/stories/Carousel.stories.ts +62 -0
- package/src/stories/CarouselBanner.stories.ts +103 -0
- package/src/stories/CheckBox.stories.ts +76 -0
- package/src/stories/ComboBox.stories.ts +524 -0
- package/src/stories/Dialog.stories.ts +174 -0
- package/src/stories/Disclosure.stories.ts +217 -0
- package/src/stories/Divider.stories.ts +68 -0
- package/src/stories/Drawer.stories.ts +135 -0
- package/src/stories/DropDown.stories.ts +195 -0
- package/src/stories/FloatingBanner.stories.ts +79 -0
- package/src/stories/Form.stories.ts +704 -0
- package/src/stories/Gallery.stories.ts +78 -0
- package/src/stories/Grid.stories.ts +357 -0
- package/src/stories/Hero.stories.ts +52 -0
- package/src/stories/Html.stories.ts +178 -0
- package/src/stories/Icon.stories.ts +176 -0
- package/src/stories/Image.stories.ts +613 -0
- package/src/stories/Label.stories.ts +54 -0
- package/src/stories/List.stories.ts +112 -0
- package/src/stories/Modal.stories.ts +123 -0
- package/src/stories/Notification.stories.ts +82 -0
- package/src/stories/Page.stories.ts +414 -0
- package/src/stories/PasswordField.stories.ts +304 -0
- package/src/stories/ProductLabels.stories.ts +65 -0
- package/src/stories/ProductList.stories.ts +679 -0
- package/src/stories/ProductPurchase.stories.ts +807 -0
- package/src/stories/Progress.stories.ts +192 -0
- package/src/stories/Radio.stories.ts +81 -0
- package/src/stories/Section.stories.ts +244 -0
- package/src/stories/SelectBox.stories.ts +377 -0
- package/src/stories/SkeletonLoader.stories.ts +170 -0
- package/src/stories/Slider.stories.ts +79 -0
- package/src/stories/SnsLink.stories.ts +259 -0
- package/src/stories/SoldStacker.stories.ts +68 -0
- package/src/stories/SpinButton.stories.ts +134 -0
- package/src/stories/Spinner.stories.ts +58 -0
- package/src/stories/Stack.stories.ts +104 -0
- package/src/stories/Switch.stories.ts +68 -0
- package/src/stories/Tab.stories.ts +52 -0
- package/src/stories/TabPanels.stories.ts +67 -0
- package/src/stories/Tabs.stories.ts +68 -0
- package/src/stories/Text.stories.ts +69 -0
- package/src/stories/TextArea.stories.ts +78 -0
- package/src/stories/TextField.stories.ts +97 -0
- package/src/stories/ToolChip.stories.ts +125 -0
- package/dist/components/elements/SkeltonLoader.vue.d.ts +0 -7
- 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
|
+
}
|