fulldev-ui 0.5.0-beta.5 → 0.5.0-beta.6
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/CONTRIBUTING.md +10 -4
- package/astro.config.ts +0 -2
- package/package.json +4 -2
- package/pnpm-lock.yaml +31 -69
- package/src/blocks/Banner.astro +2 -1
- package/src/blocks/Product.astro +11 -21
- package/src/components/Card.astro +1 -1
- package/src/components/Cart.astro +36 -0
- package/src/components/CartForm.astro +121 -0
- package/src/components/CartList.astro +134 -0
- package/src/components/CartSummary.astro +146 -0
- package/src/components/Drawer.astro +31 -7
- package/src/components/Form.astro +49 -0
- package/src/components/Header.astro +2 -9
- package/src/components/Input.astro +6 -0
- package/src/components/Price.astro +7 -11
- package/src/components/Search.astro +1 -4
- package/src/components/experimental/Form.astro +29 -5
- package/src/content/config.ts +2 -1
- package/src/content/pages/base/select.mdx +0 -8
- package/src/content/pages/base/textarea.mdx +0 -5
- package/src/content/presets/docs.yml +0 -1
- package/src/env.d.ts +1 -1
- package/src/layouts/Layout.astro +4 -0
- package/src/schemas/pageSchema.ts +53 -28
- package/src/schemas/presetSchema.ts +3 -0
- package/src/stores/cart.ts +23 -0
- package/src/utils/cart.ts +36 -0
- package/tsconfig.json +1 -3
- /package/src/types/{global-props.d.ts → global-props.ts} +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Button from './Button.astro'
|
|
3
|
+
import Element from './Element.astro'
|
|
4
|
+
import Price from './Price.astro'
|
|
5
|
+
import Text from './Text.astro'
|
|
6
|
+
|
|
7
|
+
interface Props {}
|
|
8
|
+
|
|
9
|
+
const { ...rest } = Astro.props
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<Element
|
|
13
|
+
size="md"
|
|
14
|
+
class="cart-summary"
|
|
15
|
+
>
|
|
16
|
+
<div
|
|
17
|
+
class="row"
|
|
18
|
+
{...rest}
|
|
19
|
+
>
|
|
20
|
+
<Text
|
|
21
|
+
class:list="total"
|
|
22
|
+
text="Totaal"
|
|
23
|
+
contrast
|
|
24
|
+
/>
|
|
25
|
+
<Price value={10} />
|
|
26
|
+
</div>
|
|
27
|
+
<Text
|
|
28
|
+
class:list="shipping"
|
|
29
|
+
text="Verzendkosten worden berekend bij het afrekenen."
|
|
30
|
+
/>
|
|
31
|
+
<Button
|
|
32
|
+
class:list="checkout-button"
|
|
33
|
+
variant="primary"
|
|
34
|
+
color="brand"
|
|
35
|
+
text="Afrekenen"
|
|
36
|
+
/>
|
|
37
|
+
</Element>
|
|
38
|
+
|
|
39
|
+
<style is:global lang="scss">
|
|
40
|
+
@layer fulldev {
|
|
41
|
+
.cart-summary {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: var(--space-3);
|
|
45
|
+
|
|
46
|
+
.price,
|
|
47
|
+
.total {
|
|
48
|
+
font-size: var(--text-4);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.row {
|
|
52
|
+
display: flex;
|
|
53
|
+
justify-content: space-between;
|
|
54
|
+
align-items: center;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.shipping {
|
|
58
|
+
font-size: var(--text-1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
63
|
+
|
|
64
|
+
<script>
|
|
65
|
+
import { $cart } from 'fulldev-ui/stores/cart.ts'
|
|
66
|
+
import { formatPrice } from 'fulldev-ui/utils/cart'
|
|
67
|
+
import Stripe from 'stripe'
|
|
68
|
+
|
|
69
|
+
const stripe = new Stripe(import.meta.env.PUBLIC_STRIPE_CHECKOUT_KEY)
|
|
70
|
+
|
|
71
|
+
function setPrice(items: any) {
|
|
72
|
+
const price = document.querySelector('.cart-summary .price')
|
|
73
|
+
if (price)
|
|
74
|
+
price.textContent = formatPrice(
|
|
75
|
+
items.reduce(
|
|
76
|
+
(acc: number, item: { price: number }) => acc + item.price,
|
|
77
|
+
0
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
document.addEventListener('astro:page-load', () => {
|
|
83
|
+
setPrice($cart.get())
|
|
84
|
+
|
|
85
|
+
$cart.subscribe((items: any) => {
|
|
86
|
+
setPrice(items)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const button = document.querySelector('.cart-summary .checkout-button')
|
|
90
|
+
|
|
91
|
+
button?.addEventListener('click', async () => {
|
|
92
|
+
const items = $cart.get()
|
|
93
|
+
|
|
94
|
+
const checkoutConfig = {
|
|
95
|
+
success_url: `${import.meta.env.PUBLIC_SITE_URL}/checkout`,
|
|
96
|
+
mode: 'payment',
|
|
97
|
+
customer_creation: 'always',
|
|
98
|
+
shipping_address_collection: {
|
|
99
|
+
allowed_countries: ['NL'],
|
|
100
|
+
},
|
|
101
|
+
phone_number_collection: {
|
|
102
|
+
enabled: true,
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const lineItems = items.map((item: any) => ({
|
|
108
|
+
price_data: {
|
|
109
|
+
currency: 'eur',
|
|
110
|
+
product: item.id,
|
|
111
|
+
},
|
|
112
|
+
quantity: 1,
|
|
113
|
+
}))
|
|
114
|
+
|
|
115
|
+
// @ts-ignore
|
|
116
|
+
const checkoutSession = await stripe.checkout.sessions.create({
|
|
117
|
+
line_items: lineItems,
|
|
118
|
+
...checkoutConfig,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
checkoutSession.url && window.location.replace(checkoutSession.url)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(error)
|
|
124
|
+
|
|
125
|
+
const lineItems = items.map((item: any) => ({
|
|
126
|
+
quantity: 1,
|
|
127
|
+
price_data: {
|
|
128
|
+
currency: 'eur',
|
|
129
|
+
product_data: {
|
|
130
|
+
name: item.name,
|
|
131
|
+
},
|
|
132
|
+
unit_amount: Math.round(item.price * 100),
|
|
133
|
+
},
|
|
134
|
+
}))
|
|
135
|
+
|
|
136
|
+
// @ts-ignore
|
|
137
|
+
const checkoutSession = await stripe.checkout.sessions.create({
|
|
138
|
+
line_items: lineItems,
|
|
139
|
+
...checkoutConfig,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
checkoutSession.url && window.location.replace(checkoutSession.url)
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
</script>
|
|
@@ -75,6 +75,12 @@ const {
|
|
|
75
75
|
<Menu groups={menu} />
|
|
76
76
|
<slot />
|
|
77
77
|
</Element>
|
|
78
|
+
<Element
|
|
79
|
+
class:list="drawer-footer"
|
|
80
|
+
as="footer"
|
|
81
|
+
>
|
|
82
|
+
<slot name="footer" />
|
|
83
|
+
</Element>
|
|
78
84
|
</Element>
|
|
79
85
|
</Element>
|
|
80
86
|
|
|
@@ -99,7 +105,7 @@ const {
|
|
|
99
105
|
max-height: 100%;
|
|
100
106
|
|
|
101
107
|
@media (min-width: 768px) {
|
|
102
|
-
width:
|
|
108
|
+
width: 440px;
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
&::backdrop {
|
|
@@ -108,19 +114,35 @@ const {
|
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
.drawer-body {
|
|
117
|
+
display: flex;
|
|
111
118
|
flex-shrink: 1;
|
|
119
|
+
flex-direction: column;
|
|
112
120
|
border-right: 1px solid var(--color-a6);
|
|
113
121
|
height: 100%;
|
|
114
122
|
overflow: auto;
|
|
115
123
|
}
|
|
116
124
|
|
|
117
|
-
.drawer-
|
|
125
|
+
.drawer-content {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
padding: var(--space-6) var(--gutter);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.drawer-header,
|
|
132
|
+
.drawer-footer {
|
|
118
133
|
display: flex;
|
|
119
134
|
position: sticky;
|
|
135
|
+
flex-shrink: 0;
|
|
136
|
+
z-index: 1;
|
|
137
|
+
border-style: solid;
|
|
138
|
+
border-color: var(--color-a6);
|
|
139
|
+
background-color: var(--base-background);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.drawer-header {
|
|
120
143
|
top: 0;
|
|
121
144
|
align-items: center;
|
|
122
|
-
border-bottom: 1px
|
|
123
|
-
background-color: var(--base-background);
|
|
145
|
+
border-bottom-width: 1px;
|
|
124
146
|
padding: 0 var(--gutter);
|
|
125
147
|
height: 56px;
|
|
126
148
|
|
|
@@ -129,10 +151,12 @@ const {
|
|
|
129
151
|
}
|
|
130
152
|
}
|
|
131
153
|
|
|
132
|
-
.drawer-
|
|
133
|
-
|
|
154
|
+
.drawer-footer {
|
|
155
|
+
bottom: 0;
|
|
134
156
|
flex-direction: column;
|
|
135
|
-
|
|
157
|
+
margin-top: auto;
|
|
158
|
+
border-top-width: 1px;
|
|
159
|
+
padding: var(--space-5) var(--gutter);
|
|
136
160
|
}
|
|
137
161
|
}
|
|
138
162
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ComponentProps, HTMLAttributes } from 'astro/types'
|
|
3
|
+
import Button from 'fulldev-ui/components/Button.astro'
|
|
4
|
+
import Element from 'fulldev-ui/components/Element.astro'
|
|
5
|
+
import Input from 'fulldev-ui/components/Input.astro'
|
|
6
|
+
import Select from 'fulldev-ui/components/Select.astro'
|
|
7
|
+
import Textarea from 'fulldev-ui/components/Textarea.astro'
|
|
8
|
+
import type { GlobalProps } from 'fulldev-ui/types/global-props'
|
|
9
|
+
|
|
10
|
+
interface Props extends GlobalProps, HTMLAttributes<'form'> {
|
|
11
|
+
fields: (
|
|
12
|
+
| ComponentProps<typeof Input>
|
|
13
|
+
| ComponentProps<typeof Select>
|
|
14
|
+
| (ComponentProps<typeof Textarea> & { type?: 'textarea' | undefined })
|
|
15
|
+
)[]
|
|
16
|
+
submit?: ComponentProps<typeof Button>['text']
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { fields, submit, ...rest } = Astro.props
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<Element
|
|
23
|
+
class:list="form"
|
|
24
|
+
as="form"
|
|
25
|
+
{...rest}
|
|
26
|
+
>
|
|
27
|
+
{
|
|
28
|
+
fields.map((field) => {
|
|
29
|
+
if ('options' in field) return <Select {...field} />
|
|
30
|
+
if ('type' in field && field.type === 'textarea')
|
|
31
|
+
return <Textarea {...field} />
|
|
32
|
+
return <Input {...(field as any)} />
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
<Button
|
|
36
|
+
type="submit"
|
|
37
|
+
text={submit}
|
|
38
|
+
/>
|
|
39
|
+
</Element>
|
|
40
|
+
|
|
41
|
+
<style is:global lang="scss">
|
|
42
|
+
.form {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
gap: var(--space-4);
|
|
46
|
+
width: 100%;
|
|
47
|
+
max-width: calc(var(--space-4) * 44);
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
@@ -3,9 +3,9 @@ import type { ComponentProps, HTMLAttributes } from 'astro/types'
|
|
|
3
3
|
import Buttons from 'fulldev-ui/components/Buttons.astro'
|
|
4
4
|
import Element from 'fulldev-ui/components/Element.astro'
|
|
5
5
|
import Logo from 'fulldev-ui/components/experimental/Logo.astro'
|
|
6
|
-
import Search from 'fulldev-ui/components/Search.astro'
|
|
7
6
|
import ThemeSelect from 'fulldev-ui/components/ThemeSelect.astro'
|
|
8
7
|
import type { GlobalProps } from 'fulldev-ui/types/global-props'
|
|
8
|
+
import Cart from './Cart.astro'
|
|
9
9
|
import Drawer from './Drawer.astro'
|
|
10
10
|
import type Menu from './Menu.astro'
|
|
11
11
|
|
|
@@ -52,7 +52,6 @@ const {
|
|
|
52
52
|
{buttons}
|
|
53
53
|
variant="link"
|
|
54
54
|
/>
|
|
55
|
-
{search && <Search />}
|
|
56
55
|
{themeselect && <ThemeSelect />}
|
|
57
56
|
<Drawer
|
|
58
57
|
icon="menu-2"
|
|
@@ -61,13 +60,7 @@ const {
|
|
|
61
60
|
{logo}
|
|
62
61
|
{company}
|
|
63
62
|
/>
|
|
64
|
-
<
|
|
65
|
-
icon="shopping-cart"
|
|
66
|
-
class="cart-button"
|
|
67
|
-
color="brand"
|
|
68
|
-
id="cart"
|
|
69
|
-
heading={cart}
|
|
70
|
-
/>
|
|
63
|
+
<Cart heading={cart} />
|
|
71
64
|
</Element>
|
|
72
65
|
|
|
73
66
|
<style lang="scss" is:global>
|
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
disabled,
|
|
18
18
|
required,
|
|
19
19
|
placeholder,
|
|
20
|
+
readonly,
|
|
20
21
|
name = placeholder,
|
|
21
22
|
type = 'text',
|
|
22
23
|
...rest
|
|
@@ -41,6 +42,7 @@ const {
|
|
|
41
42
|
{value}
|
|
42
43
|
{disabled}
|
|
43
44
|
{required}
|
|
45
|
+
{readonly}
|
|
44
46
|
/>
|
|
45
47
|
<Icon name={icon} />
|
|
46
48
|
</Element>
|
|
@@ -97,6 +99,10 @@ const {
|
|
|
97
99
|
cursor: not-allowed;
|
|
98
100
|
pointer-events: none;
|
|
99
101
|
}
|
|
102
|
+
|
|
103
|
+
&[readonly] {
|
|
104
|
+
pointer-events: none;
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types'
|
|
3
3
|
import Element from 'fulldev-ui/components/Element.astro'
|
|
4
4
|
import type { GlobalProps } from 'fulldev-ui/types/global-props'
|
|
5
|
+
import { formatPrice } from 'fulldev-ui/utils/cart'
|
|
5
6
|
|
|
6
7
|
interface Props extends GlobalProps, HTMLAttributes<'div'> {
|
|
7
8
|
size?: 'sm' | 'md' | 'lg' | undefined
|
|
@@ -13,17 +14,12 @@ interface Props extends GlobalProps, HTMLAttributes<'div'> {
|
|
|
13
14
|
const { value, ...rest } = Astro.props
|
|
14
15
|
---
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
>
|
|
23
|
-
€{value?.toFixed(2).replace('.', ',')}
|
|
24
|
-
</Element>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
17
|
+
<Element
|
|
18
|
+
class:list="price"
|
|
19
|
+
{...rest}
|
|
20
|
+
>
|
|
21
|
+
{value && <Fragment set:html={formatPrice(value)} />}
|
|
22
|
+
</Element>
|
|
27
23
|
|
|
28
24
|
<style is:global lang="scss">
|
|
29
25
|
@layer fulldev {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
import Pagefind from 'astro-pagefind/components/Search'
|
|
3
2
|
import Dialog from 'fulldev-ui/components/Dialog.astro'
|
|
4
3
|
|
|
5
4
|
const { variant = 'secondary', ...rest } = Astro.props
|
|
@@ -11,9 +10,7 @@ const { variant = 'secondary', ...rest } = Astro.props
|
|
|
11
10
|
icon="search"
|
|
12
11
|
{variant}
|
|
13
12
|
{...rest}
|
|
14
|
-
|
|
15
|
-
<Pagefind uiOptions={{ showImages: false }} />
|
|
16
|
-
</Dialog>
|
|
13
|
+
/>
|
|
17
14
|
|
|
18
15
|
<style is:global lang="scss">
|
|
19
16
|
:root {
|
|
@@ -1,21 +1,45 @@
|
|
|
1
1
|
---
|
|
2
|
-
import type { HTMLAttributes } from 'astro/types'
|
|
2
|
+
import type { ComponentProps, HTMLAttributes } from 'astro/types'
|
|
3
3
|
import Element from 'fulldev-ui/components/Element.astro'
|
|
4
4
|
import type { GlobalProps } from 'fulldev-ui/types/global-props'
|
|
5
|
+
import Button from '../Button.astro'
|
|
6
|
+
import Input from '../Input.astro'
|
|
7
|
+
import Select from '../Select.astro'
|
|
8
|
+
import Textarea from '../Textarea.astro'
|
|
5
9
|
|
|
6
10
|
interface Props extends GlobalProps, HTMLAttributes<'form'> {
|
|
7
|
-
|
|
11
|
+
submit?: ComponentProps<typeof Button>['text']
|
|
12
|
+
fields?:
|
|
13
|
+
| (
|
|
14
|
+
| ComponentProps<typeof Input>
|
|
15
|
+
| ComponentProps<typeof Select>
|
|
16
|
+
| (ComponentProps<typeof Textarea> & { type?: 'textarea' | undefined })
|
|
17
|
+
)[]
|
|
18
|
+
| undefined
|
|
8
19
|
}
|
|
9
20
|
|
|
10
|
-
const {
|
|
21
|
+
const { fields, submit, ...rest } = Astro.props
|
|
11
22
|
---
|
|
12
23
|
|
|
13
24
|
<Element
|
|
14
|
-
class:list=
|
|
25
|
+
class:list="form"
|
|
15
26
|
as="form"
|
|
16
27
|
{...rest}
|
|
17
28
|
>
|
|
18
|
-
<slot
|
|
29
|
+
<slot>
|
|
30
|
+
{
|
|
31
|
+
fields?.map((field) => {
|
|
32
|
+
if ('options' in field) return <Select {...field} />
|
|
33
|
+
if ('type' in field && field.type === 'textarea')
|
|
34
|
+
return <Textarea {...field} />
|
|
35
|
+
return <Input {...(field as any)} />
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
<Button
|
|
39
|
+
type="submit"
|
|
40
|
+
text={submit}
|
|
41
|
+
/>
|
|
42
|
+
</slot>
|
|
19
43
|
</Element>
|
|
20
44
|
|
|
21
45
|
<style is:global lang="scss">
|
package/src/content/config.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { defineCollection } from 'astro:content'
|
|
|
2
2
|
import imageLoader from 'fulldev-ui/loaders/imageLoader.ts'
|
|
3
3
|
import imageSchema from 'fulldev-ui/schemas/imageSchema.ts'
|
|
4
4
|
import pageSchema from 'fulldev-ui/schemas/pageSchema.ts'
|
|
5
|
+
import presetSchema from 'fulldev-ui/schemas/presetSchema.ts'
|
|
5
6
|
|
|
6
7
|
export const collections = {
|
|
7
8
|
pages: defineCollection({
|
|
@@ -10,7 +11,7 @@ export const collections = {
|
|
|
10
11
|
}),
|
|
11
12
|
presets: defineCollection({
|
|
12
13
|
type: 'data',
|
|
13
|
-
schema:
|
|
14
|
+
schema: presetSchema,
|
|
14
15
|
}),
|
|
15
16
|
images: defineCollection({
|
|
16
17
|
loader: imageLoader,
|
package/src/env.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/// <reference types="astro/client" />
|
|
2
|
-
/// <reference path="../.astro/types.d.ts" />
|
|
2
|
+
/// <reference path="../.astro/types.d.ts" />
|
package/src/layouts/Layout.astro
CHANGED
|
@@ -7,6 +7,7 @@ import Footer from 'fulldev-ui/blocks/Footer.astro'
|
|
|
7
7
|
import Element from 'fulldev-ui/components/Element.astro'
|
|
8
8
|
import Head from 'fulldev-ui/components/Head.astro'
|
|
9
9
|
import Header from 'fulldev-ui/components/Header.astro'
|
|
10
|
+
import Section from 'fulldev-ui/components/Section.astro'
|
|
10
11
|
import Sections from 'fulldev-ui/components/sections.astro'
|
|
11
12
|
import Sidebar from 'fulldev-ui/components/Sidebar.astro'
|
|
12
13
|
import Toc from 'fulldev-ui/components/Toc.astro'
|
|
@@ -19,6 +20,7 @@ type Props = GlobalProps &
|
|
|
19
20
|
header?: ComponentProps<typeof Header>
|
|
20
21
|
footer?: ComponentProps<typeof Footer>
|
|
21
22
|
sections?: ComponentProps<typeof Sections>['sections']
|
|
23
|
+
section?: ComponentProps<typeof Section> | undefined
|
|
22
24
|
banner?: any
|
|
23
25
|
toc?: any
|
|
24
26
|
sidebar?: any
|
|
@@ -47,6 +49,7 @@ const {
|
|
|
47
49
|
toc,
|
|
48
50
|
sections,
|
|
49
51
|
theme,
|
|
52
|
+
section,
|
|
50
53
|
banner,
|
|
51
54
|
code,
|
|
52
55
|
themeselect,
|
|
@@ -84,6 +87,7 @@ const {
|
|
|
84
87
|
</slot>
|
|
85
88
|
<main data-pagefind-body>
|
|
86
89
|
<slot>
|
|
90
|
+
<Section {...section} />
|
|
87
91
|
<Article />
|
|
88
92
|
<Sections {sections} />
|
|
89
93
|
</slot>
|
|
@@ -4,6 +4,9 @@ import fulldevConfig from 'virtual:fulldev-ui/config'
|
|
|
4
4
|
import footer from './components/footer'
|
|
5
5
|
import header from './components/header'
|
|
6
6
|
import image from './components/image'
|
|
7
|
+
import images from './components/images'
|
|
8
|
+
import price from './components/price'
|
|
9
|
+
import section from './components/section'
|
|
7
10
|
import sections from './components/sections'
|
|
8
11
|
import sidebar from './components/sidebar'
|
|
9
12
|
import toc from './components/toc'
|
|
@@ -12,7 +15,7 @@ import navigation from './utils/navigation'
|
|
|
12
15
|
import parents from './utils/parents'
|
|
13
16
|
import pathSchema from './utils/pathSchema'
|
|
14
17
|
|
|
15
|
-
const i18n = z
|
|
18
|
+
export const i18n = z
|
|
16
19
|
.object({
|
|
17
20
|
i18n: pathSchema('pages').optional(),
|
|
18
21
|
})
|
|
@@ -24,7 +27,7 @@ const i18n = z
|
|
|
24
27
|
return merged as typeof rest
|
|
25
28
|
})
|
|
26
29
|
|
|
27
|
-
const presets = z
|
|
30
|
+
export const presets = z
|
|
28
31
|
.object({
|
|
29
32
|
presets: z.array(pathSchema('presets')).optional(),
|
|
30
33
|
})
|
|
@@ -48,29 +51,51 @@ const presets = z
|
|
|
48
51
|
return mergedData as typeof rest
|
|
49
52
|
})
|
|
50
53
|
|
|
51
|
-
export
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
export const basePreset = z
|
|
55
|
+
.object({})
|
|
56
|
+
.passthrough()
|
|
57
|
+
.transform(async (data) => {
|
|
58
|
+
if (!fulldevConfig.basePreset) return data
|
|
59
|
+
const baseEntry = await getEntry('presets', fulldevConfig.basePreset)
|
|
60
|
+
const mergedData = assign(baseEntry?.data ?? {}, data)
|
|
61
|
+
return mergedData as typeof data
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const page = z
|
|
65
|
+
.object({
|
|
66
|
+
component,
|
|
67
|
+
parents,
|
|
68
|
+
theme: z.enum(['light', 'dark']),
|
|
69
|
+
lang: z.string(),
|
|
70
|
+
seo: z
|
|
71
|
+
.object({
|
|
72
|
+
title: z.string().optional(),
|
|
73
|
+
description: z.string().optional(),
|
|
74
|
+
})
|
|
75
|
+
.optional(),
|
|
76
|
+
title: z.string(),
|
|
77
|
+
description: z.string().optional(),
|
|
78
|
+
image,
|
|
79
|
+
images,
|
|
80
|
+
section,
|
|
81
|
+
sections,
|
|
82
|
+
footer,
|
|
83
|
+
header: z.boolean().or(header),
|
|
84
|
+
sidebar: z.boolean().or(sidebar),
|
|
85
|
+
navigation,
|
|
86
|
+
toc,
|
|
87
|
+
price,
|
|
88
|
+
code: z.any(),
|
|
89
|
+
sizes: z.any(),
|
|
90
|
+
banner: z.any(),
|
|
91
|
+
_schema: z.any(),
|
|
92
|
+
uitverkocht: z.any(),
|
|
93
|
+
rating: z.any(),
|
|
94
|
+
variants: z.any(),
|
|
95
|
+
heading: z.any(),
|
|
96
|
+
href: z.any(),
|
|
97
|
+
})
|
|
98
|
+
.partial()
|
|
99
|
+
.strict()
|
|
100
|
+
|
|
101
|
+
export default i18n.pipe(basePreset).pipe(presets).pipe(page)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { persistentAtom } from '@nanostores/persistent'
|
|
2
|
+
import { computed } from 'nanostores'
|
|
3
|
+
|
|
4
|
+
// type Item = CollectionEntry<'products'>['data'] & {
|
|
5
|
+
// quantity: number
|
|
6
|
+
// }
|
|
7
|
+
|
|
8
|
+
type Item = any
|
|
9
|
+
|
|
10
|
+
export const $cart = persistentAtom<Item[] | []>('cart', [], {
|
|
11
|
+
encode: JSON.stringify,
|
|
12
|
+
decode: JSON.parse,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// export const cartIsOpen = atom(false)
|
|
16
|
+
|
|
17
|
+
export const $totalPrice = computed($cart, ($cart) =>
|
|
18
|
+
$cart.reduce((a, b) => +a + b.price * b.quantity, 0)
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// export const totalQuantity = computed(cart, ($cart) =>
|
|
22
|
+
// $cart.reduce((a, b) => +a + b.quantity, 0)
|
|
23
|
+
// )
|