@reni-corp/reni-2c-ui 0.4.8 → 0.4.9
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/dist/components/elements/Button.vue.d.ts.map +1 -1
- package/dist/components/interactive/Tabs.vue.d.ts.map +1 -1
- package/dist/components/renderless/ItemFilter.vue.d.ts +49 -0
- package/dist/components/renderless/ItemFilter.vue.d.ts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +3711 -3681
- package/dist/mockServiceWorker.js +349 -0
- package/dist/mocks/handlers/index.d.ts +2 -0
- package/dist/mocks/handlers/index.d.ts.map +1 -0
- package/dist/mocks/handlers/products.d.ts +2 -0
- package/dist/mocks/handlers/products.d.ts.map +1 -0
- package/dist/script.es.js +481 -453
- package/dist/script.umd.js +23 -23
- package/dist/style.css +1 -1
- package/package.json +8 -1
- package/src/stories/DataProvider.stories.ts +182 -0
- package/src/stories/ItemFilter.stories.ts +283 -0
- package/dist/components/renderless/OptionGroupSwitchController.vue.d.ts +0 -41
- package/dist/components/renderless/OptionGroupSwitchController.vue.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reni-corp/reni-2c-ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/script.umd.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -107,6 +107,8 @@
|
|
|
107
107
|
"eslint-plugin-vue": "^9.29.0",
|
|
108
108
|
"globals": "^15.12.0",
|
|
109
109
|
"happy-dom": "^20.0.0",
|
|
110
|
+
"msw": "^2.12.10",
|
|
111
|
+
"msw-storybook-addon": "^2.0.6",
|
|
110
112
|
"plop": "^3.1.2",
|
|
111
113
|
"postcss": "^8.5.3",
|
|
112
114
|
"postcss-html": "^1.8.0",
|
|
@@ -130,5 +132,10 @@
|
|
|
130
132
|
"vitest": "^4.0.18",
|
|
131
133
|
"vue-eslint-parser": "^10.2.0",
|
|
132
134
|
"vue-tsc": "^1.8.5"
|
|
135
|
+
},
|
|
136
|
+
"msw": {
|
|
137
|
+
"workerDirectory": [
|
|
138
|
+
"public"
|
|
139
|
+
]
|
|
133
140
|
}
|
|
134
141
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import DataProvider from '@/components/renderless/DataProvider.vue'
|
|
3
|
+
import ProductList from '@/components/features/ProductList.vue'
|
|
4
|
+
import Tabs from '@/components/interactive/Tabs.vue'
|
|
5
|
+
import Tab from '@/components/interactive/Tab.vue'
|
|
6
|
+
import Button from '@/components/elements/Button.vue'
|
|
7
|
+
import Stack from '@/components/layouts/Stack.vue'
|
|
8
|
+
import Text from '@/components/elements/Text.vue'
|
|
9
|
+
import Spinner from '@/components/elements/Spinner.vue'
|
|
10
|
+
import Divider from '@/components/elements/Divider.vue'
|
|
11
|
+
import { ref, computed } from 'vue'
|
|
12
|
+
|
|
13
|
+
const meta: Meta<typeof DataProvider> = {
|
|
14
|
+
title: 'Renderless/DataProvider',
|
|
15
|
+
component: DataProvider,
|
|
16
|
+
tags: ['autodocs'],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default meta
|
|
20
|
+
type Story = StoryObj<typeof DataProvider>
|
|
21
|
+
|
|
22
|
+
const storyComponents = {
|
|
23
|
+
'rn-data-provider': DataProvider,
|
|
24
|
+
'rn-product-list': ProductList,
|
|
25
|
+
'rn-tabs': Tabs,
|
|
26
|
+
'rn-tab': Tab,
|
|
27
|
+
'rn-button': Button,
|
|
28
|
+
'rn-stack': Stack,
|
|
29
|
+
'rn-text': Text,
|
|
30
|
+
'rn-spinner': Spinner,
|
|
31
|
+
'rn-divider': Divider,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const 基本: Story = {
|
|
35
|
+
render: () => ({
|
|
36
|
+
components: storyComponents,
|
|
37
|
+
template: `
|
|
38
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
39
|
+
<rn-data-provider url="/api/products" :limit="8" v-slot="{ data, loading, error }">
|
|
40
|
+
<rn-stack direction="vertical" horizontal-re-size="fill" gap="md">
|
|
41
|
+
<rn-stack v-if="loading" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" style="min-height: 200px;">
|
|
42
|
+
<rn-spinner size="md" />
|
|
43
|
+
</rn-stack>
|
|
44
|
+
<rn-text v-else-if="error" color="danger">{{ error.message }}</rn-text>
|
|
45
|
+
<rn-product-list v-else :data="data" :pc-columns="4" :sp-columns="2" />
|
|
46
|
+
</rn-stack>
|
|
47
|
+
</rn-data-provider>
|
|
48
|
+
</div>
|
|
49
|
+
`,
|
|
50
|
+
}),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const ページネーション: Story = {
|
|
54
|
+
render: () => ({
|
|
55
|
+
components: storyComponents,
|
|
56
|
+
template: `
|
|
57
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
58
|
+
<rn-data-provider
|
|
59
|
+
url="/api/products"
|
|
60
|
+
:limit="8"
|
|
61
|
+
:pagination="true"
|
|
62
|
+
v-slot="{ data, loading, currentPage, next, prev }"
|
|
63
|
+
>
|
|
64
|
+
<rn-stack direction="vertical" horizontal-re-size="fill" gap="lg">
|
|
65
|
+
<rn-stack v-if="loading" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" style="min-height: 200px;">
|
|
66
|
+
<rn-spinner size="md" />
|
|
67
|
+
</rn-stack>
|
|
68
|
+
<template v-else>
|
|
69
|
+
<rn-product-list :data="data" :pc-columns="4" :sp-columns="2" />
|
|
70
|
+
<rn-stack direction="horizontal" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" gap="md">
|
|
71
|
+
<rn-button variant="outlined" size="sm" :disabled="currentPage <= 1" @click="prev">前へ</rn-button>
|
|
72
|
+
<rn-text size="body" weight="bold">{{ currentPage }}</rn-text>
|
|
73
|
+
<rn-button variant="outlined" size="sm" :disabled="data.length === 0" @click="next">次へ</rn-button>
|
|
74
|
+
</rn-stack>
|
|
75
|
+
</template>
|
|
76
|
+
</rn-stack>
|
|
77
|
+
</rn-data-provider>
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
}),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const 無限スクロール風: Story = {
|
|
84
|
+
render: () => ({
|
|
85
|
+
components: storyComponents,
|
|
86
|
+
template: `
|
|
87
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
88
|
+
<rn-data-provider
|
|
89
|
+
url="/api/products"
|
|
90
|
+
:limit="8"
|
|
91
|
+
:pagination="true"
|
|
92
|
+
:accumulate="true"
|
|
93
|
+
v-slot="{ data, loading, moreLoading, next }"
|
|
94
|
+
>
|
|
95
|
+
<rn-stack direction="vertical" horizontal-re-size="fill" gap="lg">
|
|
96
|
+
<rn-stack v-if="loading" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" style="min-height: 200px;">
|
|
97
|
+
<rn-spinner size="md" />
|
|
98
|
+
</rn-stack>
|
|
99
|
+
<rn-product-list
|
|
100
|
+
v-else
|
|
101
|
+
:data="data"
|
|
102
|
+
:pc-columns="4"
|
|
103
|
+
:sp-columns="2"
|
|
104
|
+
:infinite-scroll="true"
|
|
105
|
+
:has-more="data.length % 8 === 0"
|
|
106
|
+
:loading-more="moreLoading"
|
|
107
|
+
@load-more="next"
|
|
108
|
+
/>
|
|
109
|
+
</rn-stack>
|
|
110
|
+
</rn-data-provider>
|
|
111
|
+
</div>
|
|
112
|
+
`,
|
|
113
|
+
}),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const タグ絞り込み_LoadMore: Story = {
|
|
117
|
+
render: () => ({
|
|
118
|
+
components: storyComponents,
|
|
119
|
+
setup() {
|
|
120
|
+
const tabIndex = ref(0)
|
|
121
|
+
const activeTagCode = ref<string | null>(null)
|
|
122
|
+
const productQuery = computed(() =>
|
|
123
|
+
activeTagCode.value ? `tag_codes=${activeTagCode.value}` : '',
|
|
124
|
+
)
|
|
125
|
+
const selectTag = (code: string | null, index: number) => {
|
|
126
|
+
activeTagCode.value = code
|
|
127
|
+
tabIndex.value = index
|
|
128
|
+
}
|
|
129
|
+
return { tabIndex, activeTagCode, productQuery, selectTag }
|
|
130
|
+
},
|
|
131
|
+
template: `
|
|
132
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
133
|
+
<rn-data-provider url="/api/tags" v-slot="{ data: tags, loading: tagsLoading }">
|
|
134
|
+
<rn-stack v-if="tagsLoading" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" style="min-height: 200px;">
|
|
135
|
+
<rn-spinner size="md" />
|
|
136
|
+
</rn-stack>
|
|
137
|
+
<rn-stack v-else direction="vertical" horizontal-re-size="fill" gap="md">
|
|
138
|
+
<div>
|
|
139
|
+
<rn-tabs :modelValue="tabIndex" :is-scrollable="true">
|
|
140
|
+
<rn-tab
|
|
141
|
+
label="すべて"
|
|
142
|
+
:active="!activeTagCode"
|
|
143
|
+
@click="selectTag(null, 0)"
|
|
144
|
+
/>
|
|
145
|
+
<rn-tab
|
|
146
|
+
v-for="(tag, i) in tags"
|
|
147
|
+
:key="tag.code"
|
|
148
|
+
:label="tag.name"
|
|
149
|
+
:active="activeTagCode === tag.code"
|
|
150
|
+
@click="selectTag(tag.code, i + 1)"
|
|
151
|
+
/>
|
|
152
|
+
</rn-tabs>
|
|
153
|
+
<rn-divider />
|
|
154
|
+
</div>
|
|
155
|
+
<rn-data-provider
|
|
156
|
+
url="/api/products"
|
|
157
|
+
:query="productQuery"
|
|
158
|
+
:limit="8"
|
|
159
|
+
:pagination="true"
|
|
160
|
+
:accumulate="true"
|
|
161
|
+
v-slot="{ data: products, loading, moreLoading, next }"
|
|
162
|
+
>
|
|
163
|
+
<rn-stack v-if="loading" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" style="min-height: 200px;">
|
|
164
|
+
<rn-spinner size="md" />
|
|
165
|
+
</rn-stack>
|
|
166
|
+
<rn-product-list
|
|
167
|
+
v-else
|
|
168
|
+
:data="products"
|
|
169
|
+
:pc-columns="4"
|
|
170
|
+
:sp-columns="2"
|
|
171
|
+
:infinite-scroll="true"
|
|
172
|
+
:has-more="products.length % 8 === 0"
|
|
173
|
+
:loading-more="moreLoading"
|
|
174
|
+
@load-more="next"
|
|
175
|
+
/>
|
|
176
|
+
</rn-data-provider>
|
|
177
|
+
</rn-stack>
|
|
178
|
+
</rn-data-provider>
|
|
179
|
+
</div>
|
|
180
|
+
`,
|
|
181
|
+
}),
|
|
182
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import ItemFilter, {
|
|
3
|
+
type ItemFilterProps,
|
|
4
|
+
} from '@/components/renderless/ItemFilter.vue'
|
|
5
|
+
import DataProvider from '@/components/renderless/DataProvider.vue'
|
|
6
|
+
import ProductList from '@/components/features/ProductList.vue'
|
|
7
|
+
import Tabs from '@/components/interactive/Tabs.vue'
|
|
8
|
+
import Tab from '@/components/interactive/Tab.vue'
|
|
9
|
+
import Stack from '@/components/layouts/Stack.vue'
|
|
10
|
+
import Spinner from '@/components/elements/Spinner.vue'
|
|
11
|
+
import Divider from '@/components/elements/Divider.vue'
|
|
12
|
+
import { ref } from 'vue'
|
|
13
|
+
|
|
14
|
+
const meta: Meta<typeof ItemFilter> = {
|
|
15
|
+
title: 'Renderless/ItemFilter',
|
|
16
|
+
component: ItemFilter,
|
|
17
|
+
tags: ['autodocs'],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default meta
|
|
21
|
+
type StoryArgs = ItemFilterProps
|
|
22
|
+
type Story = StoryObj<StoryArgs>
|
|
23
|
+
|
|
24
|
+
// --- タグデータ(実API構造準拠) ---
|
|
25
|
+
const mockTags = [
|
|
26
|
+
{ id: '2649', name: 'Tshirt / Long Sleeve Tshirt', code: 'OMT-T-001' },
|
|
27
|
+
{ id: '2650', name: 'Bag', code: 'OMT-T-002' },
|
|
28
|
+
{ id: '2651', name: 'Other', code: 'OMT-T-003' },
|
|
29
|
+
{ id: '2652', name: 'CD / Cassette / 7inch', code: 'OMT-T-004' },
|
|
30
|
+
{ id: '3266', name: 'Live at 日本武道館', code: 'OMT-T-011' },
|
|
31
|
+
{ id: '3082', name: 'ONE MAN TOUR 2012-2025 Shinka', code: 'OMT-T-010' },
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
// --- 商品データ(タグ多対多) ---
|
|
35
|
+
const tagAssignments = [
|
|
36
|
+
[{ id: '2649', text: 'Tshirt / Long Sleeve Tshirt' }],
|
|
37
|
+
[
|
|
38
|
+
{ id: '2649', text: 'Tshirt / Long Sleeve Tshirt' },
|
|
39
|
+
{ id: '3266', text: 'Live at 日本武道館' },
|
|
40
|
+
],
|
|
41
|
+
[{ id: '2650', text: 'Bag' }],
|
|
42
|
+
[{ id: '2652', text: 'CD / Cassette / 7inch' }],
|
|
43
|
+
[
|
|
44
|
+
{ id: '2651', text: 'Other' },
|
|
45
|
+
{ id: '3082', text: 'ONE MAN TOUR 2012-2025 Shinka' },
|
|
46
|
+
],
|
|
47
|
+
[{ id: '3266', text: 'Live at 日本武道館' }],
|
|
48
|
+
[
|
|
49
|
+
{ id: '2650', text: 'Bag' },
|
|
50
|
+
{ id: '3266', text: 'Live at 日本武道館' },
|
|
51
|
+
],
|
|
52
|
+
[{ id: '3082', text: 'ONE MAN TOUR 2012-2025 Shinka' }],
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
const productNames = [
|
|
56
|
+
'ロゴ Tシャツ',
|
|
57
|
+
'武道館限定 ロングスリーブ',
|
|
58
|
+
'キャンバストートバッグ',
|
|
59
|
+
'1st Album「Ammolite」',
|
|
60
|
+
'Shinka ツアータオル',
|
|
61
|
+
'武道館公演 パンフレット',
|
|
62
|
+
'武道館限定 サコッシュ',
|
|
63
|
+
'Shinka ラバーバンド',
|
|
64
|
+
'フォトTシャツ BLACK',
|
|
65
|
+
'フォトTシャツ WHITE',
|
|
66
|
+
'2nd EP「Pieces」',
|
|
67
|
+
'アクリルキーホルダー',
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
const generateMockProducts = (count: number) => {
|
|
71
|
+
const products = []
|
|
72
|
+
for (let i = 1; i <= count; i++) {
|
|
73
|
+
products.push({
|
|
74
|
+
product_id: `product_${i}`,
|
|
75
|
+
product_code: `OMT-${String(i).padStart(3, '0')}`,
|
|
76
|
+
title: productNames[(i - 1) % productNames.length],
|
|
77
|
+
title2: '',
|
|
78
|
+
price_sale: [2200, 3300, 4400, 5500, 6600, 1650, 3850, 1100][i % 8],
|
|
79
|
+
price_prefix_freeWord: '',
|
|
80
|
+
slide: 1,
|
|
81
|
+
product_url: `https://example.com/products/product_${i}`,
|
|
82
|
+
img_urls: [
|
|
83
|
+
`https://placehold.jp/400x400.png?text=Product${i}`,
|
|
84
|
+
`https://placehold.jp/400x400.png?text=Image${i}-2`,
|
|
85
|
+
],
|
|
86
|
+
label_urls: [],
|
|
87
|
+
label_texts: tagAssignments[(i - 1) % tagAssignments.length],
|
|
88
|
+
soldout_url: '',
|
|
89
|
+
is_soldout: i % 7 === 0,
|
|
90
|
+
is_forced_publish: i % 11 === 0,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
return products
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const mockProducts = generateMockProducts(24)
|
|
97
|
+
|
|
98
|
+
// --- ストーリー共通コンポーネント ---
|
|
99
|
+
const storyComponents = {
|
|
100
|
+
'rn-item-filter': ItemFilter,
|
|
101
|
+
'rn-data-provider': DataProvider,
|
|
102
|
+
'rn-product-list': ProductList,
|
|
103
|
+
'rn-tabs': Tabs,
|
|
104
|
+
'rn-tab': Tab,
|
|
105
|
+
'rn-stack': Stack,
|
|
106
|
+
'rn-spinner': Spinner,
|
|
107
|
+
'rn-divider': Divider,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const 基本: Story = {
|
|
111
|
+
render: () => ({
|
|
112
|
+
components: storyComponents,
|
|
113
|
+
setup() {
|
|
114
|
+
const current = ref(0)
|
|
115
|
+
const tags = mockTags
|
|
116
|
+
const products = mockProducts
|
|
117
|
+
return { current, tags, products }
|
|
118
|
+
},
|
|
119
|
+
template: `
|
|
120
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
121
|
+
<rn-item-filter
|
|
122
|
+
:groups="tags"
|
|
123
|
+
:items="products"
|
|
124
|
+
:item-groups="(p) => p.label_texts"
|
|
125
|
+
all-label="すべて"
|
|
126
|
+
>
|
|
127
|
+
<template #default="{ filteredItems, groups, setActive }">
|
|
128
|
+
<rn-tabs :modelValue="current" :is-scrollable="true">
|
|
129
|
+
<rn-tab
|
|
130
|
+
v-for="(g, index) in groups"
|
|
131
|
+
:key="g.key"
|
|
132
|
+
:label="g.label"
|
|
133
|
+
:active="g.isActive"
|
|
134
|
+
@click="current = index; setActive(g.key)"
|
|
135
|
+
/>
|
|
136
|
+
</rn-tabs>
|
|
137
|
+
<rn-divider />
|
|
138
|
+
<rn-product-list :data="filteredItems" :pc-columns="4" :sp-columns="2" />
|
|
139
|
+
</template>
|
|
140
|
+
</rn-item-filter>
|
|
141
|
+
</div>
|
|
142
|
+
`,
|
|
143
|
+
}),
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const プレフィルタ付き: Story = {
|
|
147
|
+
render: () => ({
|
|
148
|
+
components: storyComponents,
|
|
149
|
+
setup() {
|
|
150
|
+
const current = ref(0)
|
|
151
|
+
const tags = mockTags
|
|
152
|
+
const products = mockProducts
|
|
153
|
+
const availableFilter = (p: any) => !p.is_soldout && !p.is_forced_publish
|
|
154
|
+
return { current, tags, products, availableFilter }
|
|
155
|
+
},
|
|
156
|
+
template: `
|
|
157
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
158
|
+
<rn-item-filter
|
|
159
|
+
:groups="tags"
|
|
160
|
+
:items="products"
|
|
161
|
+
:item-groups="(p) => p.label_texts"
|
|
162
|
+
:filter="availableFilter"
|
|
163
|
+
all-label="すべて"
|
|
164
|
+
>
|
|
165
|
+
<template #default="{ filteredItems, groups, setActive }">
|
|
166
|
+
<rn-tabs :modelValue="current" :is-scrollable="true">
|
|
167
|
+
<rn-tab
|
|
168
|
+
v-for="(g, index) in groups"
|
|
169
|
+
:key="g.key"
|
|
170
|
+
:label="g.label"
|
|
171
|
+
:active="g.isActive"
|
|
172
|
+
@click="current = index; setActive(g.key)"
|
|
173
|
+
/>
|
|
174
|
+
</rn-tabs>
|
|
175
|
+
<rn-divider />
|
|
176
|
+
<rn-product-list :data="filteredItems" :pc-columns="4" :sp-columns="2" />
|
|
177
|
+
</template>
|
|
178
|
+
</rn-item-filter>
|
|
179
|
+
</div>
|
|
180
|
+
`,
|
|
181
|
+
}),
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const キー指定マッチ: Story = {
|
|
185
|
+
render: () => ({
|
|
186
|
+
components: storyComponents,
|
|
187
|
+
setup() {
|
|
188
|
+
const current = ref(0)
|
|
189
|
+
const categories = [
|
|
190
|
+
{ code: 'apparel', name: 'アパレル' },
|
|
191
|
+
{ code: 'goods', name: 'グッズ' },
|
|
192
|
+
{ code: 'media', name: 'CD / メディア' },
|
|
193
|
+
]
|
|
194
|
+
const products = Array.from({ length: 18 }, (_, i) => ({
|
|
195
|
+
product_id: `product_${i + 1}`,
|
|
196
|
+
product_code: `CAT-${String(i + 1).padStart(3, '0')}`,
|
|
197
|
+
title: productNames[i % productNames.length],
|
|
198
|
+
price_sale: [2200, 3300, 4400, 5500, 1650][i % 5],
|
|
199
|
+
price_prefix_freeWord: '',
|
|
200
|
+
slide: 1,
|
|
201
|
+
product_url: `https://example.com/products/product_${i + 1}`,
|
|
202
|
+
img_urls: [`https://placehold.jp/400x400.png?text=Product${i + 1}`],
|
|
203
|
+
label_urls: [],
|
|
204
|
+
label_texts: [],
|
|
205
|
+
soldout_url: '',
|
|
206
|
+
is_soldout: false,
|
|
207
|
+
is_forced_publish: false,
|
|
208
|
+
category: categories[i % categories.length].code,
|
|
209
|
+
}))
|
|
210
|
+
return { current, categories, products }
|
|
211
|
+
},
|
|
212
|
+
template: `
|
|
213
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
214
|
+
<rn-item-filter
|
|
215
|
+
:groups="categories"
|
|
216
|
+
:items="products"
|
|
217
|
+
item-groups="category"
|
|
218
|
+
group-key="code"
|
|
219
|
+
group-label="name"
|
|
220
|
+
all-label="すべて"
|
|
221
|
+
>
|
|
222
|
+
<template #default="{ filteredItems, groups, setActive }">
|
|
223
|
+
<rn-tabs :modelValue="current" :is-scrollable="true">
|
|
224
|
+
<rn-tab
|
|
225
|
+
v-for="(g, index) in groups"
|
|
226
|
+
:key="g.key"
|
|
227
|
+
:label="g.label"
|
|
228
|
+
:active="g.isActive"
|
|
229
|
+
@click="current = index; setActive(g.key)"
|
|
230
|
+
/>
|
|
231
|
+
</rn-tabs>
|
|
232
|
+
<rn-divider />
|
|
233
|
+
<rn-product-list :data="filteredItems" :pc-columns="4" :sp-columns="2" />
|
|
234
|
+
</template>
|
|
235
|
+
</rn-item-filter>
|
|
236
|
+
</div>
|
|
237
|
+
`,
|
|
238
|
+
}),
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const API連携: Story = {
|
|
242
|
+
render: () => ({
|
|
243
|
+
components: storyComponents,
|
|
244
|
+
setup() {
|
|
245
|
+
const current = ref(0)
|
|
246
|
+
return { current }
|
|
247
|
+
},
|
|
248
|
+
template: `
|
|
249
|
+
<div class="sb-canvas" style="padding: 20px;">
|
|
250
|
+
<rn-data-provider url="/api/tags" v-slot="{ data: tags, loading: tagsLoading }">
|
|
251
|
+
<rn-data-provider url="/api/products" :limit="48" v-slot="{ data: products, loading: productsLoading }">
|
|
252
|
+
<rn-stack v-if="tagsLoading || productsLoading" horizontal-re-size="fill" horizontal-align="center" vertical-align="center" style="min-height: 200px;">
|
|
253
|
+
<rn-spinner size="md" />
|
|
254
|
+
</rn-stack>
|
|
255
|
+
<rn-item-filter
|
|
256
|
+
v-else
|
|
257
|
+
:groups="tags"
|
|
258
|
+
:items="products"
|
|
259
|
+
:item-groups="(p) => p.label_texts"
|
|
260
|
+
all-label="すべて"
|
|
261
|
+
>
|
|
262
|
+
<template #default="{ filteredItems, groups, setActive }">
|
|
263
|
+
<rn-stack direction="vertical" horizontal-re-size="fill" gap="md">
|
|
264
|
+
<rn-tabs :modelValue="current" :is-scrollable="true">
|
|
265
|
+
<rn-tab
|
|
266
|
+
v-for="(g, index) in groups"
|
|
267
|
+
:key="g.key"
|
|
268
|
+
:label="g.label"
|
|
269
|
+
:active="g.isActive"
|
|
270
|
+
@click="current = index; setActive(g.key)"
|
|
271
|
+
/>
|
|
272
|
+
</rn-tabs>
|
|
273
|
+
<rn-divider />
|
|
274
|
+
<rn-product-list :data="filteredItems" :pc-columns="4" :sp-columns="2" />
|
|
275
|
+
</rn-stack>
|
|
276
|
+
</template>
|
|
277
|
+
</rn-item-filter>
|
|
278
|
+
</rn-data-provider>
|
|
279
|
+
</rn-data-provider>
|
|
280
|
+
</div>
|
|
281
|
+
`,
|
|
282
|
+
}),
|
|
283
|
+
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export type OptionGroupSwitchControllerProps = {
|
|
2
|
-
itemMapKey: string;
|
|
3
|
-
groupMapKey: string;
|
|
4
|
-
groups: any[];
|
|
5
|
-
items: any[];
|
|
6
|
-
defaultActiveValue: string;
|
|
7
|
-
};
|
|
8
|
-
declare function __VLS_template(): {
|
|
9
|
-
attrs: Partial<{}>;
|
|
10
|
-
slots: {
|
|
11
|
-
default?(_: {
|
|
12
|
-
optionGroups: any[];
|
|
13
|
-
optionGroupByContents: {
|
|
14
|
-
isActive: boolean;
|
|
15
|
-
mapKey: any;
|
|
16
|
-
group: any;
|
|
17
|
-
items: any[];
|
|
18
|
-
}[];
|
|
19
|
-
activeValue: string;
|
|
20
|
-
updateActiveValue: (value: string) => void;
|
|
21
|
-
}): any;
|
|
22
|
-
};
|
|
23
|
-
refs: {};
|
|
24
|
-
rootEl: any;
|
|
25
|
-
};
|
|
26
|
-
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
27
|
-
declare const __VLS_component: import('vue').DefineComponent<OptionGroupSwitchControllerProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<OptionGroupSwitchControllerProps> & Readonly<{}>, {
|
|
28
|
-
items: any[];
|
|
29
|
-
itemMapKey: string;
|
|
30
|
-
groupMapKey: string;
|
|
31
|
-
groups: any[];
|
|
32
|
-
defaultActiveValue: string;
|
|
33
|
-
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
34
|
-
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
35
|
-
export default _default;
|
|
36
|
-
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
37
|
-
new (): {
|
|
38
|
-
$slots: S;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
//# sourceMappingURL=OptionGroupSwitchController.vue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"OptionGroupSwitchController.vue.d.ts","sourceRoot":"","sources":["../../../src/components/renderless/OptionGroupSwitchController.vue"],"names":[],"mappings":"AAQA;AAkEA,MAAM,MAAM,gCAAgC,GAAG;IAC7C,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,GAAG,EAAE,CAAA;IACb,KAAK,EAAE,GAAG,EAAE,CAAA;IACZ,kBAAkB,EAAE,MAAM,CAAA;CAC3B,CAAA;AAwDD,iBAAS,cAAc;WAwBT,OAAO,IAA6B;;;;;;;;;;;uCA/ChB,MAAM;YAqCX,GAAG;;;;EAe/B;AAaD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;WArGZ,GAAG,EAAE;gBAHA,MAAM;iBACL,MAAM;YACX,GAAG,EAAE;wBAEO,MAAM;6EA2G1B,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|