fulldev-ui 0.5.0-beta.1 → 0.5.0-beta.10

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 (77) hide show
  1. package/CONTRIBUTING.md +10 -4
  2. package/astro.config.ts +2 -4
  3. package/package.json +6 -4
  4. package/pnpm-lock.yaml +54 -127
  5. package/src/blocks/Article.astro +31 -12
  6. package/src/blocks/Banner.astro +2 -1
  7. package/src/blocks/Contact.astro +7 -30
  8. package/src/blocks/Excerpt.astro +14 -0
  9. package/src/blocks/Intro.astro +3 -1
  10. package/src/blocks/Product.astro +55 -22
  11. package/src/components/Card.astro +11 -5
  12. package/src/components/Cart.astro +38 -0
  13. package/src/components/CartForm.astro +121 -0
  14. package/src/components/CartList.astro +134 -0
  15. package/src/components/CartSummary.astro +147 -0
  16. package/src/components/Drawer.astro +128 -52
  17. package/src/components/Footer.astro +6 -3
  18. package/src/components/Form.astro +49 -0
  19. package/src/components/Header.astro +19 -14
  20. package/src/components/Image.astro +0 -1
  21. package/src/components/Input.astro +6 -0
  22. package/src/components/Menu.astro +56 -0
  23. package/src/components/Price.astro +7 -11
  24. package/src/components/Search.astro +1 -4
  25. package/src/components/Section.astro +2 -1
  26. package/src/components/Sidebar.astro +61 -111
  27. package/src/components/Social.astro +7 -0
  28. package/src/components/ThemeSelect.astro +3 -0
  29. package/src/components/Toc.astro +33 -94
  30. package/src/components/docs/PropsTable.astro +1 -1
  31. package/src/components/experimental/Form.astro +31 -5
  32. package/src/components/experimental/Gallery.astro +50 -126
  33. package/src/components/experimental/Prose.astro +32 -0
  34. package/src/content/pages/base/select.mdx +0 -8
  35. package/src/content/pages/base/textarea.mdx +0 -5
  36. package/src/content/pages/blocks/_product.mdx +73 -2
  37. package/src/content/pages/index.mdx +0 -3
  38. package/src/content/pages/layouts/_layout.mdx +14 -0
  39. package/src/content/pages/layouts/docslayout.mdx +10 -1
  40. package/src/content/pages/overview/components.mdx +5 -0
  41. package/src/content/pages/overview/content-collections.mdx +139 -0
  42. package/src/content/pages/overview/installation.mdx +25 -26
  43. package/src/content/pages/overview/introduction.mdx +16 -10
  44. package/src/content/pages/overview/theming.mdx +69 -0
  45. package/src/content/pages/schemas/page.mdx +6 -0
  46. package/src/content/presets/base.yml +56 -43
  47. package/src/content/presets/blocks.yml +0 -3
  48. package/src/content/presets/docs.yml +3 -62
  49. package/src/env.d.ts +1 -1
  50. package/src/integration/index.ts +31 -33
  51. package/src/layouts/DocsLayout.astro +23 -35
  52. package/src/layouts/Layout.astro +37 -21
  53. package/src/layouts/OverviewLayout.astro +17 -10
  54. package/src/layouts/PostLayout.astro +7 -7
  55. package/src/layouts/ProductLayout.astro +5 -2
  56. package/src/schemas/components/footer.ts +1 -0
  57. package/src/schemas/components/header.ts +1 -0
  58. package/src/schemas/components/menu.ts +13 -0
  59. package/src/schemas/components/sidebar.ts +15 -0
  60. package/src/schemas/components/toc.ts +3 -0
  61. package/src/schemas/pageSchema.ts +72 -34
  62. package/src/schemas/presetSchema.ts +2 -28
  63. package/src/schemas/utils/navigation.ts +6 -51
  64. package/src/stores/cart.ts +23 -0
  65. package/src/utils/cart.ts +36 -0
  66. package/src/utils/{getChildPages.ts → getFolderChildren.ts} +3 -1
  67. package/src/utils/{getCategoryPages.ts → getReferenceChildren.ts} +6 -3
  68. package/tsconfig.json +1 -3
  69. package/src/components/experimental/GalleryVertical.astro +0 -161
  70. package/src/content/pages/layouts/layout.mdx +0 -21
  71. package/src/content/pages/layouts/pagelayout.mdx +0 -21
  72. package/src/content/pages/overview/content-layer.mdx +0 -128
  73. package/src/content/pages/overview/customization.mdx +0 -83
  74. package/src/layouts/ContentLayout.astro +0 -16
  75. package/src/layouts/PageLayout.astro +0 -15
  76. /package/src/schemas/utils/{categories.ts → parents.ts} +0 -0
  77. /package/src/types/{global-props.d.ts → global-props.ts} +0 -0
