fulldev-ui 0.4.14 → 0.4.16

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 (47) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +7 -52
  2. package/.github/ISSUE_TEMPLATE/config.yml +3 -0
  3. package/.github/ISSUE_TEMPLATE/documentation.yml +12 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.yml +7 -34
  5. package/.github/ISSUE_TEMPLATE/support.yml +12 -0
  6. package/.github/pull_request_template.md +1 -13
  7. package/CODE_OF_CONDUCT.md +6 -0
  8. package/CONTRIBUTING.md +20 -4
  9. package/README.md +1 -0
  10. package/package.json +6 -5
  11. package/pnpm-lock.yaml +2903 -3639
  12. package/public/showcase/anti-slip-profiles.webp +0 -0
  13. package/public/showcase/fulldev-ui.webp +0 -0
  14. package/public/showcase/lavis-detailing.webp +0 -0
  15. package/public/showcase/leren-trainingsacteren.webp +0 -0
  16. package/public/showcase/miele-witgoedcenter-amsterdam.webp +0 -0
  17. package/public/showcase/sloepverhuur-bolsward.webp +0 -0
  18. package/public/showcase/stark-vulcanising-products.webp +0 -0
  19. package/public/showcase/the-gym-haren.webp +0 -0
  20. package/public/showcase/van-dillen-antieke-bouwmaterialen.webp +0 -0
  21. package/src/blocks/Features.astro +1 -1
  22. package/src/blocks/Prices.astro +1 -1
  23. package/src/components/Badge.astro +1 -1
  24. package/src/components/Box.astro +3 -0
  25. package/src/components/Button.astro +7 -5
  26. package/src/components/Card.astro +23 -2
  27. package/src/components/Drawer.astro +1 -0
  28. package/src/components/Footer.astro +1 -1
  29. package/src/components/Grid.astro +1 -1
  30. package/src/components/Header.astro +52 -48
  31. package/src/components/Rating.astro +6 -2
  32. package/src/components/Root.astro +1 -1
  33. package/src/components/Section.astro +20 -2
  34. package/src/components/Stack.astro +36 -0
  35. package/src/components/docs/NavigationButtons.astro +59 -0
  36. package/src/components/experimental/Channel.astro +1 -4
  37. package/src/content/pages/_segment/footer.mdx +11 -11
  38. package/src/content/pages/base/badge.mdx +1 -1
  39. package/src/content/pages/base/button.mdx +9 -9
  40. package/src/content/pages/overview/showcase.mdx +62 -0
  41. package/src/content/pages/structure/stack.mdx +22 -0
  42. package/src/content/settings/base.yml +4 -0
  43. package/src/content/settings/components.yml +2 -0
  44. package/src/css/custom.css +16 -1
  45. package/src/integration/index.ts +7 -7
  46. package/src/layouts/Layout.astro +63 -62
  47. package/src/schemas/base.ts +10 -1
Binary file
@@ -6,6 +6,6 @@ const { ...rest } = Astro.props
6
6
 
7
7
  <Section
8
8
  class:list="features"
9
- structure="grid"
9
+ structure="stack"
10
10
  {...rest}
11
11
  />
@@ -6,7 +6,7 @@ const { ...rest } = Astro.props
6
6
 
7
7
  <Section
8
8
  class:list="prices"
9
- structure="grid"
9
+ structure="stack"
10
10
  frame="panel"
11
11
  {...rest}
12
12
  />
