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

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 (78) hide show
  1. package/.env.example +3 -0
  2. package/CONTRIBUTING.md +10 -4
  3. package/astro.config.ts +2 -13
  4. package/package.json +8 -4
  5. package/pnpm-lock.yaml +82 -130
  6. package/src/blocks/Article.astro +31 -12
  7. package/src/blocks/Banner.astro +2 -1
  8. package/src/blocks/Contact.astro +7 -30
  9. package/src/blocks/Excerpt.astro +14 -0
  10. package/src/blocks/Intro.astro +3 -1
  11. package/src/blocks/Product.astro +55 -22
  12. package/src/components/Card.astro +11 -5
  13. package/src/components/Cart.astro +38 -0
  14. package/src/components/CartForm.astro +121 -0
  15. package/src/components/CartList.astro +134 -0
  16. package/src/components/CartSummary.astro +147 -0
  17. package/src/components/Drawer.astro +128 -52
  18. package/src/components/Footer.astro +6 -3
  19. package/src/components/Form.astro +49 -0
  20. package/src/components/Header.astro +19 -14
  21. package/src/components/Image.astro +0 -1
  22. package/src/components/Input.astro +6 -0
  23. package/src/components/Menu.astro +56 -0
  24. package/src/components/Price.astro +7 -11
  25. package/src/components/Search.astro +1 -4
  26. package/src/components/Section.astro +2 -1
  27. package/src/components/Sidebar.astro +61 -111
  28. package/src/components/Social.astro +7 -0
  29. package/src/components/ThemeSelect.astro +3 -0
  30. package/src/components/Toc.astro +33 -94
  31. package/src/components/docs/PropsTable.astro +1 -1
  32. package/src/components/experimental/Form.astro +31 -5
  33. package/src/components/experimental/Gallery.astro +50 -126
  34. package/src/components/experimental/Prose.astro +32 -0
  35. package/src/content/pages/base/select.mdx +0 -8
  36. package/src/content/pages/base/textarea.mdx +0 -5
  37. package/src/content/pages/blocks/_product.mdx +73 -2
  38. package/src/content/pages/index.mdx +0 -3
  39. package/src/content/pages/layouts/_layout.mdx +14 -0
  40. package/src/content/pages/layouts/docslayout.mdx +10 -1
  41. package/src/content/pages/overview/components.mdx +5 -0
  42. package/src/content/pages/overview/content-collections.mdx +139 -0
  43. package/src/content/pages/overview/installation.mdx +25 -26
  44. package/src/content/pages/overview/introduction.mdx +16 -10
  45. package/src/content/pages/overview/theming.mdx +69 -0
  46. package/src/content/pages/schemas/page.mdx +6 -0
  47. package/src/content/presets/base.yml +56 -43
  48. package/src/content/presets/blocks.yml +0 -3
  49. package/src/content/presets/docs.yml +3 -62
  50. package/src/env.d.ts +1 -1
  51. package/src/integration/index.ts +63 -34
  52. package/src/layouts/DocsLayout.astro +23 -35
  53. package/src/layouts/Layout.astro +37 -21
  54. package/src/layouts/OverviewLayout.astro +17 -10
  55. package/src/layouts/PostLayout.astro +7 -7
  56. package/src/layouts/ProductLayout.astro +5 -2
  57. package/src/schemas/components/footer.ts +1 -0
  58. package/src/schemas/components/header.ts +1 -0
  59. package/src/schemas/components/menu.ts +13 -0
  60. package/src/schemas/components/sidebar.ts +15 -0
  61. package/src/schemas/components/toc.ts +3 -0
  62. package/src/schemas/pageSchema.ts +72 -34
  63. package/src/schemas/presetSchema.ts +2 -28
  64. package/src/schemas/utils/navigation.ts +6 -51
  65. package/src/stores/cart.ts +23 -0
  66. package/src/utils/cart.ts +32 -0
  67. package/src/utils/{getChildPages.ts → getFolderChildren.ts} +3 -1
  68. package/src/utils/{getCategoryPages.ts → getReferenceChildren.ts} +6 -3
  69. package/tsconfig.json +1 -3
  70. package/src/components/experimental/GalleryVertical.astro +0 -161
  71. package/src/content/pages/layouts/layout.mdx +0 -21
  72. package/src/content/pages/layouts/pagelayout.mdx +0 -21
  73. package/src/content/pages/overview/content-layer.mdx +0 -128
  74. package/src/content/pages/overview/customization.mdx +0 -83
  75. package/src/layouts/ContentLayout.astro +0 -16
  76. package/src/layouts/PageLayout.astro +0 -15
  77. /package/src/schemas/utils/{categories.ts → parents.ts} +0 -0
  78. /package/src/types/{global-props.d.ts → global-props.ts} +0 -0