@@ -1,10 +1,11 @@
1
1
  ---
2
- import Button from 'fulldev-ui/components/Button.astro'
2
+ import { getImage } from 'astro:assets'
3
+ import CartForm from 'fulldev-ui/components/CartForm.astro'
3
4
  import Element from 'fulldev-ui/components/Element.astro'
5
+ import Gallery from 'fulldev-ui/components/experimental/Gallery.astro'
4
6
  import Heading from 'fulldev-ui/components/Heading.astro'
5
7
  import Image from 'fulldev-ui/components/Image.astro'
6
- import Input from 'fulldev-ui/components/Input.astro'
7
- import Price from 'fulldev-ui/components/Price.astro'
8
+ import List from 'fulldev-ui/components/List.astro'
8
9
  import Section from 'fulldev-ui/components/Section.astro'
9
10
  import Text from 'fulldev-ui/components/Text.astro'
10
11
 
@@ -19,11 +20,19 @@ const {
19
20
  buttons,
20
21
  text,
21
22
  subheading,
23
+ variants,
22
24
  button,
23
25
  description,
24
26
  form,
27
+ gallery = 'horizontal',
25
28
  ...rest
26
29
  } = Astro.props
30
+
31
+ const slug = Astro.url.pathname.replace(/^\/|\/$/g, '') || 'index'
32
+ const src = `/src/images/${images?.[0].id}`
33
+ const gotImage = await getImage({ src, width: 200, height: 200 })
34
+
35
+ console.log({ hoi: gotImage.src })
27
36
  ---
28
37
 
29
38
  <Section
@@ -31,47 +40,71 @@ const {
31
40
  structure="split"
32
41
  {...rest}
33
42
  >
34
- <Image {...image || images?.[0]} />
43
+ {
44
+ images && images.length > 1 ? (
45
+ <Gallery
46
+ {images}
47
+ direction={gallery}
48
+ />
49
+ ) : (
50
+ <Image {...(image || images?.[0])} />
51
+ )
52
+ }
35
53
  <Element class:list="column">
36
54
  <Heading
37
- level={1}
55
+ level={2}
38
56
  text={heading || title}
39
57
  />
40
58
  <Text
41
59
  style={{ maxWidth: '420px' }}
42
60
  text={text || description}
43
61
  />
44
- <Price value={price} />
45
- <Element class:list="row">
46
- <Input
47
- value={1}
48
- name={'quantity'}
49
- style={{ width: 'var(--space-6)' }}
50
- type="number"
51
- />
52
- <Button
53
- color="brand"
54
- text={form?.submit}
55
- />
56
- </Element>
57
- <Button
58
- style={{ marginTop: 'var(--space-3)', width: '100%', maxWidth: '400px' }}
59
- variant="secondary"
60
- {...button}
62
+ <List
63
+ items={list}
64
+ style={{ marginTop: 'var(--space-5)' }}
65
+ />
66
+ <CartForm
67
+ image={gotImage.src}
68
+ {title}
69
+ {price}
70
+ {slug}
71
+ {variants}
61
72
  />
62
73
  </Element>
63
74
  </Section>
64
75
 
65
76
  <style is:global lang="scss">
66
77
  .product {
78
+ padding-top: 0px;
79
+
80
+ @media (min-width: 1024px) {
81
+ padding-top: var(--space-6);
82
+ }
83
+
67
84
  .column {
68
85
  display: flex;
69
86
  flex-direction: column;
70
87
  align-items: flex-start;
88
+ gap: var(--space-5);
71
89
  }
72
90
 
73
91
  .row {
74
92
  display: flex;
93
+ align-items: center;
94
+ gap: var(--space-5);
95
+ }
96
+
97
+ @media (max-width: 1023.99px) {
98
+ .gallery {
99
+ margin-right: calc(-1 * var(--gutter));
100
+ margin-left: calc(-1 * var(--gutter));
101
+ width: calc(100% + (var(--gutter) * 2));
102
+
103
+ .small-carousel {
104
+ padding-right: var(--gutter);
105
+ padding-left: var(--gutter);
106
+ }
107
+ }
75
108
  }
76
109
  }
77
110
  </style>
@@ -9,6 +9,7 @@ import Image from 'fulldev-ui/components/Image.astro'
9
9
  import Writeup from 'fulldev-ui/components/Writeup.astro'
10
10
  import type { GlobalProps } from 'fulldev-ui/types/global-props'
11
11
  import Icon from './Icon.astro'
12
+ import Price from './Price.astro'
12
13
 
13
14
  interface Props
14
15
  extends GlobalProps,
@@ -22,9 +23,11 @@ interface Props
22
23
  variant?: ComponentProps<typeof Button>['variant']
23
24
  align?: ComponentProps<typeof Writeup>['align']
24
25
  icon?: ComponentProps<typeof Icon>['name']
26
+ price?: ComponentProps<typeof Price>['value']
25
27
  slug?: CollectionEntry<'pages'>['slug'] | undefined
26
28
  id?: any
27
29
  panel?: boolean | undefined
30
+ title?: ComponentProps<typeof Writeup>['heading']
28
31
  }
