@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.
Files changed (101) hide show
  1. package/README.md +139 -16
  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 +102 -0
  26. package/dist/components/features/ProductPurchase.vue.d.ts.map +1 -0
  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 +11 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.es.js +7442 -6048
  40. package/dist/script.es.js +8881 -7489
  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 +22 -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,870 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref, computed } from 'vue'
3
+ import AppFrame from '@/components/foundation/AppFrame.vue'
4
+ import AppBar, {
5
+ AppBarAction,
6
+ AppBarMenuItem,
7
+ } from '@/components/foundation/AppBar.vue'
8
+ import AppBody from '@/components/foundation/AppBody.vue'
9
+ import AppFooter, {
10
+ SnsLinkItem,
11
+ LogoItem,
12
+ } from '@/components/foundation/AppFooter.vue'
13
+ import { MenuItem } from '@/types'
14
+ import Page from '@/components/layouts/Page.vue'
15
+ import Section from '@/components/layouts/Section.vue'
16
+ import Stack from '@/components/layouts/Stack.vue'
17
+ import Button from '@/components/elements/Button.vue'
18
+ import Text from '@/components/elements/Text.vue'
19
+ import Icon from '@/components/elements/Icon.vue'
20
+ import Divider from '@/components/elements/Divider.vue'
21
+ import Label from '@/components/elements/Label.vue'
22
+ import SpinButton from '@/components/elements/SpinButton.vue'
23
+ import Gallery from '@/components/interactive/Gallery.vue'
24
+ import AnnounceBar from '@/components/features/AnnounceBar.vue'
25
+ import BrandLogo from '@/components/features/BrandLogo.vue'
26
+ import SnsLink from '@/components/features/SnsLink.vue'
27
+ import ProductList from '@/components/features/ProductList.vue'
28
+ import VariationSelector from '@/components/features/VariationSelector.vue'
29
+ import Image from '@/components/elements/Image.vue'
30
+ import Spinner from '@/components/elements/Spinner.vue'
31
+ import Card from '@/components/layouts/Card.vue'
32
+ import Alert from '@/components/elements/Alert.vue'
33
+
34
+ const meta: Meta<typeof AppFrame> = {
35
+ title: 'Example/配送通販',
36
+ parameters: {
37
+ layout: 'fullscreen',
38
+ docs: {
39
+ description: {
40
+ component: `
41
+ アプリケーション全体のレイアウト構成例です。AppFrame、AppBar、AppBody、AppFooterを組み合わせた実際の使用例を示します。
42
+
43
+ ## レイアウト仕様
44
+ - **AppBar高さ**: PC 72px / SP 56px
45
+ - **X方向パディング**: PC 40px / SP 16px(AppBar, AppBody, AppFooter共通)
46
+ - **AppBar SP時**: CSS Grid 3カラムでタイトル中央配置
47
+
48
+ ## AppFooter レスポンシブ
49
+ - **子供ありメニュー**: PC=セクション表示 / SP=Disclosure折りたたみ
50
+ - **子供なしメニュー**: PC=横並び / SP=縦リスト
51
+ `,
52
+ },
53
+ },
54
+ },
55
+ }
56
+
57
+ export default meta
58
+ type Story = StoryObj<typeof AppFrame>
59
+
60
+ // モックデータ
61
+ const mockShopData = {
62
+ shopName: 'Sample Store',
63
+ shopUrl: '/',
64
+ logoUrl: 'https://placehold.co/200x60?text=LOGO',
65
+ logoWidth: 120,
66
+ }
67
+
68
+ // AppBar用メニュー
69
+ const mockAppBarMenu: AppBarMenuItem[] = [
70
+ { title: '新着商品', url: '/products?sort=new' },
71
+ {
72
+ title: 'カテゴリ',
73
+ children: [
74
+ { title: 'トップス', url: '/category/tops' },
75
+ { title: 'ボトムス', url: '/category/bottoms' },
76
+ { title: 'アクセサリー', url: '/category/accessories' },
77
+ ],
78
+ },
79
+ { title: 'ランキング', url: '/products?sort=ranking' },
80
+ { title: 'セール', url: '/sale' },
81
+ ]
82
+
83
+ // AppBar用アクション
84
+ const mockAppBarActions: AppBarAction[] = [
85
+ {
86
+ title: '検索',
87
+ icon: ['far', 'magnifying-glass'],
88
+ action: () => console.log('検索'),
89
+ },
90
+ { title: 'アカウント', icon: ['far', 'user'], url: '/account' },
91
+ { title: 'カート', icon: ['far', 'cart-shopping'], url: '/cart' },
92
+ ]
93
+
94
+ // AppFooter用データ
95
+ const mockFooterLogos: LogoItem[] = [
96
+ {
97
+ href: '/',
98
+ logoUrl: 'https://placehold.co/160x40?text=LOGO',
99
+ title: 'Sample Store',
100
+ width: 120,
101
+ },
102
+ ]
103
+
104
+ const mockFooterSnsLinks: SnsLinkItem[] = [
105
+ { type: 'x-twitter', href: 'https://twitter.com/' },
106
+ { type: 'instagram', href: 'https://instagram.com/' },
107
+ { type: 'youtube', href: 'https://youtube.com/' },
108
+ ]
109
+
110
+ // 子供ありフッターメニュー
111
+ const mockFooterMenusWithChildren: MenuItem[] = [
112
+ {
113
+ title: 'サポート',
114
+ children: [
115
+ { title: 'FAQ', url: '/faq' },
116
+ { title: 'お問い合わせ', url: '/contact' },
117
+ { title: '配送について', url: '/shipping' },
118
+ ],
119
+ },
120
+ {
121
+ title: '規約・免責',
122
+ children: [
123
+ { title: '利用規約', url: '/terms' },
124
+ { title: 'プライバシーポリシー', url: '/privacy' },
125
+ { title: '特定商取引法に基づく表記', url: '/legal' },
126
+ ],
127
+ },
128
+ {
129
+ title: 'アカウント',
130
+ children: [
131
+ { title: 'サインアップ', url: '/signup' },
132
+ { title: 'ログイン', url: '/login' },
133
+ ],
134
+ },
135
+ ]
136
+
137
+ // 商品詳細ページ用モックデータ
138
+ const mockProduct = {
139
+ title: 'サンプル商品名',
140
+ title2: 'ブランド名',
141
+ title3: '商品コード: ABC-123',
142
+ price_sale: '3,980',
143
+ comment:
144
+ 'この商品は高品質な素材を使用した人気アイテムです。\n\n快適な着心地と耐久性を両立しています。\n\n日常使いからアウトドアまで幅広いシーンでお使いいただけます。',
145
+ img_urls: [
146
+ 'https://placehold.co/600x600/e0e0e0/666666?text=Image+1',
147
+ 'https://placehold.co/600x600/d0d0d0/666666?text=Image+2',
148
+ 'https://placehold.co/600x600/c0c0c0/666666?text=Image+3',
149
+ 'https://placehold.co/600x600/b0b0b0/666666?text=Image+4',
150
+ ],
151
+ tags: [
152
+ { tag_code: 'new', name: '新着' },
153
+ { tag_code: 'popular', name: '人気' },
154
+ { tag_code: 'limited', name: '限定' },
155
+ ],
156
+ }
157
+
158
+ const mockVariationOptions = [
159
+ { label: 'S', value: 's' },
160
+ { label: 'M', value: 'm' },
161
+ { label: 'L', value: 'l' },
162
+ { label: 'XL', value: 'xl', disabled: true },
163
+ ]
164
+
165
+ const mockRelatedProducts = [
166
+ {
167
+ product_id: '1',
168
+ title: '関連商品1',
169
+ price_sale: 2980,
170
+ img_urls: ['https://placehold.co/300x300/e0e0e0/666666?text=Product+1'],
171
+ },
172
+ {
173
+ product_id: '2',
174
+ title: '関連商品2',
175
+ price_sale: 3480,
176
+ img_urls: ['https://placehold.co/300x300/d0d0d0/666666?text=Product+2'],
177
+ },
178
+ {
179
+ product_id: '3',
180
+ title: '関連商品3',
181
+ price_sale: 4980,
182
+ img_urls: ['https://placehold.co/300x300/c0c0c0/666666?text=Product+3'],
183
+ },
184
+ {
185
+ product_id: '4',
186
+ title: '関連商品4',
187
+ price_sale: 2480,
188
+ img_urls: ['https://placehold.co/300x300/b0b0b0/666666?text=Product+4'],
189
+ },
190
+ ]
191
+
192
+ export const 商品詳細ページ: Story = {
193
+ render: () => ({
194
+ components: {
195
+ 'rn-app-frame': AppFrame,
196
+ 'rn-app-bar': AppBar,
197
+ 'rn-app-body': AppBody,
198
+ 'rn-app-footer': AppFooter,
199
+ 'rn-page': Page,
200
+ 'rn-section': Section,
201
+ 'rn-stack': Stack,
202
+ 'rn-button': Button,
203
+ 'rn-text': Text,
204
+ 'rn-icon': Icon,
205
+ 'rn-divider': Divider,
206
+ 'rn-label': Label,
207
+ 'rn-spin-button': SpinButton,
208
+ 'rn-gallery': Gallery,
209
+ 'rn-announce-bar': AnnounceBar,
210
+ 'rn-product-list': ProductList,
211
+ 'rn-variation-selector': VariationSelector,
212
+ },
213
+ setup() {
214
+ const variation = ref('m')
215
+ const quantity = ref(1)
216
+
217
+ const handleAddToCart = () => {
218
+ console.log('カートに追加:', {
219
+ variation: variation.value,
220
+ quantity: quantity.value,
221
+ })
222
+ }
223
+
224
+ return {
225
+ // Shop data
226
+ shopData: mockShopData,
227
+ appBarMenu: mockAppBarMenu,
228
+ appBarActions: mockAppBarActions,
229
+ // Footer data
230
+ footerLogos: mockFooterLogos,
231
+ footerSnsLinks: mockFooterSnsLinks,
232
+ footerMenus: mockFooterMenusWithChildren,
233
+ // Product data
234
+ product: mockProduct,
235
+ variationOptions: mockVariationOptions,
236
+ relatedProducts: mockRelatedProducts,
237
+ // State
238
+ variation,
239
+ quantity,
240
+ // Methods
241
+ handleAddToCart,
242
+ }
243
+ },
244
+ template: /* html */ `
245
+ <rn-app-frame>
246
+ <!-- AppBar - Propsベース -->
247
+ <rn-app-bar
248
+ :title-logo="shopData.logoUrl"
249
+ :title="shopData.shopName"
250
+ :menu="appBarMenu"
251
+ :actions="appBarActions"
252
+ />
253
+
254
+ <!-- AppBody -->
255
+ <rn-app-body>
256
+ <rn-page sticky-mode="body" :main-ratio="60">
257
+ <!-- Banner: フルワイズ表示 -->
258
+ <template #banner>
259
+ <rn-announce-bar
260
+ color="info"
261
+ title="🎉 期間限定セール開催中!全品10%OFF"
262
+ />
263
+ </template>
264
+
265
+ <template #body>
266
+ <rn-gallery
267
+ :max-visible-thumbs="4"
268
+ :image-urls="product.img_urls"
269
+ :autoplay="false"
270
+ />
271
+ </template>
272
+ <template #aside>
273
+ <rn-stack horizontal-resize="fill" direction="vertical" gap="lg">
274
+ <!-- 商品情報 -->
275
+ <rn-stack direction="vertical">
276
+ <rn-stack direction="vertical" gap="xs">
277
+ <rn-text>{{ product.title2 }}</rn-text>
278
+ <rn-text size="title">{{ product.title }}</rn-text>
279
+ <rn-text size="caption">{{ product.title3 }}</rn-text>
280
+ </rn-stack>
281
+ <rn-text>
282
+ ¥{{ product.price_sale }}
283
+ <rn-text size="caption">(税込)</rn-text>
284
+ </rn-text>
285
+ </rn-stack>
286
+
287
+ <!-- バリエーション選択 -->
288
+ <rn-stack direction="vertical">
289
+ <rn-text>サイズを選択</rn-text>
290
+ <rn-variation-selector
291
+ v-model="variation"
292
+ :options="variationOptions"
293
+ />
294
+ </rn-stack>
295
+
296
+ <!-- 数量選択 -->
297
+ <rn-stack direction="vertical">
298
+ <rn-text>数量</rn-text>
299
+ <rn-spin-button
300
+ v-model="quantity"
301
+ size="lg"
302
+ :min="1"
303
+ :max="10"
304
+ />
305
+ </rn-stack>
306
+
307
+ <!-- カートボタン -->
308
+ <rn-button block @click="handleAddToCart">
309
+ カートに入れる
310
+ </rn-button>
311
+
312
+ <rn-divider />
313
+
314
+ <!-- 商品説明 -->
315
+ <rn-stack direction="vertical" gap="xs">
316
+ <rn-text weight="bold">商品説明</rn-text>
317
+ <rn-text style="white-space: pre-line;">{{ product.comment }}</rn-text>
318
+ </rn-stack>
319
+
320
+ <!-- タグ -->
321
+ <rn-stack v-if="product.tags.length" wrap="wrap" gap="sm">
322
+ <rn-text weight="bold">Tags:</rn-text>
323
+ <rn-stack wrap="wrap" gap="sm">
324
+ <rn-button
325
+ v-for="tag in product.tags"
326
+ :key="tag.tag_code"
327
+ variant="text"
328
+ >
329
+ <rn-label>#{{ tag.name }}</rn-label>
330
+ </rn-button>
331
+ </rn-stack>
332
+ </rn-stack>
333
+ </rn-stack>
334
+ </template>
335
+
336
+ <!-- 関連商品 -->
337
+ <template #bottom>
338
+ <rn-section title="関連する商品">
339
+ <template #body>
340
+ <rn-product-list
341
+ :data="relatedProducts"
342
+ :loading="false"
343
+ :error="null"
344
+ :pc-columns="4"
345
+ :sp-columns="2"
346
+ :pc-rows="1"
347
+ :sp-rows="1"
348
+ />
349
+ </template>
350
+ </rn-section>
351
+ </template>
352
+ </rn-page>
353
+ </rn-app-body>
354
+
355
+ <!-- AppFooter - Propsベース -->
356
+ <rn-app-footer
357
+ :logos="footerLogos"
358
+ :sns-links="footerSnsLinks"
359
+ :menus="footerMenus"
360
+ copyright="© 2024 Sample Store. All rights reserved."
361
+ />
362
+ </rn-app-frame>
363
+ `,
364
+ }),
365
+ }
366
+
367
+ export const Slotカスタマイズ版: Story = {
368
+ render: () => ({
369
+ components: {
370
+ 'rn-app-frame': AppFrame,
371
+ 'rn-app-bar': AppBar,
372
+ 'rn-app-body': AppBody,
373
+ 'rn-app-footer': AppFooter,
374
+ 'rn-section': Section,
375
+ 'rn-stack': Stack,
376
+ 'rn-button': Button,
377
+ 'rn-text': Text,
378
+ 'rn-icon': Icon,
379
+ 'rn-brand-logo': BrandLogo,
380
+ 'rn-sns-link': SnsLink,
381
+ },
382
+ setup() {
383
+ return {
384
+ shopData: mockShopData,
385
+ footerSnsLinks: mockFooterSnsLinks,
386
+ footerLogos: mockFooterLogos,
387
+ }
388
+ },
389
+ template: /* html */ `
390
+ <rn-app-frame>
391
+ <!-- AppBar - Slotベースでカスタマイズ -->
392
+ <rn-app-bar>
393
+ <template #title>
394
+ <rn-brand-logo
395
+ :href="shopData.shopUrl"
396
+ :title="shopData.shopName"
397
+ :logo-url="shopData.logoUrl"
398
+ :width="shopData.logoWidth"
399
+ height="auto"
400
+ />
401
+ </template>
402
+ <template #menu>
403
+ <rn-stack direction="horizontal" gap="sm">
404
+ <a href="/new">新着</a>
405
+ <a href="/ranking">ランキング</a>
406
+ <a href="/sale">セール</a>
407
+ </rn-stack>
408
+ </template>
409
+ <template #actions>
410
+ <rn-icon :icon="['far', 'magnifying-glass']" />
411
+ <rn-icon :icon="['far', 'user']" />
412
+ <rn-icon :icon="['far', 'cart-shopping']" />
413
+ </template>
414
+ <template #extra>
415
+ <div style="background: #fef3c7; padding: 8px 16px; text-align: center; font-size: 12px;">
416
+ 🎉 期間限定セール開催中!全品10%OFF
417
+ </div>
418
+ </template>
419
+ </rn-app-bar>
420
+
421
+ <!-- AppBody -->
422
+ <rn-app-body>
423
+ <rn-section title="Slotカスタマイズ版">
424
+ <template #body>
425
+ <rn-text>
426
+ このストーリーはAppBar/AppFooterのSlotを使ったカスタマイズ例です。
427
+ </rn-text>
428
+ <rn-stack direction="vertical" gap="md" style="margin-top: 24px;">
429
+ <rn-text weight="bold">レイアウト仕様:</rn-text>
430
+ <ul style="margin: 0; padding-left: 20px;">
431
+ <li>AppBar高さ: PC 72px / SP 56px</li>
432
+ <li>X方向パディング: PC 40px / SP 16px</li>
433
+ <li>SP時: CSS Grid 3カラムでタイトル中央配置</li>
434
+ </ul>
435
+ </rn-stack>
436
+ </template>
437
+ </rn-section>
438
+ </rn-app-body>
439
+
440
+ <!-- AppFooter - Slotベース -->
441
+ <rn-app-footer>
442
+ <template #logos>
443
+ <rn-brand-logo
444
+ v-for="logo in footerLogos"
445
+ :key="logo.logoUrl"
446
+ :href="logo.href"
447
+ :title="logo.title || ''"
448
+ :logo-url="logo.logoUrl"
449
+ :width="logo.width || 120"
450
+ height="auto"
451
+ />
452
+ </template>
453
+ <template #sns-links>
454
+ <rn-sns-link
455
+ v-for="sns in footerSnsLinks"
456
+ :key="sns.type"
457
+ :type="sns.type"
458
+ :href="sns.href"
459
+ variant="icon"
460
+ />
461
+ </template>
462
+ <template #menus>
463
+ <rn-stack direction="horizontal" gap="xl">
464
+ <rn-stack direction="vertical">
465
+ <rn-text weight="bold">カスタムセクション1</rn-text>
466
+ <rn-stack direction="vertical" gap="sm">
467
+ <a href="#">リンク1</a>
468
+ <a href="#">リンク2</a>
469
+ </rn-stack>
470
+ </rn-stack>
471
+ <rn-stack direction="vertical">
472
+ <rn-text weight="bold">カスタムセクション2</rn-text>
473
+ <rn-stack direction="vertical" gap="sm">
474
+ <a href="#">リンク3</a>
475
+ <a href="#">リンク4</a>
476
+ </rn-stack>
477
+ </rn-stack>
478
+ </rn-stack>
479
+ </template>
480
+ <template #copyright>
481
+ <rn-text size="caption">© 2024 Sample Store. All rights reserved.</rn-text>
482
+ </template>
483
+ </rn-app-footer>
484
+ </rn-app-frame>
485
+ `,
486
+ }),
487
+ }
488
+
489
+ // カートページ用モックデータ
490
+ interface CartItem {
491
+ product_sku_id: string
492
+ product_name: string
493
+ unit_price: number
494
+ quantity: number
495
+ image_url: string
496
+ }
497
+
498
+ const mockCartItems: CartItem[] = [
499
+ {
500
+ product_sku_id: 'sku-001',
501
+ product_name: 'オリジナルTシャツ / ブラック / Lサイズ',
502
+ unit_price: 3980,
503
+ quantity: 2,
504
+ image_url: 'https://placehold.co/100x100/333333/ffffff?text=T-Shirt',
505
+ },
506
+ {
507
+ product_sku_id: 'sku-002',
508
+ product_name: 'トートバッグ / ナチュラル',
509
+ unit_price: 2480,
510
+ quantity: 1,
511
+ image_url: 'https://placehold.co/100x100/e0d5c0/666666?text=Tote',
512
+ },
513
+ {
514
+ product_sku_id: 'sku-003',
515
+ product_name: 'アクリルキーホルダー / 限定デザインA',
516
+ unit_price: 880,
517
+ quantity: 3,
518
+ image_url: 'https://placehold.co/100x100/ffcccc/666666?text=Key',
519
+ },
520
+ ]
521
+
522
+ // 価格フォーマット関数
523
+ const formatPrice = (price: number): string => {
524
+ return price.toLocaleString('ja-JP')
525
+ }
526
+
527
+ export const カートページ: Story = {
528
+ render: () => ({
529
+ components: {
530
+ 'rn-app-frame': AppFrame,
531
+ 'rn-app-bar': AppBar,
532
+ 'rn-app-body': AppBody,
533
+ 'rn-app-footer': AppFooter,
534
+ 'rn-page': Page,
535
+ 'rn-stack': Stack,
536
+ 'rn-button': Button,
537
+ 'rn-text': Text,
538
+ 'rn-icon': Icon,
539
+ 'rn-divider': Divider,
540
+ 'rn-spin-button': SpinButton,
541
+ 'rn-image': Image,
542
+ 'rn-card': Card,
543
+ 'rn-alert': Alert,
544
+ 'rn-spinner': Spinner,
545
+ },
546
+ setup() {
547
+ const cartItems = ref<CartItem[]>([...mockCartItems])
548
+ const isLoading = ref(false)
549
+ const error = ref<{ message: string } | null>(null)
550
+
551
+ // 商品小計
552
+ const productSubtotal = computed(() =>
553
+ cartItems.value.reduce(
554
+ (sum, item) => sum + item.unit_price * item.quantity,
555
+ 0
556
+ )
557
+ )
558
+
559
+ // 手数料(固定値)
560
+ const otherFee = computed(() => 500)
561
+
562
+ // 合計
563
+ const total = computed(() => productSubtotal.value + otherFee.value)
564
+
565
+ // 数量変更
566
+ const handleQuantityChange = (item: CartItem, newQuantity: number) => {
567
+ const target = cartItems.value.find(
568
+ (i) => i.product_sku_id === item.product_sku_id
569
+ )
570
+ if (target) {
571
+ target.quantity = newQuantity
572
+ }
573
+ }
574
+
575
+ // 削除
576
+ const deleteCartListItemHandle = (item: CartItem) => {
577
+ cartItems.value = cartItems.value.filter(
578
+ (i) => i.product_sku_id !== item.product_sku_id
579
+ )
580
+ }
581
+
582
+ return {
583
+ // Shop data
584
+ shopData: mockShopData,
585
+ appBarMenu: mockAppBarMenu,
586
+ appBarActions: mockAppBarActions,
587
+ // Footer data
588
+ footerLogos: mockFooterLogos,
589
+ footerSnsLinks: mockFooterSnsLinks,
590
+ footerMenus: mockFooterMenusWithChildren,
591
+ // Cart data
592
+ cartItems,
593
+ isLoading,
594
+ error,
595
+ productSubtotal,
596
+ otherFee,
597
+ total,
598
+ // Methods
599
+ handleQuantityChange,
600
+ deleteCartListItemHandle,
601
+ formatPrice,
602
+ }
603
+ },
604
+ template: /* html */ `
605
+ <rn-app-frame>
606
+ <rn-app-bar
607
+ :title-logo="shopData.logoUrl"
608
+ :title="shopData.shopName"
609
+ :menu="appBarMenu"
610
+ :actions="appBarActions"
611
+ />
612
+
613
+ <rn-app-body>
614
+ <rn-page title="カート" sticky-mode="aside" :main-ratio="70">
615
+ <template #extra>
616
+ <rn-alert v-if="error" :message="error.message" type="error" />
617
+ </template>
618
+
619
+ <template #body>
620
+ <rn-stack horizontal-re-size="fill" direction="vertical">
621
+ <rn-spinner v-if="isLoading" />
622
+ <template v-else-if="cartItems.length > 0">
623
+ <rn-stack direction="vertical" horizontal-re-size="fill">
624
+ <template v-for="item in cartItems" :key="item.product_sku_id">
625
+ <rn-stack horizontal-re-size="fill">
626
+ <rn-image
627
+ v-if="item.image_url"
628
+ :src="item.image_url"
629
+ alt="商品画像"
630
+ width="100"
631
+ height="100"
632
+ />
633
+ <rn-stack direction="vertical" horizontal-re-size="fill">
634
+ <rn-stack direction="vertical" gap="xs">
635
+ <rn-text>{{ item.product_name }}</rn-text>
636
+ <rn-text color="subtle">¥{{ formatPrice(item.unit_price) }}</rn-text>
637
+ </rn-stack>
638
+ <rn-stack
639
+ vertical-align="center"
640
+ horizontal-re-size="fill"
641
+ horizontal-align="between"
642
+ >
643
+ <rn-stack vertical-align="center">
644
+ <rn-spin-button
645
+ :model-value="item.quantity"
646
+ :min="1"
647
+ :show-delete-at-min="true"
648
+ @update:model-value="(newValue) => handleQuantityChange(item, newValue)"
649
+ @delete="deleteCartListItemHandle(item)"
650
+ />
651
+ <rn-button variant="text" @click="deleteCartListItemHandle(item)">
652
+ <rn-icon :icon="['far', 'trash']" />
653
+ </rn-button>
654
+ </rn-stack>
655
+ <rn-text>¥{{ formatPrice(item.unit_price * item.quantity) }}</rn-text>
656
+ </rn-stack>
657
+ </rn-stack>
658
+ </rn-stack>
659
+ <rn-divider />
660
+ </template>
661
+ </rn-stack>
662
+ </template>
663
+ <template v-else>
664
+ <rn-stack
665
+ horizontal-re-size="fill"
666
+ direction="vertical"
667
+ horizontal-align="center"
668
+ vertical-align="center"
669
+ style="min-height: 400px"
670
+ >
671
+ <rn-text size="subtitle" weight="bold">カートに商品はありません。</rn-text>
672
+ <rn-button variant="text">
673
+ <rn-icon :icon="['far', 'arrow-left']" size="sm" />
674
+ TOPに戻る
675
+ </rn-button>
676
+ </rn-stack>
677
+ </template>
678
+ </rn-stack>
679
+ </template>
680
+
681
+ <template #aside>
682
+ <rn-stack v-if="cartItems.length > 0" horizontal-re-size="fill" direction="vertical">
683
+ <rn-stack horizontal-re-size="fill" direction="vertical">
684
+ <rn-stack horizontal-re-size="fill" horizontal-align="between">
685
+ <rn-text>商品小計</rn-text>
686
+ <rn-text>¥{{ formatPrice(productSubtotal) }}</rn-text>
687
+ </rn-stack>
688
+ <rn-stack horizontal-re-size="fill" horizontal-align="between">
689
+ <rn-text>手数料</rn-text>
690
+ <rn-text>¥{{ formatPrice(otherFee) }}</rn-text>
691
+ </rn-stack>
692
+ <rn-stack horizontal-re-size="fill" horizontal-align="between">
693
+ <rn-text>合計(税込)</rn-text>
694
+ <rn-text>¥{{ formatPrice(total) }}</rn-text>
695
+ </rn-stack>
696
+ </rn-stack>
697
+
698
+ <rn-stack horizontal-re-size="fill">
699
+ <rn-text size="caption" color="subtle">
700
+ *決済手数料はチェックアウト時に計算されます。
701
+ </rn-text>
702
+ </rn-stack>
703
+
704
+ <rn-stack horizontal-re-size="fill">
705
+ <rn-button :style="{ width: '100%' }">購入の手続きに進む</rn-button>
706
+ </rn-stack>
707
+
708
+ <rn-stack horizontal-re-size="fill" horizontal-align="center">
709
+ <rn-button variant="text">
710
+ <rn-stack direction="horizontal" vertical-align="center" gap="sm">
711
+ <rn-icon :icon="['fas', 'chevron-left']" size="sm" color="subtle" />
712
+ <rn-text color="subtle">買い物を続ける</rn-text>
713
+ </rn-stack>
714
+ </rn-button>
715
+ </rn-stack>
716
+
717
+ <rn-card :elevation="false" color="muted" variant="tonal">
718
+ <template #header>
719
+ <rn-stack gap="xs" vertical-align="center">
720
+ <rn-icon :icon="['far', 'circle-info']" size="sm" />
721
+ <rn-text size="subtitle" weight="bold">注意事項</rn-text>
722
+ </rn-stack>
723
+ </template>
724
+ <rn-stack direction="vertical" gap="xs">
725
+ <rn-text size="caption">
726
+ ※ご注文確定するまでは、カートに商品が入っていても在庫は確保されません。予めご了承ください。
727
+ </rn-text>
728
+ <rn-text size="caption">
729
+ ※在庫の少ない商品は、ご注文手続き中に在庫切れとなる場合があります。
730
+ </rn-text>
731
+ <rn-text size="caption">
732
+ ※ご注文手続き中に、ご注文商品の販売期間が終了してしまった場合、ご注文はできません
733
+ </rn-text>
734
+ </rn-stack>
735
+ </rn-card>
736
+ </rn-stack>
737
+ </template>
738
+ </rn-page>
739
+ </rn-app-body>
740
+
741
+ <rn-app-footer
742
+ :logos="footerLogos"
743
+ :sns-links="footerSnsLinks"
744
+ :menus="footerMenus"
745
+ copyright="© 2024 Sample Store. All rights reserved."
746
+ />
747
+ </rn-app-frame>
748
+ `,
749
+ }),
750
+ }
751
+
752
+ export const カートページ_空の状態: Story = {
753
+ render: () => ({
754
+ components: {
755
+ 'rn-app-frame': AppFrame,
756
+ 'rn-app-bar': AppBar,
757
+ 'rn-app-body': AppBody,
758
+ 'rn-app-footer': AppFooter,
759
+ 'rn-page': Page,
760
+ 'rn-stack': Stack,
761
+ 'rn-button': Button,
762
+ 'rn-text': Text,
763
+ 'rn-icon': Icon,
764
+ },
765
+ setup() {
766
+ return {
767
+ shopData: mockShopData,
768
+ appBarMenu: mockAppBarMenu,
769
+ appBarActions: mockAppBarActions,
770
+ footerLogos: mockFooterLogos,
771
+ footerSnsLinks: mockFooterSnsLinks,
772
+ footerMenus: mockFooterMenusWithChildren,
773
+ }
774
+ },
775
+ template: /* html */ `
776
+ <rn-app-frame>
777
+ <rn-app-bar
778
+ :title-logo="shopData.logoUrl"
779
+ :title="shopData.shopName"
780
+ :menu="appBarMenu"
781
+ :actions="appBarActions"
782
+ />
783
+
784
+ <rn-app-body>
785
+ <rn-page title="カート" sticky-mode="aside" :main-ratio="70">
786
+ <template #body>
787
+ <rn-stack
788
+ horizontal-re-size="fill"
789
+ direction="vertical"
790
+ horizontal-align="center"
791
+ vertical-align="center"
792
+ style="min-height: 400px"
793
+ >
794
+ <rn-text size="subtitle" weight="bold">カートに商品はありません。</rn-text>
795
+ <rn-button variant="text">
796
+ <rn-icon :icon="['far', 'arrow-left']" size="sm" />
797
+ TOPに戻る
798
+ </rn-button>
799
+ </rn-stack>
800
+ </template>
801
+ </rn-page>
802
+ </rn-app-body>
803
+
804
+ <rn-app-footer
805
+ :logos="footerLogos"
806
+ :sns-links="footerSnsLinks"
807
+ :menus="footerMenus"
808
+ copyright="© 2024 Sample Store. All rights reserved."
809
+ />
810
+ </rn-app-frame>
811
+ `,
812
+ }),
813
+ }
814
+
815
+ export const カートページ_ローディング: Story = {
816
+ render: () => ({
817
+ components: {
818
+ 'rn-app-frame': AppFrame,
819
+ 'rn-app-bar': AppBar,
820
+ 'rn-app-body': AppBody,
821
+ 'rn-app-footer': AppFooter,
822
+ 'rn-page': Page,
823
+ 'rn-stack': Stack,
824
+ 'rn-spinner': Spinner,
825
+ },
826
+ setup() {
827
+ return {
828
+ shopData: mockShopData,
829
+ appBarMenu: mockAppBarMenu,
830
+ appBarActions: mockAppBarActions,
831
+ footerLogos: mockFooterLogos,
832
+ footerSnsLinks: mockFooterSnsLinks,
833
+ footerMenus: mockFooterMenusWithChildren,
834
+ }
835
+ },
836
+ template: /* html */ `
837
+ <rn-app-frame>
838
+ <rn-app-bar
839
+ :title-logo="shopData.logoUrl"
840
+ :title="shopData.shopName"
841
+ :menu="appBarMenu"
842
+ :actions="appBarActions"
843
+ />
844
+
845
+ <rn-app-body>
846
+ <rn-page title="カート" sticky-mode="aside" :main-ratio="70">
847
+ <template #body>
848
+ <rn-stack
849
+ horizontal-re-size="fill"
850
+ direction="vertical"
851
+ horizontal-align="center"
852
+ vertical-align="center"
853
+ style="min-height: 400px"
854
+ >
855
+ <rn-spinner />
856
+ </rn-stack>
857
+ </template>
858
+ </rn-page>
859
+ </rn-app-body>
860
+
861
+ <rn-app-footer
862
+ :logos="footerLogos"
863
+ :sns-links="footerSnsLinks"
864
+ :menus="footerMenus"
865
+ copyright="© 2024 Sample Store. All rights reserved."
866
+ />
867
+ </rn-app-frame>
868
+ `,
869
+ }),
870
+ }