@@ -1,21 +1,21 @@
1
1
  ---
2
2
  import type { ComponentProps } from 'astro/types'
3
3
  import Article from 'fulldev-ui/blocks/Article.astro'
4
- import Intro from 'fulldev-ui/blocks/Intro.astro'
5
4
  import Sections from 'fulldev-ui/components/sections.astro'
6
5
  import Layout from 'fulldev-ui/layouts/Layout.astro'
7
6
 
8
7
  type Props = ComponentProps<typeof Layout>
9
8
 
10
- const { render, sections, ...rest } = Astro.props
9
+ const { sections, render, toc } = Astro.props
11
10
  ---
12
11
 
13
12
  <Layout {...Astro.props}>
14
- <Intro
15
- align="start"
16
- {...rest}
17
- />
18
- <Article />
13
+ <Article
14
+ {render}
15
+ {toc}
16
+ >
17
+ <slot name="prose" />
18
+ </Article>
19
19
  <Sections {sections} />
20
20
  <slot />
21
21
  </Layout>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import type { ComponentProps } from 'astro/types'
3
3
  import Article from 'fulldev-ui/blocks/Article.astro'
4
- import Product from 'fulldev-ui/blocks/Product.astro'
4
+ import Section from 'fulldev-ui/components/Section.astro'
5
5
  import Sections from 'fulldev-ui/components/sections.astro'
6
6
  import Layout from 'fulldev-ui/layouts/Layout.astro'
7
7
 
@@ -11,7 +11,10 @@ const { render, sections, ...rest } = Astro.props
11
11
  ---
12
12
 
13
13
  <Layout {...Astro.props}>
14
- <Product {...rest} />
14
+ <Section
15
+ component="Product"
16
+ {...rest as any}
17
+ />
15
18
  <Article />
16
19
  <Sections {sections} />
17
20
  <slot />
@@ -22,3 +22,4 @@ export default z
22
22
  ),
23
23
  })
24
24
  .partial()
25
+ .passthrough()
@@ -8,3 +8,4 @@ export default z
8
8
  buttons,
9
9
  })
10
10
  .partial()
11
+ .passthrough()
@@ -0,0 +1,13 @@
1
+ import { z } from 'astro:content'
2
+ import buttons from './buttons'
3
+ import heading from './heading'
4
+
5
+ export default z.array(
6
+ z
7
+ .object({
8
+ heading,
9
+ buttons,
10
+ })
11
+ .partial()
12
+ .passthrough()
13
+ )
@@ -0,0 +1,15 @@
1
+ import { z } from 'astro:content'
2
+ import logo from '../components/logo'
3
+ import menu from '../components/menu'
4
+ import heading from './heading'
5
+ import toc from './toc'
6
+
7
+ export default z
8
+ .object({
9
+ logo,
10
+ heading,
11
+ menu,
12
+ toc,
13
+ })
14
+ .partial()
15
+ .passthrough()
@@ -0,0 +1,3 @@
1
+ import { z } from 'astro:content'
2
+
3
+ export default z.string()
@@ -1,13 +1,21 @@
1
1
  import { getEntries, getEntry, z } from 'astro:content'
2
2
  import { assign } from 'radash'
3
+ import fulldevConfig from 'virtual:fulldev-ui/config'
4
+ import footer from './components/footer'
5
+ import header from './components/header'
3
6
  import image from './components/image'
7
+ import images from './components/images'
8
+ import price from './components/price'
9
+ import section from './components/section'
4
10
  import sections from './components/sections'
5
- import categories from './utils/categories'
11
+ import sidebar from './components/sidebar'
12
+ import toc from './components/toc'
6
13
  import component from './utils/component'
7
14
  import navigation from './utils/navigation'
15
+ import parents from './utils/parents'
8
16
  import pathSchema from './utils/pathSchema'
9
17
 