29
32
 
30
33
  const { slug } = Astro.props
@@ -33,7 +36,7 @@ const page = slug ? (await getEntry('pages', slug)).data : {}
33
36
  const entryProps = {
34
37
  heading: page.title,
35
38
  text: page.description,
36
- href: `/${slug}/`,
39
+ href: slug ? `/${slug}/` : undefined,
37
40
  ...page,
38
41
  }
39
42
 
@@ -47,12 +50,14 @@ const {
47
50
  heading,
48
51
  text,
49
52
  variant,
53
+ price,
50
54
  list,
51
55
  button,
52
56
  buttons,
53
57
  panel,
54
58
  image,
55
59
  icon,
60
+ title,
56
61
  images,
57
62
  position,
58
63
  ...rest
@@ -81,14 +86,15 @@ const {
81
86
  </slot>
82
87
  <slot name="writeup">
83
88
  <Writeup
89
+ heading={heading || title}
84
90
  {level}
85
91
  {rating}
86
92
  {badge}
87
93
  {tagline}
88
- {heading}
89
94
  {text}
90
95
  {list}
91
96
  />
97
+ <Price value={price} />
92
98
  </slot>
93
99
  <slot name="buttons">
94
100
  <Button
@@ -121,12 +127,12 @@ const {
121
127
  &[href] {
122
128
  cursor: pointer;
123
129
 
124
- > .root.image {
125
- transition: all;
130
+ > .image {
131
+ transition: all 0.2s ease;
126
132
  }
127
133
 
128
134
  &:hover {
129
- > .root.image {
135
+ > .image {
130
136
  opacity: 0.8;
131
137
  }
132
138
  }
@@ -0,0 +1,38 @@
1
+ ---
2
+ import type { ComponentProps } from 'astro/types'
3
+ import CartList from 'fulldev-ui/components/CartList.astro'
4
+ import CartSummary from 'fulldev-ui/components/CartSummary.astro'
5
+ import Drawer from 'fulldev-ui/components/Drawer.astro'
6
+
7
+ interface Props extends ComponentProps<typeof Drawer> {}
8
+
9
+ const {
10
+ icon = 'shopping-cart',
11
+ color = 'brand',
12
+ id = 'cart',
13
+ if: ifProp,
14
+ ...rest
15
+ } = Astro.props
16
+ ---
17
+
18
+ <Drawer
19
+ if={ifProp}
20
+ class:list="cart"
21
+ {icon}
22
+ {color}
23
+ {id}
24
+ {...rest}
25
+ >
26
+ <CartList />
27
+ <CartSummary slot="footer" />
28
+ </Drawer>
29
+
30
+ <style lang="scss" is:global>
31
+ @layer fulldev {
32
+ form {
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: var(--space-4);
36
+ }
37
+ }
38
+ </style>
@@ -0,0 +1,121 @@
1
+ ---
2
+ import type { ComponentProps } from 'astro/types'
3
+ import Button from 'fulldev-ui/components/Button.astro'
4
+ import Input from 'fulldev-ui/components/Input.astro'
5
+ import Select from 'fulldev-ui/components/Select.astro'
6
+ import { capitalize } from 'radash'
7
+ import Form from './experimental/Form.astro'
8
+ import Price from './Price.astro'
9
+
10
+ interface Props extends Omit<ComponentProps<typeof Form>, 'fields' | 'submit'> {
11
+ title: string
12
+ slug: string
13
+ price: number
14
+ image?: string | undefined
15
+ variants?:
16
+ | {
17
+ [key: string]: string[]
18
+ }
19
+ | undefined
20
+ }
21
+
22
+ const { title, slug, image, price, variants, ...rest } = Astro.props
23
+ ---
24
+
25
+ <Form
26
+ class:list="cart-form"
27
+ {...rest}
28
+ >
29
+ <Input
30
+ type="hidden"
31
+ name="title"
32
+ value={title}
33
+ />
34
+ <Input
35
+ type="hidden"
36
+ name="slug"
37
+ value={slug}
38
+ />
39
+ <Input
40
+ type="hidden"
41
+ name="url"
42
+ value={Astro.site ? Astro.site.href + slug : undefined}
43
+ />
44
+ <Input
45
+ type="hidden"
46
+ name="image"
47
+ value={Astro.url.origin ? Astro.url.origin + image : undefined}
48
+ />
49
+ {
50
+ Object.entries(variants ?? {}).map(([key, options]) => (
51
+ <Select
52
+ label={capitalize(key)}
53
+ name={`variant-${key}`}
54
+ options={options}
55
+ />
56
+ ))
57
+ }
58
+ <Price value={price} />
59
+ <Input
60
+ type="hidden"
61
+ name="price"
62
+ value={price}
63
+ />
64
+ <Button
65
+ variant="primary"
66
+ color="brand"
67
+ type="submit"
68
+ class="cart-add"
69
+ text="Toevoegen aan winkelwagen"
70
+ />
71
+ </Form>
72
+
73
+ <script>
74
+ import { $cart } from 'fulldev-ui/stores/cart.ts'
75
+ import { generateProductId, generateProductName } from 'fulldev-ui/utils/cart'
76
+
77
+ document.addEventListener('astro:page-load', () => {
78
+ const cartForm = document.querySelector('.cart-form')
79
+
80
+ cartForm?.addEventListener('submit', (e) => {
81
+ e.preventDefault()
82
+
83
+ if (e.target instanceof HTMLFormElement) {
84
+ const formData = new FormData(e.target)
85
+
86
+ const title = formData.get('title') as string
87
+ const slug = formData.get('slug') as string
88
+ const url = formData.get('url') as string
89
+ const image = formData.get('image') as string
90
+ const price = Number(formData.get('price')) as number
91
+ const variants = Array.from(formData.entries())
92
+ .filter(([key]) => key.startsWith('variant-'))
93
+ .map(([_, value]) => value) as string[]
94
+
95
+ const id = generateProductId(slug, variants)
96
+ const name = generateProductName(title, variants)
97
+
98
+ const currentCart = $cart.get()
99
+ const newCart = [...currentCart, { id, name, price, image, url }]
100
+ $cart.set(newCart)
101
+
102
+ const cartDialog = document?.querySelector('dialog#cart')
103
+ if (cartDialog instanceof HTMLDialogElement) cartDialog.showModal()
104
+ }
105
+ })
106
+ })
107
+ </script>
108
+
109
+ <style is:global lang="scss">
110
+ @layer fulldev {
111
+ .cart-form {
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: var(--space-4);
115
+
116
+ .input {
117
+ display: none;
118
+ }
119
+ }
120
+ }
121
+ </style>
@@ -0,0 +1,134 @@
1
+ ---
2
+ import Element from 'fulldev-ui/components/Element.astro'
3
+ import Button from './Button.astro'
4
+ import Card from './Card.astro'
5
+ import Heading from './Heading.astro'
6
+ import Text from './Text.astro'
7
+
8
+ const { ...rest } = Astro.props
9
+ ---
10
+
11
+ <Element
12
+ if
13
+ class:list="cart-list"
14
+ {...rest}
15
+ />
16
+
17
+ <template>
18
+ <Card class="cart-list-card">
19
+ <img class="cart-list-image image" />
20
+ <Element class="column">
21
+ <Heading
22
+ if
23
+ level={4}
24
+ class="heading"
25
+ />
26
+ <Element class="row">
27
+ <Text
28
+ if
29
+ class="cart-list-price"
30
+ />
31
+ <Button
32
+ class:list="cart-list-remove"
33
+ variant="tertiary"
34
+ icon="trash"
35
+ type="button"
36
+ />
37
+ </Element>
38
+ </Element>
39
+ </Card>
40
+ </template>
41
+
42
+ <script>
43
+ import { $cart } from 'fulldev-ui/stores/cart.ts'
44
+ import { formatPrice } from 'fulldev-ui/utils/cart'
45
+
46
+ function setCards(items: any) {
47
+ const template = document.querySelector('template')
48
+ const container = document.querySelector('.cart-list')
49
+
50
+ if (!container || !template) return
51
+
52
+ container.innerHTML = ''
53
+
54
+ items.forEach((item: any, index: number) => {
55
+ const clone = template?.content.cloneNode(true)
56
+ if (!(clone instanceof DocumentFragment)) return
57
+ const card = clone.querySelector('.cart-list-card')
58
+ if (!card) return
59
+
60
+ const img = card.querySelector('img')
61
+ if (img) img.src = item.image
62
+
63
+ const h4 = card.querySelector('h4')
64
+ if (h4) h4.textContent = item.name
65
+
66
+ const p = card.querySelector('p')
67
+ if (p) p.textContent = formatPrice(item.price)
68
+
69
+ const button = card.querySelector('button')
70
+ if (button) {
71
+ button.addEventListener('click', () => {
72
+ const currentCart = $cart.get()
73
+ const newCart = [...currentCart]
74
+ newCart.splice(index, 1)
75
+ $cart.set(newCart)
76
+ })
77
+ }
78
+
79
+ container.appendChild(clone)
80
+ })
81
+ }
82
+
83
+ document.addEventListener('astro:page-load', () => {
84
+ setCards($cart.get())
85
+
86
+ $cart.subscribe((items: any) => {
87
+ setCards(items)
88
+ })
89
+ })
90
+ </script>
91
+
92
+ <style lang="scss" is:global>
93
+ @layer fulldev {
94
+ .cart-list {
95
+ display: flex;
96
+ flex-direction: column;
97
+
98
+ .card {
99
+ display: flex;
100
+ flex-direction: row;
101
+ gap: var(--space-5);
102
+ width: 100%;
103
+
104
+ &:not(:first-child) {
105
+ margin-top: var(--space-5);
106
+ border-top: 1px solid var(--color-a6);
107
+ padding-top: var(--space-5);
108
+ }
109
+
110
+ .image {
111
+ display: block;
112
+ width: 100px;
113
+ height: auto;
114
+ }
115
+
116
+ .column {
117
+ display: flex;
118
+ flex-direction: column;
119
+ justify-content: space-between;
120
+ gap: var(--space-3);
121
+ width: 100%;
122
+ height: 100%;
123
+ }
124
+
125
+ .row {
126
+ display: flex;
127
+ flex-direction: row;
128
+ justify-content: space-between;
129
+ align-items: center;
130
+ }
131
+ }
132
+ }
133
+ }
134
+ </style>
@@ -0,0 +1,147 @@
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
+ images: [window],
132
+ },
133
+ unit_amount: Math.round(item.price * 100),
134
+ },
135
+ }))
136
+
137
+ // @ts-ignore
138
+ const checkoutSession = await stripe.checkout.sessions.create({
139
+ line_items: lineItems,
140
+ ...checkoutConfig,
141
+ })
142
+
143
+ checkoutSession.url && window.location.replace(checkoutSession.url)
144
+ }
145
+ })
146
+ })
147
+ </script>