@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,679 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import ProductList, {
3
+ type ProductListProps,
4
+ } from '@/components/features/ProductList.vue'
5
+ import Button from '@/components/elements/Button.vue'
6
+ import Stack from '@/components/layouts/Stack.vue'
7
+ import Divider from '@/components/elements/Divider.vue'
8
+ import { ref } from 'vue'
9
+
10
+ const meta: Meta<typeof ProductList> = {
11
+ title: 'Features/ProductList',
12
+ component: ProductList,
13
+ tags: ['autodocs'],
14
+ argTypes: {
15
+ displayMode: {
16
+ control: 'select',
17
+ options: ['list', 'carousel', 'carousel-peek'],
18
+ },
19
+ paginationType: {
20
+ control: 'select',
21
+ options: ['bullets', 'scrollbar'],
22
+ },
23
+ theme: {
24
+ control: 'select',
25
+ options: ['light', 'dark'],
26
+ },
27
+ itemType: {
28
+ control: 'select',
29
+ options: ['default', 'boxed'],
30
+ },
31
+ pcRows: { control: 'number' },
32
+ pcColumns: { control: 'number' },
33
+ spRows: { control: 'number' },
34
+ spColumns: { control: 'number' },
35
+ breakpoint: { control: 'number' },
36
+ loading: { control: 'boolean' },
37
+ autoplay: { control: 'boolean' },
38
+ infiniteScroll: { control: 'boolean' },
39
+ hasMore: { control: 'boolean' },
40
+ loadingMore: { control: 'boolean' },
41
+ showTitle2: { control: 'boolean' },
42
+ },
43
+ args: {
44
+ displayMode: 'list',
45
+ paginationType: 'bullets',
46
+ theme: 'light',
47
+ itemType: 'default',
48
+ pcRows: 2,
49
+ pcColumns: 4,
50
+ spRows: 2,
51
+ spColumns: 2,
52
+ breakpoint: 768,
53
+ loading: false,
54
+ autoplay: false,
55
+ infiniteScroll: false,
56
+ hasMore: false,
57
+ loadingMore: false,
58
+ showTitle2: true,
59
+ },
60
+ }
61
+
62
+ export default meta
63
+ type StoryArgs = ProductListProps
64
+ type Story = StoryObj<StoryArgs>
65
+
66
+ // モックデータ生成関数
67
+ const generateMockProducts = (count: number) => {
68
+ const products = []
69
+ for (let i = 1; i <= count; i++) {
70
+ let labelUrls: string[] = []
71
+ let labelTexts: { id: string; text: string }[] = []
72
+ let soldoutUrl = ''
73
+
74
+ // 各商品のラベル設定
75
+ if (i % 7 === 1) {
76
+ // 単一ラベル(7個に1個)
77
+ labelUrls = ['https://assets.official-goods-store.jp/label/YUR001.png']
78
+ } else if (i % 7 === 2) {
79
+ // 複数ラベル(7個に1個)
80
+ labelUrls = [
81
+ 'https://assets.official-goods-store.jp/label/YUR001.png',
82
+ 'https://assets.official-goods-store.jp/label/KTK007.png',
83
+ ]
84
+ } else if (i % 7 === 3) {
85
+ // Soldoutラベル(7個に1個)
86
+ soldoutUrl =
87
+ 'https://official-goods-store.jp/yuuri/assets/img/yuuri/soldout.png'
88
+ }
89
+
90
+ if (i % 7 === 1) {
91
+ // 単一ラベルテキスト(7個に1個)
92
+ labelTexts = [{ id: 'YUR001', text: '受注生産' }]
93
+ } else if (i % 7 === 2) {
94
+ // 複数ラベルテキスト(7個に1個)
95
+ labelTexts = [
96
+ { id: 'YUR001', text: '受注生産' },
97
+ { id: 'KTK007', text: '新商品' },
98
+ ]
99
+ }
100
+
101
+ products.push({
102
+ product_id: `product_${i}`,
103
+ product_code: `ZMY-DESIGN3-${String(i).padStart(3, '0')}`,
104
+ title: `ZUTOMAYO 商品 ${i}`,
105
+ title2: `THE WORLD IS CHANGING ${i}`,
106
+ price_sale: Math.floor(Math.random() * 8000) + 1000,
107
+ price_prefix_freeWord: '', // 空文字(CurrencyValueで円記号が自動追加される)
108
+ slide: 1,
109
+ product_url: `https://example.com/products/product_${i}`,
110
+ img_urls: [
111
+ `https://placehold.jp/400x400.png?text=Product${i}`,
112
+ `https://placehold.jp/400x400.png?text=Image${i}-2`,
113
+ `https://placehold.jp/400x400.png?text=Image${i}-3`,
114
+ `https://placehold.jp/400x400.png?text=Image${i}-4`,
115
+ ],
116
+ label_urls: labelUrls,
117
+ label_texts: labelTexts,
118
+ soldout_url: soldoutUrl,
119
+ is_forced_publish: i % 5 === 0, // 5個に1個は受付期間外
120
+ })
121
+ }
122
+ return products
123
+ }
124
+
125
+ export const 基本リスト: Story = {
126
+ args: {
127
+ displayMode: 'list',
128
+ data: generateMockProducts(12),
129
+ },
130
+ render: (args: StoryArgs) => ({
131
+ components: {
132
+ 'rn-product-list': ProductList,
133
+ },
134
+ setup() {
135
+ return { args }
136
+ },
137
+ template: `
138
+ <div class="sb-canvas" style="padding: 20px;">
139
+ <h3>商品リスト(基本)</h3>
140
+ <rn-product-list v-bind="args" />
141
+ </div>
142
+ `,
143
+ }),
144
+ }
145
+
146
+ export const カルーセル: Story = {
147
+ args: {
148
+ displayMode: 'carousel',
149
+ paginationType: 'bullets',
150
+ data: generateMockProducts(16),
151
+ },
152
+ render: (args: StoryArgs) => ({
153
+ components: {
154
+ 'rn-product-list': ProductList,
155
+ },
156
+ setup() {
157
+ return { args }
158
+ },
159
+ template: `
160
+ <div class="sb-canvas" style="padding: 20px;">
161
+ <h3>商品カルーセル(ドット)</h3>
162
+ <rn-product-list v-bind="args" />
163
+ </div>
164
+ `,
165
+ }),
166
+ }
167
+
168
+ export const カルーセルピーク: Story = {
169
+ args: {
170
+ displayMode: 'carousel-peek',
171
+ data: generateMockProducts(10),
172
+ },
173
+ render: (args: StoryArgs) => ({
174
+ components: {
175
+ 'rn-product-list': ProductList,
176
+ },
177
+ setup() {
178
+ return { args }
179
+ },
180
+ template: `
181
+ <div class="sb-canvas" style="padding: 20px;">
182
+ <h3>商品カルーセル(ピーク効果)</h3>
183
+ <p>モバイルサイズでピーク効果を確認できます</p>
184
+ <rn-product-list v-bind="args" />
185
+ </div>
186
+ `,
187
+ }),
188
+ }
189
+
190
+ export const スクロールバー: Story = {
191
+ args: {
192
+ displayMode: 'carousel',
193
+ paginationType: 'scrollbar',
194
+ data: generateMockProducts(20),
195
+ },
196
+ render: (args: StoryArgs) => ({
197
+ components: {
198
+ 'rn-product-list': ProductList,
199
+ },
200
+ setup() {
201
+ return { args }
202
+ },
203
+ template: `
204
+ <div class="sb-canvas" style="padding: 20px;">
205
+ <h3>カルーセル(スクロールバー)</h3>
206
+ <rn-product-list v-bind="args" />
207
+ </div>
208
+ `,
209
+ }),
210
+ }
211
+
212
+ export const ダークテーマ: Story = {
213
+ args: {
214
+ displayMode: 'carousel',
215
+ theme: 'dark',
216
+ data: generateMockProducts(12),
217
+ },
218
+ render: (args: StoryArgs) => ({
219
+ components: {
220
+ 'rn-product-list': ProductList,
221
+ },
222
+ setup() {
223
+ return { args }
224
+ },
225
+ template: `
226
+ <div class="sb-canvas" style="padding: 20px; background: #1a1a1a;">
227
+ <h3 style="color: white;">商品リスト(ダークテーマ)</h3>
228
+ <rn-product-list v-bind="args" />
229
+ </div>
230
+ `,
231
+ }),
232
+ }
233
+
234
+ export const ローディング状態: Story = {
235
+ args: {
236
+ loading: true,
237
+ displayMode: 'list',
238
+ data: [],
239
+ },
240
+ render: (args: StoryArgs) => ({
241
+ components: {
242
+ 'rn-product-list': ProductList,
243
+ },
244
+ setup() {
245
+ return { args }
246
+ },
247
+ template: `
248
+ <div class="sb-canvas" style="padding: 20px;">
249
+ <h3>ローディング状態</h3>
250
+ <rn-product-list v-bind="args" />
251
+ </div>
252
+ `,
253
+ }),
254
+ }
255
+
256
+ export const エラー状態: Story = {
257
+ args: {
258
+ error: new Error('商品データの読み込みに失敗しました'),
259
+ displayMode: 'list',
260
+ data: [],
261
+ },
262
+ render: (args: StoryArgs) => ({
263
+ components: {
264
+ 'rn-product-list': ProductList,
265
+ },
266
+ setup() {
267
+ return { args }
268
+ },
269
+ template: `
270
+ <div class="sb-canvas" style="padding: 20px;">
271
+ <h3>エラー状態</h3>
272
+ <rn-product-list v-bind="args" />
273
+ </div>
274
+ `,
275
+ }),
276
+ }
277
+
278
+ export const 無限スクロール: Story = {
279
+ args: {
280
+ displayMode: 'list',
281
+ infiniteScroll: true,
282
+ hasMore: true,
283
+ loadingMore: false,
284
+ data: generateMockProducts(8),
285
+ },
286
+ render: (args: StoryArgs) => ({
287
+ components: {
288
+ 'rn-product-list': ProductList,
289
+ },
290
+ setup() {
291
+ const products = ref([...args.data])
292
+ const loadingMore = ref(false)
293
+ const hasMore = ref(true)
294
+
295
+ const handleLoadMore = async () => {
296
+ loadingMore.value = true
297
+
298
+ // 模拟API调用
299
+ await new Promise((resolve) => setTimeout(resolve, 1500))
300
+
301
+ // 新しい商品を追加
302
+ const newProducts = generateMockProducts(4).map((item, index) => ({
303
+ ...item,
304
+ product_id: `product_${products.value.length + index + 1}`,
305
+ product_code: `ZMY-DESIGN3-${String(products.value.length + index + 1).padStart(3, '0')}`,
306
+ title: `ZUTOMAYO 商品 ${products.value.length + index + 1}`,
307
+ }))
308
+
309
+ products.value.push(...newProducts)
310
+ loadingMore.value = false
311
+
312
+ // 40個で終了
313
+ if (products.value.length >= 40) {
314
+ hasMore.value = false
315
+ }
316
+ }
317
+
318
+ return {
319
+ products,
320
+ loadingMore,
321
+ hasMore,
322
+ handleLoadMore,
323
+ }
324
+ },
325
+ template: `
326
+ <div class="sb-canvas" style="padding: 20px;">
327
+ <h3>無限スクロール</h3>
328
+ <p>下にスクロールすると自動的に追加読み込みします</p>
329
+ <rn-product-list
330
+ :data="products"
331
+ :infinite-scroll="true"
332
+ :has-more="hasMore"
333
+ :loading-more="loadingMore"
334
+ display-mode="list"
335
+ @load-more="handleLoadMore"
336
+ />
337
+ </div>
338
+ `,
339
+ }),
340
+ }
341
+
342
+ export const appendスロット: Story = {
343
+ args: {
344
+ displayMode: 'list',
345
+ hasMore: true,
346
+ loadingMore: false,
347
+ data: generateMockProducts(8),
348
+ },
349
+ render: (args: StoryArgs) => ({
350
+ components: {
351
+ 'rn-product-list': ProductList,
352
+ 'rn-button': Button,
353
+ },
354
+ setup() {
355
+ const products = ref([...args.data])
356
+ const loadingMore = ref(false)
357
+ const hasMore = ref(true)
358
+
359
+ const handleLoadMore = async () => {
360
+ loadingMore.value = true
361
+
362
+ await new Promise((resolve) => setTimeout(resolve, 1000))
363
+
364
+ const newProducts = generateMockProducts(4).map((item, index) => ({
365
+ ...item,
366
+ product_id: `product_${products.value.length + index + 1}`,
367
+ product_code: `ZMY-DESIGN3-${String(products.value.length + index + 1).padStart(3, '0')}`,
368
+ title: `ZUTOMAYO 商品 ${products.value.length + index + 1}`,
369
+ }))
370
+
371
+ products.value.push(...newProducts)
372
+ loadingMore.value = false
373
+
374
+ if (products.value.length >= 24) {
375
+ hasMore.value = false
376
+ }
377
+ }
378
+
379
+ return {
380
+ products,
381
+ loadingMore,
382
+ hasMore,
383
+ handleLoadMore,
384
+ }
385
+ },
386
+ template: `
387
+ <div class="sb-canvas" style="padding: 20px;">
388
+ <h3>appendスロット活用</h3>
389
+ <rn-product-list
390
+ :data="products"
391
+ :has-more="hasMore"
392
+ :loading-more="loadingMore"
393
+ display-mode="list"
394
+ @load-more="handleLoadMore"
395
+ >
396
+ <template #append="{ hasMore, loadingMore, loadMore, totalItems }">
397
+ <div style="text-align: center; padding: 20px;" v-if="hasMore">
398
+ <rn-button
399
+ @click="loadMore"
400
+ :loading="loadingMore"
401
+ variant="fill"
402
+ color="primary"
403
+ >
404
+ さらに読み込む({{ totalItems }}件表示中)
405
+ </rn-button>
406
+ </div>
407
+ <div v-else style="text-align: center; padding: 20px; color: #666;">
408
+ すべての商品を表示しました({{ totalItems }}件)
409
+ </div>
410
+ </template>
411
+ </rn-product-list>
412
+ </div>
413
+ `,
414
+ }),
415
+ }
416
+
417
+ export const footerスロット: Story = {
418
+ args: {
419
+ displayMode: 'list',
420
+ data: generateMockProducts(12),
421
+ },
422
+ render: (args: StoryArgs) => ({
423
+ components: {
424
+ 'rn-product-list': ProductList,
425
+ 'rn-button': Button,
426
+ 'rn-stack': Stack,
427
+ 'rn-divider': Divider,
428
+ },
429
+ setup() {
430
+ return { args }
431
+ },
432
+ template: `
433
+ <div class="sb-canvas" style="padding: 20px;">
434
+ <h3>footerスロット活用</h3>
435
+ <rn-product-list v-bind="args">
436
+ <template #footer="{ totalItems, displayMode }">
437
+ <rn-divider />
438
+ <rn-stack direction="vertical" gap="md" horizontal-align="center" style="padding: 24px 0;">
439
+ <p style="color: #666; margin: 0;">
440
+ {{ totalItems }}件の商品を{{ displayMode }}形式で表示中
441
+ </p>
442
+ <rn-stack direction="horizontal" gap="sm" horizontal-align="center">
443
+ <rn-button href="/products/all" variant="fill" color="primary">
444
+ すべての商品を見る
445
+ </rn-button>
446
+ <rn-button href="/categories" variant="text" color="default">
447
+ カテゴリから探す
448
+ </rn-button>
449
+ </rn-stack>
450
+ </rn-stack>
451
+ </template>
452
+ </rn-product-list>
453
+ </div>
454
+ `,
455
+ }),
456
+ }
457
+
458
+ export const 両方のスロット使用: Story = {
459
+ args: {
460
+ displayMode: 'carousel',
461
+ hasMore: true,
462
+ loadingMore: false,
463
+ data: generateMockProducts(8),
464
+ },
465
+ render: (args: StoryArgs) => ({
466
+ components: {
467
+ 'rn-product-list': ProductList,
468
+ 'rn-button': Button,
469
+ 'rn-stack': Stack,
470
+ 'rn-divider': Divider,
471
+ },
472
+ setup() {
473
+ const products = ref([...args.data])
474
+ const loadingMore = ref(false)
475
+ const hasMore = ref(true)
476
+
477
+ const handleLoadMore = async () => {
478
+ loadingMore.value = true
479
+ await new Promise((resolve) => setTimeout(resolve, 1200))
480
+
481
+ const newProducts = generateMockProducts(4).map((item, index) => ({
482
+ ...item,
483
+ product_id: `product_${products.value.length + index + 1}`,
484
+ product_code: `ZMY-DESIGN3-${String(products.value.length + index + 1).padStart(3, '0')}`,
485
+ title: `ZUTOMAYO 商品 ${products.value.length + index + 1}`,
486
+ }))
487
+
488
+ products.value.push(...newProducts)
489
+ loadingMore.value = false
490
+
491
+ if (products.value.length >= 20) {
492
+ hasMore.value = false
493
+ }
494
+ }
495
+
496
+ return {
497
+ products,
498
+ loadingMore,
499
+ hasMore,
500
+ handleLoadMore,
501
+ }
502
+ },
503
+ template: `
504
+ <div class="sb-canvas" style="padding: 20px;">
505
+ <h3>appendスロット + footerスロット</h3>
506
+ <rn-product-list
507
+ :data="products"
508
+ :has-more="hasMore"
509
+ :loading-more="loadingMore"
510
+ display-mode="carousel"
511
+ @load-more="handleLoadMore"
512
+ >
513
+ <template #append="{ hasMore, loadingMore, loadMore }">
514
+ <div v-if="hasMore" style="display: flex; align-items: center; justify-content: center; padding: 0 10px;">
515
+ <rn-button
516
+ @click="loadMore"
517
+ :loading="loadingMore"
518
+ variant="text"
519
+ color="primary"
520
+ size="sm"
521
+ >
522
+ もっと →
523
+ </rn-button>
524
+ </div>
525
+ </template>
526
+
527
+ <template #footer="{ totalItems, displayMode }">
528
+ <rn-divider />
529
+ <rn-stack direction="vertical" gap="md" horizontal-align="center" style="padding: 24px 0;">
530
+ <p style="color: #666; margin: 0;">
531
+ {{ totalItems }}件の注目商品をご紹介
532
+ </p>
533
+ <rn-stack direction="horizontal" gap="sm" horizontal-align="center">
534
+ <rn-button href="/featured" variant="fill" color="primary">
535
+ 注目商品一覧
536
+ </rn-button>
537
+ <rn-button href="/new-arrivals" variant="text" color="default">
538
+ 新着商品
539
+ </rn-button>
540
+ </rn-stack>
541
+ </rn-stack>
542
+ </template>
543
+ </rn-product-list>
544
+ </div>
545
+ `,
546
+ }),
547
+ }
548
+
549
+ export const アイテムスロット設定: Story = {
550
+ args: {
551
+ displayMode: 'list',
552
+ pcRows: 2,
553
+ pcColumns: 5,
554
+ spRows: 3,
555
+ spColumns: 2,
556
+ breakpoint: 768,
557
+ data: generateMockProducts(20),
558
+ },
559
+ render: (args: StoryArgs) => ({
560
+ components: {
561
+ 'rn-product-list': ProductList,
562
+ },
563
+ setup() {
564
+ return { args }
565
+ },
566
+ template: `
567
+ <div class="sb-canvas" style="padding: 20px;">
568
+ <rn-text size="title">アイテムスロット活用 - ProductItemComponent利用</rn-text>
569
+ <rn-product-list v-bind="args">
570
+ <template #item="props">
571
+ <a :href="props.item.product_url" target="_blank">
572
+ <rn-product-list-item
573
+ :style="{ pointerEvents: 'none' }"
574
+ :item="props.item"
575
+ :autoplay="props.autoplay"
576
+ :type="props.itemType"
577
+ />
578
+ </a>
579
+ </template>
580
+ </rn-product-list>
581
+
582
+ <br><br><br>
583
+
584
+ <rn-text size="title">アイテムスロット活用 - 独自コンポーネント</rn-text>
585
+ <rn-product-list v-bind="args">
586
+ <template #item="{item}">
587
+ <rn-stack direction="vertical" gap="xs">
588
+ <rn-image :src="item.img_urls[0]" />
589
+ <rn-text size="caption">カスタマイズ表記テキスト</rn-text>
590
+ <rn-text>{{ item.title }}</rn-text>
591
+ </rn-stack>
592
+ </template>
593
+ </rn-product-list>
594
+ </div>
595
+ `,
596
+ }),
597
+ }
598
+
599
+ export const レスポンシブ設定: Story = {
600
+ args: {
601
+ displayMode: 'list',
602
+ pcRows: 2,
603
+ pcColumns: 5,
604
+ spRows: 3,
605
+ spColumns: 2,
606
+ breakpoint: 768,
607
+ data: generateMockProducts(20),
608
+ },
609
+ render: (args: StoryArgs) => ({
610
+ components: {
611
+ 'rn-product-list': ProductList,
612
+ },
613
+ setup() {
614
+ return { args }
615
+ },
616
+ template: `
617
+ <div class="sb-canvas" style="padding: 20px;">
618
+ <h3>レスポンシブ設定</h3>
619
+ <p>PC: 5列×2行 / SP: 2列×3行(ブラウザサイズを変更して確認)</p>
620
+ <rn-product-list v-bind="args" />
621
+ </div>
622
+ `,
623
+ }),
624
+ }
625
+
626
+ export const title2表示制御: Story = {
627
+ args: {
628
+ displayMode: 'list',
629
+ data: generateMockProducts(8),
630
+ showTitle2: true,
631
+ },
632
+ render: (args: StoryArgs) => ({
633
+ components: {
634
+ 'rn-product-list': ProductList,
635
+ 'rn-button': Button,
636
+ 'rn-stack': Stack,
637
+ },
638
+ setup() {
639
+ const showTitle2 = ref(args.showTitle2)
640
+
641
+ const toggleTitle2 = () => {
642
+ showTitle2.value = !showTitle2.value
643
+ }
644
+
645
+ return {
646
+ args: { ...args, showTitle2 },
647
+ toggleTitle2,
648
+ showTitle2,
649
+ }
650
+ },
651
+ template: `
652
+ <div class="sb-canvas" style="padding: 20px;">
653
+ <rn-stack direction="vertical" gap="md">
654
+ <div>
655
+ <h3>title2表示制御</h3>
656
+ <p>showTitle2プロパティでtitle2(サブタイトル)の表示・非表示を制御できます</p>
657
+ </div>
658
+
659
+ <rn-stack direction="horizontal" gap="sm">
660
+ <rn-button
661
+ @click="toggleTitle2"
662
+ :variant="showTitle2 ? 'fill' : 'outline'"
663
+ color="primary"
664
+ >
665
+ {{ showTitle2 ? 'title2を非表示' : 'title2を表示' }}
666
+ </rn-button>
667
+ <span style="color: #666;">現在の設定: {{ showTitle2 ? '表示' : '非表示' }}</span>
668
+ </rn-stack>
669
+
670
+ <rn-product-list
671
+ :data="args.data"
672
+ :display-mode="args.displayMode"
673
+ :show-title2="showTitle2"
674
+ />
675
+ </rn-stack>
676
+ </div>
677
+ `,
678
+ }),
679
+ }