10
- const i18n = z
18
+ export const i18n = z
11
19
  .object({
12
20
  i18n: pathSchema('pages').optional(),
13
21
  })
@@ -19,45 +27,75 @@ const i18n = z
19
27
  return merged as typeof rest
20
28
  })
21
29
 
22
- const presets = z
30
+ export const presets = z
23
31
  .object({
24
32
  presets: z.array(pathSchema('presets')).optional(),
25
33
  })
26
34
  .passthrough()
27
35
  .transform(async ({ presets, ...rest }) => {
28
- if (!presets) return rest
29
- const entries = await getEntries(presets)
30
36
  let mergedData = {}
31
- entries.forEach(
32
- (preset: any) => (mergedData = assign(mergedData, preset.data))
33
- )
37
+
38
+ if (fulldevConfig.basePreset) {
39
+ const baseEntry = await getEntry('presets', fulldevConfig.basePreset)
40
+ mergedData = assign(mergedData, baseEntry?.data || {})
41
+ }
42
+
43
+ if (presets) {
44
+ const entries = await getEntries(presets)
45
+ entries.forEach(
46
+ (preset: any) => (mergedData = assign(mergedData, preset.data))
47
+ )
48
+ }
49
+
34
50
  mergedData = assign(mergedData, rest)
35
51
  return mergedData as typeof rest
36
52
  })
37
53
 
38
- export default i18n.pipe(presets).pipe(
39
- z
40
- .object({
41
- component,
42
- categories,
43
- theme: z.enum(['light', 'dark']),
44
- lang: z.string(),
45
- seo: z
46
- .object({
47
- title: z.string().optional(),
48
- description: z.string().optional(),
49
- })
50
- .optional(),
51
- title: z.string(),
52
- description: z.string().optional(),
53
- image,
54
- sections,
55
- header: z.any(),
56
- footer: z.any(),
57
- navigation,
58
- toc: z.any(),
59
- sidebar: z.any(),
60
- })
61
- .partial()
62
- .passthrough()
63
- )
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: header,
84
+ sidebar: 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)
@@ -1,29 +1,3 @@
1
- import { z } from 'astro:content'
2
- import image from './components/image'
3
- import sections from './components/sections'
4
- import categories from './utils/categories'
5
- import component from './utils/component'
1
+ import { page } from './pageSchema'
6
2
 
7
- export default z
8
- .object({
9
- component,
10
- categories,
11
- theme: z.enum(['light', 'dark']),
12
- lang: z.string(),
13
- seo: z
14
- .object({
15
- title: z.string().optional(),
16
- description: z.string().optional(),
17
- })
18
- .optional(),
19
- title: z.string(),
20
- description: z.string().optional(),
21
- image,
22
- sections,
23
- header: z.any(),
24
- footer: z.any(),
25
- navigation: z.any(),
26
- toc: z.any(),
27
- sidebar: z.any(),
28
- })
29
- .partial()
3
+ export default page
@@ -1,53 +1,8 @@
1
- // import { z } from 'astro:content'
2
- // import button from '../components/button'
3
- // import buttons from '../components/buttons'
4
- // import heading from '../components/heading'
5
- // import logo from '../components/logo'
6
-
7
- // export default z.object({
8
- // logo,
9
- // buttons: z.array(
10
- // button.or(
11
- // z.object({
12
- // heading,
13
- // buttons,
14
- // })
15
- // )
16
- // ),
17
- // })
18
-
19
1
  import { z } from 'astro:content'
20
- import pathSchema from '../utils/pathSchema'
21
-
22
- const button = pathSchema('pages').or(
23
- z
24
- .object({
25
- text: z.string(),
26
- href: z.string(),
27
- heading: z.never(),
28
- buttons: z.never(),
29
- })
30
- .partial()
31
- .passthrough()
32
- )
33
-
34
- const group = z
35
- .object({
36
- heading: z.string(),
37
- buttons: z.array(button),
38
- text: z.never(),
39
- href: z.never(),
40
- })
41
- .partial()
42
- .passthrough()
2
+ import header from '../components/header'
3
+ import sidebar from '../components/sidebar'
43
4
 