@@ -34,8 +34,8 @@ const {
34
34
  <slot>
35
35
  <Fragment set:text={text} />
36
36
  <Fragment set:html={html} />
37
- <Icon name={icon} />
38
37
  </slot>
38
+ <Icon name={icon} />
39
39
  </Root>
40
40
 
41
41
  <style is:global lang="scss">
@@ -8,6 +8,7 @@ import Masonry from './Masonry.astro'
8
8
  import Row from './Row.astro'
9
9
  import Split from './Split.astro'
10
10
  import Spread from './Spread.astro'
11
+ import Stack from './Stack.astro'
11
12
  import Wrap from './Wrap.astro'
12
13
 
13
14
  type Props<As extends HTMLTag = 'div'> = Polymorphic<{ as: As }> & {
@@ -25,6 +26,7 @@ type Props<As extends HTMLTag = 'div'> = Polymorphic<{ as: As }> & {
25
26
  | 'split'
26
27
  | 'spread'
27
28
  | 'wrap'
29
+ | 'stack'
28
30
  }
29
31
 
30
32
  const structuresMap = {
@@ -37,6 +39,7 @@ const structuresMap = {
37
39
  split: Split,
38
40
  spread: Spread,
39
41
  wrap: Wrap,
42
+ stack: Stack,
40
43
  }
41
44
 
42
45
  const {
@@ -35,9 +35,9 @@ const {
35
35
  {radius}
36
36
  {...rest}
37
37
  >
38
+ <Avatar src={avatar} />
39
+ <Icon name={icon} />
38
40
  <slot>
39
- <Avatar src={avatar} />
40
- <Icon name={icon} />
41
41
  <Fragment set:html={html} />
42
42
  <Fragment set:text={text} />
43
43
  </slot>
@@ -57,7 +57,7 @@ const {
57
57
  cursor: pointer;
58
58
  outline: none;
59
59
  box-shadow: none;
60
- border: none;
60
+ border-width: 0px;
61
61
  background: none;
62
62
  padding: 0 var(--space-4);
63
63
  height: calc(var(--text-2) + 2 * var(--space-4));
@@ -111,21 +111,23 @@ const {
111
111
  }
112
112
 
113
113
  &.variant-secondary {
114
- box-shadow: 0 var(--space-1) var(--space-3) 0 rgba(0, 0, 0, 0.05);
114
+ border-style: solid;
115
+ border-color: var(--color-7);
115
116
  background-color: var(--color-3);
116
117
  color: var(--color-11);
117
118
 
118
119
  &:hover {
120
+ border-color: var(--color-8);
119
121
  background-color: var(--color-4);
120
122
  }
121
123
 
122
124
  &:active {
125
+ border-color: var(--color-8);
123
126
  background-color: var(--color-5);
124
127
  }
125
128
  }
126
129
 
127
130
  &.variant-tertiary {
128
- box-shadow: 0 var(--space-1) var(--space-3) 0 rgba(0, 0, 0, 0.05);
129
131
  background-color: transparent;
130
132
  color: var(--color-11);
131
133
 
@@ -12,12 +12,14 @@ import Spread from 'fulldev-ui/components/Spread.astro'
12
12
  import Tagline from 'fulldev-ui/components/Tagline.astro'
13
13
  import Text from 'fulldev-ui/components/Text.astro'
14
14
  import Wrap from 'fulldev-ui/components/Wrap.astro'
15
+ import Badge from './Badge.astro'
15
16
 
16
17
  type Props<As extends HTMLTag = 'a'> = Polymorphic<{ as: As }> & {
17
18
  ratio?: 'landscape' | 'portrait' | 'square' | number
18
19
  variant?: 'primary' | 'secondary' | 'tertiary'
19
20
  position?: 'background' | 'cover' | 'inset'
20
21
  align?: 'start' | 'center' | 'end'
22
+ justify?: 'start' | 'center' | 'end'
21
23
  level?: 1 | 2 | 3 | 4 | 5 | 6
22
24
  size?: 'sm' | 'md' | 'lg'
23
25
  radius?: 'auto' | 'none'
@@ -26,8 +28,11 @@ type Props<As extends HTMLTag = 'a'> = Polymorphic<{ as: As }> & {
26
28
  space?: 'auto' | 'none'
27
29
  mask?: 'sm' | 'md' | 'lg'
28
30
  image?: string
31
+ images?: string[]
29
32
  button?: ComponentProps<typeof Button<'a'>>
30
33
  buttons?: ComponentProps<typeof Button<'a'>>[]
34
+ badge?: string | ComponentProps<typeof Badge<'a'>>
35
+ badges?: (string | ComponentProps<typeof Badge<'a'>>)[]
31
36
  rating?: number | ComponentProps<typeof Rating>
32
37
  price?: number
33
38
  icon?: string
@@ -48,7 +53,7 @@ const {
48
53
  href,
49
54
  as = href ? 'a' : 'div',
50
55
  space = 'auto',
51
- frame = 'panel',
56
+ frame = 'none',
52
57
  radius = 'auto',
53
58
  align = 'start',
54
59
  variant,
@@ -63,8 +68,11 @@ const {
63
68
  description,
64
69
  list,
65
70
  buttons,
71
+ badge,
72
+ badges,
66
73
  ratio,
67
74
  image,
75
+ images,
68
76
  rating,
69
77
  button,
70
78
  price,
@@ -101,13 +109,26 @@ const buttonVariant = (i: number) => {
101
109
  {position}
102
110
  {mask}
103
111
  ratio={position === 'background' ? undefined : ratio}
104
- src={image}
112
+ src={image || images?.[0]}
105
113
  />
106
114
  <Icon name={icon} />
107
115
  <Rating
108
116
  color="brand"
117
+ {avatars}
118
+ {avatar}
119
+ {label}
109
120
  {...typeof rating === 'number' ? { value: rating } : rating}
110
121
  />
122
+ <Wrap>
123
+ {
124
+ [badge, ...(badges ?? [])].filter(Boolean)?.map((badge: any) => (
125
+ <Badge
126
+ color="base"
127
+ {...(typeof badge === 'string' ? { html: badge } : badge)}
128
+ />
129
+ ))
130
+ }
131
+ </Wrap>
111
132
  <Tagline
112
133
  color="base"
113
134
  html={tagline}
@@ -59,6 +59,7 @@ const {
59
59
  background: var(--color-2);
60
60
  padding: 0;
61
61
  padding: var(--space-7) var(--space-7) var(--space-7) var(--gutter);
62
+ min-width: 260px;
62
63
  max-width: 360px;
63
64
  height: 100vh;
64
65
  max-height: 100vh;
@@ -8,7 +8,7 @@ import Text from 'fulldev-ui/components/Text.astro'
8
8
  import Logo from 'fulldev-ui/components/experimental/Logo.astro'
9
9
 
10
10
  type Props<As extends HTMLTag = 'footer'> = Polymorphic<{ as: As }> & {
11
- structure?: 'spread' | 'row' | 'split' | 'column'
11
+ structure?: 'spread' | 'row' | 'split' | 'column' | 'stack'
12
12
  variant?: 'base' | 'ghost' | 'subtle' | 'soft' | 'surface' | 'outline'
13
13
  frame?: 'fill' | 'panel' | 'contain'
14
14
  align?: 'start' | 'center' | 'end'
@@ -24,7 +24,7 @@ const { ...rest } = Astro.props
24
24
 
25
25
  display: grid;
26
26
  position: relative;
27
- grid-template-columns: repeat(auto-fit, minmax(var(--column-width), 1fr));
27
+ grid-template-columns: repeat(auto-fill, minmax(var(--column-width), 1fr));
28
28
  gap: var(--space-4);
29
29
  width: 100%;
30
30
  }
@@ -32,8 +32,7 @@ const {
32
32
  structure = 'spread',
33
33
  space = 'auto',
34
34
  size = 'sm',
35
- frame = 'fill',
36
- variant = 'base',
35
+ frame = 'none',
37
36
  logo,
38
37
  heading,
39
38
  links,
@@ -52,7 +51,6 @@ const url = Astro.url.href
52
51
  {as}
53
52
  {frame}
54
53
  {structure}
55
- {variant}
56
54
  {space}
57
55
  {size}
58
56
  {...rest}
@@ -86,52 +84,54 @@ const url = Astro.url.href
86
84
  />
87
85
  ))
88
86
  }
89
- <Search {...search} />
90
- <Drawer
91
- icon="menu-2"
92
- id="navigation-drawer"
93
- >
94
- {
95
- drawer &&
96
- drawer.length > 0 &&
97
- drawer.map((item: any) => (
98
- <>
99
- <Heading
100
- level={6}
101
- html={item.heading}
102
- />
103
- {item.links?.map((link: any) => (
104
- <Link
105
- {...link}
106
- class="sidebar-link"
107
- color={
108
- url.replace(/\/$/, '').endsWith(link.href)
109
- ? 'brand'
110
- : undefined
111
- }
87
+ {search && <Search {...search} />}
88
+ {
89
+ drawer && (
90
+ <Drawer
91
+ icon="menu-2"
92
+ id="navigation-drawer"
93
+ >
94
+ {drawer &&
95
+ drawer.length > 0 &&
96
+ drawer.map((item: any) => (
97
+ <>
98
+ <Heading
99
+ level={6}
100
+ html={item.heading}
112
101
  />
113
- ))}
114
- {item.folder &&
115
- (async () => {
116
- const pages = await getCollection(
117
- 'pages',
118
- (entry: any) =>
119
- entry.slug.startsWith(item.folder) &&
120
- !entry.slug.endsWith(item.folder)
121
- )
122
- return pages.map((page: any) => (
123
- <Link
124
- href={`/${page.slug}`}
125
- class="sidebar-link"
126
- color={slug === page.slug ? 'brand' : undefined}
127
- html={page.data.title}
128
- />
129
- ))
130
- })()}
131
- </>
132
- ))
133
- }
134
- </Drawer>
102
+ {item.links?.map((link: any) => (
103
+ <Link
104
+ {...link}
105
+ class="sidebar-link"
106
+ color={
107
+ url.replace(/\/$/, '').endsWith(link.href)
108
+ ? 'brand'
109
+ : undefined
110
+ }
111
+ />
112
+ ))}
113
+ {item.folder &&
114
+ (async () => {
115
+ const pages = await getCollection(
116
+ 'pages',
117
+ (entry: any) =>
118
+ entry.slug.startsWith(item.folder) &&
119
+ !entry.slug.endsWith(item.folder)
120
+ )
121
+ return pages.map((page: any) => (
122
+ <Link
123
+ href={`/${page.slug}`}
124
+ class="sidebar-link"
125
+ color={slug === page.slug ? 'brand' : undefined}
126
+ html={page.data.title}
127
+ />
128
+ ))
129
+ })()}
130
+ </>
131
+ ))}
132
+ </Drawer>
133
+ )
134
+ }
135
135
  </Row>
136
136
  </Root>
137
137
 
@@ -160,6 +160,10 @@ const url = Astro.url.href
160
160
  max-width: calc(100% - (2 * var(--outer-x)));
161
161
  height: var(--header-height);
162
162
 
163
+ &.frame-none {
164
+ padding: 0 var(--gutter);
165
+ }
166
+
163
167
  &.frame-fill {
164
168
  background-color: var(--color-2);
165
169
  padding: 0 var(--gutter);
@@ -8,6 +8,7 @@ import Label from './Label.astro'
8
8
 
9
9
  type Props = HTMLAttributes<'div'> & {
10
10
  color?: 'base' | 'brand'
11
+ align?: 'start' | 'center' | 'end'
11
12
  value?: number
12
13
  size?: string
13
14
  label?: string
@@ -15,7 +16,7 @@ type Props = HTMLAttributes<'div'> & {
15
16
  avatar?: string
16
17
  }
17
18
 
18
- const { value, label, avatars, avatar, ...rest } = Astro.props
19
+ const { value, label, avatars, avatar, align, ...rest } = Astro.props
19
20
 
20
21
  const getIcon = (count: number) => {
21
22
  if (!value) return
@@ -24,6 +25,8 @@ const getIcon = (count: number) => {
24
25
  if (difference >= -0.75) return 'star-half-filled'
25
26
  return 'star'
26
27
  }
28
+
29
+ const hasAvatars = avatars && avatars?.length > 0
27
30
  ---
28
31
 
29
32
  <Root
@@ -38,7 +41,7 @@ const getIcon = (count: number) => {
38
41
  />
39
42
  ))
40
43
  }
41
- <Column>
44
+ <Column align={!hasAvatars ? align : undefined}>
42
45
  <Root
43
46
  class:list={'stars'}
44
47
  {...rest}
@@ -64,6 +67,7 @@ const getIcon = (count: number) => {
64
67
  @layer fulldev {
65
68
  .rating {
66
69
  display: flex;
70
+ align-items: center;
67
71
  gap: var(--space-4);
68
72
 
69
73
  .avatar + .avatar {
@@ -87,7 +87,7 @@ const render =
87
87
 
88
88
  @layer fulldev {
89
89
  .root {
90
- z-index: 1;
90
+ z-index: 0;
91
91
  box-sizing: border-box;
92
92
  }
93
93
  }
@@ -41,7 +41,14 @@ type Props<As extends HTMLTag = 'section'> = Polymorphic<{ as: As }> & {
41
41
  frame?: 'none' | 'fill' | 'panel'
42
42
  align?: 'start' | 'center' | 'end'
43
43
  justify?: 'start' | 'center' | 'end'
44
- structure?: 'column' | 'split' | 'spread' | 'carousel' | 'grid' | 'masonry'
44
+ structure?:
45
+ | 'column'
46
+ | 'split'
47
+ | 'spread'
48
+ | 'carousel'
49
+ | 'grid'
50
+ | 'masonry'
51
+ | 'stack'
45
52
  level?: 1 | 2 | 3 | 4 | 5 | 6
46
53
  space?: 'auto' | 'none'
47
54
  reverse?: 'odd' | 'even'
@@ -75,12 +82,16 @@ const {
75
82
  pages,
76
83
  records,
77
84
  mask,
85
+ justify,
86
+ theme,
78
87
  ratio,
79
88
  ...rest
80
89
  } = Astro.props
81
90
 
82
91
  const mergedCards = (pages || records || cards)?.filter(Boolean)
83
92
  const hasCards = mergedCards && mergedCards?.length > 0
93
+ const hasText = text || html || description || list
94
+ const hasButton = button || (buttons && buttons?.length > 0)
84
95
 
85
96
  const buttonVariant = (i: number) => {
86
97
  if (variant === 'primary' && i === 0) return 'primary'
@@ -95,6 +106,8 @@ const buttonVariant = (i: number) => {
95
106
  <Root
96
107
  class:list="section"
97
108
  variant={hasCards ? undefined : variant}
109
+ justify={hasCards ? undefined : justify}
110
+ theme={hasCards ? undefined : theme}
98
111
  structure={structure === 'split' ? 'split' : 'column'}
99
112
  frame={frame === 'panel' && hasCards ? 'none' : frame}
100
113
  {as}
@@ -104,7 +117,9 @@ const buttonVariant = (i: number) => {
104
117
  {...rest}
105
118
  >
106
119
  <Box
107
- structure={structure === 'spread' || structure === 'carousel'
120
+ structure={structure === 'spread' ||
121
+ structure === 'carousel' ||
122
+ (!hasText && hasButton)
108
123
  ? 'spread'
109
124
  : 'column'}
110
125
  align={structure === 'split' ? 'start' : align}
@@ -119,6 +134,7 @@ const buttonVariant = (i: number) => {
119
134
  <slot name="writeup">
120
135
  <Rating
121
136
  color="brand"
137
+ {align}
122
138
  {...typeof rating === 'number' ? { value: rating } : rating}
123
139
  />
124
140
  <Badge
@@ -174,6 +190,8 @@ const buttonVariant = (i: number) => {
174
190
  level={Number(level) + 2}
175
191
  {position}
176
192
  {frame}
193
+ {justify}
194
+ {theme}
177
195
  {variant}
178
196
  {ratio}
179
197
  {mask}
@@ -0,0 +1,36 @@
1
+ ---
2
+ import type { HTMLTag, Polymorphic } from 'astro/types'
3
+ import Root from 'fulldev-ui/components/Root.astro'
4
+
5
+ type Props<As extends HTMLTag = 'div'> = Polymorphic<{ as: As }> & {
6
+ color?: 'base' | 'brand'
7
+ size?: 'sm' | 'md' | 'lg'
8
+ }
9
+
10
+ const { ...rest } = Astro.props
11
+ ---
12
+
13
+ <Root
14
+ class:list="stack"
15
+ {...rest}
16
+ >
17
+ <slot />
18
+ </Root>
19
+
20
+ <style is:global lang="scss">
21
+ @layer fulldev {
22
+ .stack {
23
+ display: grid;
24
+ position: relative;
25
+ grid-template-columns: 1fr;
26
+ align-items: flex-start;
27
+ gap: var(--space-6);
28
+ width: 100%;
29
+
30
+ @media (min-width: 1024px) {
31
+ grid-auto-columns: 1fr;
32
+ grid-auto-flow: column;
33
+ }
34
+ }
35
+ }
36
+ </style>
@@ -0,0 +1,59 @@
1
+ ---
2
+ import { getCollection } from 'astro:content'
3
+ import Button from 'fulldev-ui/components/Button.astro'
4
+ import Section from '../Section.astro'
5
+ import Spread from '../Spread.astro'
6
+
7
+ const { sidebar = [] } = Astro.props
8
+
9
+ const allPages = await getCollection('pages')
10
+ const pages = []
11
+
12
+ for (const item of sidebar as any) {
13
+ if (item.links) pages.push(...item.links)
14
+ if (item.folder) {
15
+ const folderPages = allPages.filter(
16
+ (page: any) =>
17
+ page.slug.startsWith(item.folder) && !page.slug.endsWith(item.folder)
18
+ )
19
+ pages.push(
20
+ ...folderPages.map((page: any) => ({
21
+ href: `/${page.slug}`,
22
+ text: page.data.title,
23
+ }))
24
+ )
25
+ }
26
+ }
27
+
28
+ const withoutLeadingOrTrailingSlashes = (path: string) =>
29
+ path.replace(/^\/+|\/+$/g, '')
30
+
31
+ const currentIndex = pages.findIndex(
32
+ (page: any) =>
33
+ withoutLeadingOrTrailingSlashes(page?.href) ===
34
+ withoutLeadingOrTrailingSlashes(Astro.url.pathname)
35
+ )
36
+
37
+ const prevPage = pages[currentIndex - 1]
38
+ const nextPage = pages[currentIndex + 1]
39
+ ---
40
+
41
+ {
42
+ sidebar && sidebar.length > 0 && (
43
+ <Section>
44
+ <Spread>
45
+ <Button
46
+ href={prevPage?.href}
47
+ variant="secondary"
48
+ text={prevPage ? `← ${prevPage.text}` : undefined}
49
+ />
50
+ <Button
51
+ href={nextPage?.href}
52
+ variant="secondary"
53
+ text={nextPage ? `${nextPage.text} →` : undefined}
54
+ style={{ marginLeft: 'auto' }}
55
+ />
56
+ </Spread>
57
+ </Section>
58
+ )
59
+ }
@@ -51,10 +51,7 @@ const {
51
51
  {level}
52
52
  set:html={heading}
53
53
  />
54
- <Text
55
- style={{ marginTop: 'var(--space-1)' }}
56
- set:html={text}
57
- />
54
+ <Text set:html={text} />
58
55
  </Column>
59
56
  </Root>
60
57