44
- export default z
45
- .object({
46
- logo: z.string(),
47
- search: z.boolean(),
48
- sidebar: z.boolean(),
49
- toc: z.boolean(),
50
- buttons: z.array(button.or(group)),
51
- })
52
- .partial()
53
- .passthrough()
5
+ export default sidebar.merge(header).extend({
6
+ header: z.boolean().optional(),
7
+ sidebar: z.boolean().optional(),
8
+ })
@@ -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
+ // )
@@ -0,0 +1,32 @@
1
+ const slugify = (input: string): string => {
2
+ return input
3
+ .toLowerCase()
4
+ .replace(/[^a-z0-9]+/g, '-')
5
+ .replace(/^-+|-+$/g, '')
6
+ }
7
+
8
+ export const generateProductId = (
9
+ slug: string,
10
+ variants?: string[] | undefined
11
+ ): string => {
12
+ const slugified = slugify(slug)
13
+
14
+ if (variants && variants.length > 0)
15
+ return `${slugified}--${variants.map((variant) => slugify(variant)).join('--')}`
16
+
17
+ return slugified
18
+ }
19
+
20
+ export const generateProductName = (
21
+ title: string,
22
+ variants?: string[] | undefined
23
+ ): string => {
24
+ if (variants && variants.length > 0)
25
+ return `${title} / ${variants.join(' / ')}`
26
+
27
+ return title
28
+ }
29
+
30
+ export const formatPrice = (price: number): string => {
31
+ return `€${price?.toFixed(2).replace('.', ',')}`
32
+ }
@@ -1,7 +1,9 @@
1
1
  import { getCollection, type CollectionEntry } from 'astro:content'
2
2
 
3
3
  // Records that are in a folder with the current page's slug
4
- export const getChildPages = async (slug: CollectionEntry<'pages'>['slug']) =>
4
+ export const getFolderChildren = async (
5
+ slug: CollectionEntry<'pages'>['slug']
6
+ ) =>
5
7
  (
6
8
  await getCollection(
7
9
  'pages',
@@ -1,11 +1,14 @@
1
1
  import { getCollection, type CollectionEntry } from 'astro:content'
2
2
 
3
3
  // Pages that reference the current page
4
- export const getCategoryPages = async (
4
+ export const getReferenceChildren = async (
5
5
  slug: CollectionEntry<'pages'>['slug']
6
6
  ) =>
7
7
  (
8
8
  await getCollection('pages', (page) =>
9
- page.data?.categories?.some((p: any) => p.slug === slug)
9
+ page.data?.parents?.some((p: any) => p.slug === slug)
10
10
  )
11
- )?.map((page) => page.data)
11
+ )?.map(({ slug, data }) => ({
12
+ ...data,
13
+ href: `/${slug}/`,
14
+ }))
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "extends": "astro/tsconfigs/strictest",
3
+ "include": ["src", "astro.config.ts"],
3
4
  "compilerOptions": {
4
5
  "baseUrl": ".",
5
6
  "paths": {
@@ -8,7 +9,4 @@
8
9
  ]
9
10
  }
10
11
  },
11
- "exclude": [
12
- "dist"
13
- ]
14
12
  }
@@ -1,161 +0,0 @@
1
- ---
2
- import Icon from 'fulldev-ui/components/Icon.astro'
3
- import Image from 'fulldev-ui/components/Image.astro'
4
-
5
- const { images, direction } = Astro.props
6
- ---
7
-
8
- <script>
9
- const carousel = document.querySelector('.gallery .large-carousel')
10
- const icon = document.querySelector('.gallery .icon')
11
- const smallImages = document.querySelectorAll('.gallery .small-image')
12
-
13
- carousel &&
14
- icon?.addEventListener('click', () => {
15
- const carouselWidth = (carousel as HTMLElement).offsetWidth
16
- const currentScroll = carousel.scrollLeft
17
- const maxScroll = carousel.scrollWidth - carouselWidth
18
-
19
- let scrollAmount = currentScroll + carouselWidth
20
- if (scrollAmount > maxScroll) {
21
- scrollAmount = 0 // Reset to beginning if we're at the end
22
- }
23
-
24
- carousel.scrollTo({
25
- left: scrollAmount,
26
- behavior: 'smooth',
27
- })
28
- })
29
-
30
- carousel &&
31
- smallImages.forEach((image, i) => {
32
- image.addEventListener('click', () => {
33
- const imageWidth = (carousel as HTMLElement).offsetWidth
34
- carousel.scrollTo({
35
- left: i * imageWidth,
36
- behavior: 'smooth',
37
- })
38
- })
39
- })
40
- </script>
41
-
42
- <div
43
- class:list={[
44
- 'full',
45
- 'gallery',
46
- {
47
- [`direction-${direction}`]: direction,
48
- },
49
- ]}
50
- >
51
- <div class="large-container">
52
- <div class="large-carousel">
53
- {
54
- images?.map((image: any) => (
55
- <Image
56
- class="large-image"
57
- src={image}
58
- />
59
- ))
60
- }
61
- </div>
62
- {
63
- images?.length > 1 && (
64
- <Icon
65
- class="icon"
66
- name="swipe-left"
67
- />
68
- )
69
- }
70
- </div>
71
- <div class="small-carousel">
72
- {
73
- images?.length > 1 &&
74
- images.map((image: any) => (
75
- <Image
76
- class="small-image"
77
- src={image}
78
- />
79
- ))
80
- }
81
- </div>
82
- </div>
83
-
84
- <style is:global lang="scss">
85
- .gallery {
86
- display: flex;
87
- position: relative;
88
- flex-direction: column;
89
- gap: var(--space-1);
90
-
91
- .large-container {
92
- display: flex;
93
- justify-content: space-between;
94
- align-items: center;
95
-
96
- &:hover .icon {
97
- opacity: 1;
98
- }
99
-
100
- .icon {
101
- position: absolute;
102
- right: var(--space-3);
103
- opacity: 0.5;
104
- animation: swipe-icon 1.6s ease-in-out infinite;
105
- transition: opacity 0.2s ease-in-out;
106
- font-size: calc(var(--text-2) * 1.7);
107
- }
108
-
109
- @keyframes swipe-icon {
110
- 0%,
111
- 100% {
112
- right: var(--space-3);
113
- }
114
- 50% {
115
- right: calc(var(--space-3) * 2);
116
- }
117
- }
118
- }
119
-
120
- .small-carousel,
121
- .large-carousel {
122
- --column-width: 100%;
123
- display: flex;
124
- position: relative;
125
- flex-direction: row;
126
- align-items: flex-start;
127
- gap: var(--space-4);
128
-
129
- box-sizing: border-box;
130
- padding-bottom: var(--space-4);
131
- width: 100%;
132
- overflow-x: auto;
133
- scroll-behavior: smooth;
134
- scroll-snap-type: x mandatory;
135
-
136
- > .root {
137
- flex-grow: 0;
138
- flex-shrink: 0;
139
- width: var(--column-width);
140
- scroll-snap-align: center;
141
- }
142
-
143
- &.position-inset {
144
- margin-right: calc(var(--inner-x) * -1);
145
- margin-left: calc(var(--inner-x) * -1);
146
- padding-right: var(--inner-x);
147
- padding-left: var(--inner-x);
148
- width: calc(100% + var(--inner-x) * 2);
149
- }
150
- }
151
-
152
- .small-carousel {
153
- --column-width: var(--space-7);
154
- }
155
-
156
- .small-image:hover {
157
- opacity: 0.8;
158
- cursor: pointer;
159
- }
160
- }
161
- </style>
@@ -1,21 +0,0 @@
1
- ---
2
- presets: [docs, blocks]
3
-
4
- title: Layout
5
- ---
6
-
7
- ```astro live
8
- ---
9
- import Hero from 'fulldev-ui/blocks/Hero.astro'
10
- import Layout from 'fulldev-ui/layouts/Layout.astro'
11
- ---
12
-
13
- <Layout>
14
- <Hero
15
- heading="Layout"
16
- text="The Layout component is used to wrap the content of a page."
17
- align="center"
18
- frame="viewport"
19
- />
20
- </Layout>
21
- ```
@@ -1,21 +0,0 @@
1
- ---
2
- presets: [docs, blocks]
3
-
4
- title: PageLayout
5
- ---
6
-
7
- ```astro live
8
- ---
9
- import Hero from 'fulldev-ui/blocks/Hero.astro'
10
- import PageLayout from 'fulldev-ui/layouts/PageLayout.astro'
11
- ---
12
-
13
- <PageLayout>
14
- <Hero
15
- heading="PageLayout"
16
- text="The PageLayout component is used to wrap the content of a page."
17
- align="center"
18
- frame="viewport"
19
- />
20
- </PageLayout>
21
- ```