fulldev-ui 0.5.0-beta.5 → 0.5.0-beta.7

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 (42) hide show
  1. package/CONTRIBUTING.md +10 -4
  2. package/astro.config.ts +2 -3
  3. package/package.json +4 -2
  4. package/pnpm-lock.yaml +31 -69
  5. package/src/blocks/Article.astro +31 -12
  6. package/src/blocks/Banner.astro +2 -1
  7. package/src/blocks/Product.astro +11 -21
  8. package/src/components/Card.astro +1 -1
  9. package/src/components/Cart.astro +38 -0
  10. package/src/components/CartForm.astro +121 -0
  11. package/src/components/CartList.astro +134 -0
  12. package/src/components/CartSummary.astro +146 -0
  13. package/src/components/Drawer.astro +41 -8
  14. package/src/components/Footer.astro +0 -1
  15. package/src/components/Form.astro +49 -0
  16. package/src/components/Header.astro +5 -8
  17. package/src/components/Image.astro +0 -1
  18. package/src/components/Input.astro +6 -0
  19. package/src/components/Price.astro +7 -11
  20. package/src/components/Search.astro +1 -4
  21. package/src/components/Section.astro +0 -1
  22. package/src/components/Sidebar.astro +1 -1
  23. package/src/components/Toc.astro +3 -6
  24. package/src/components/docs/PropsTable.astro +1 -1
  25. package/src/components/experimental/Form.astro +29 -5
  26. package/src/content/config.ts +2 -1
  27. package/src/content/pages/base/select.mdx +0 -8
  28. package/src/content/pages/base/textarea.mdx +0 -5
  29. package/src/content/presets/base.yml +1 -2
  30. package/src/content/presets/docs.yml +4 -4
  31. package/src/env.d.ts +1 -1
  32. package/src/layouts/DocsLayout.astro +23 -35
  33. package/src/layouts/Layout.astro +12 -16
  34. package/src/layouts/OverviewLayout.astro +5 -2
  35. package/src/layouts/PostLayout.astro +7 -10
  36. package/src/schemas/pageSchema.ts +53 -28
  37. package/src/schemas/presetSchema.ts +3 -0
  38. package/src/schemas/utils/navigation.ts +5 -1
  39. package/src/stores/cart.ts +23 -0
  40. package/src/utils/cart.ts +36 -0
  41. package/tsconfig.json +1 -3
  42. /package/src/types/{global-props.d.ts → global-props.ts} +0 -0
@@ -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,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>
@@ -5,6 +5,7 @@ import Element from 'fulldev-ui/components/Element.astro'
5
5
  import Logo from 'fulldev-ui/components/experimental/Logo.astro'
6
6
  import Menu from 'fulldev-ui/components/Menu.astro'
7
7
  import type { GlobalProps } from 'fulldev-ui/types/global-props'
8
+ import Buttons from './Buttons.astro'
8
9
  import Heading from './Heading.astro'
9
10
 
10
11
  interface Props extends GlobalProps, HTMLAttributes<'aside'> {
@@ -15,6 +16,7 @@ interface Props extends GlobalProps, HTMLAttributes<'aside'> {
15
16
  logo?: ComponentProps<typeof Logo> | undefined
16
17
  company?: ComponentProps<typeof Logo>['company']
17
18
  menu?: ComponentProps<typeof Menu>['groups']
19
+ buttons?: ComponentProps<typeof Buttons>['buttons']
18
20
  }
19
21
 
20
22
  const {
@@ -24,15 +26,21 @@ const {
24
26
  color,
25
27
  variant,
26
28
  text,
29
+ buttons,
27
30
  heading,
28
31
  logo,
29
32
  company,
30
33
  menu,
34
+ if: ifProp,
31
35
  ...rest
32
36
  } = Astro.props
37
+
38
+ const buttonsGroup = { heading: '', buttons: buttons }
39
+ const mergedMenu = [buttonsGroup, ...(menu || [])] as typeof menu
33
40
  ---
34
41
 
35
42
  <Button
43
+ if={ifProp}
36
44
  variant="secondary"
37
45
  class:list={['drawer-button', `${id}-button`]}
38
46
  onclick={`document.getElementById('${id}').showModal()`}
@@ -43,6 +51,7 @@ const {
43
51
  />
44
52
 
45
53
  <Element
54
+ if={ifProp}
46
55
  onclick={`event.target === this && this.close()`}
47
56
  class:list="drawer"
48
57
  as="dialog"
@@ -72,9 +81,15 @@ const {
72
81
  <slot name="header" />
73
82
  </Element>
74
83
  <Element class:list="drawer-content">
75
- <Menu groups={menu} />
84
+ <Menu groups={mergedMenu} />
76
85
  <slot />
77
86
  </Element>
87
+ <Element
88
+ class:list="drawer-footer"
89
+ as="footer"
90
+ >
91
+ <slot name="footer" />
92
+ </Element>
78
93
  </Element>
79
94
  </Element>
80
95
 
@@ -99,7 +114,7 @@ const {
99
114
  max-height: 100%;
100
115
 
101
116
  @media (min-width: 768px) {
102
- width: 360px;
117
+ width: 440px;
103
118
  }
104
119
 
105
120
  &::backdrop {
@@ -108,19 +123,35 @@ const {
108
123
  }
109
124
 
110
125
  .drawer-body {
126
+ display: flex;
111
127
  flex-shrink: 1;
128
+ flex-direction: column;
112
129
  border-right: 1px solid var(--color-a6);
113
130
  height: 100%;
114
131
  overflow: auto;
115
132
  }
116
133
 
117
- .drawer-header {
134
+ .drawer-content {
135
+ display: flex;
136
+ flex-direction: column;
137
+ padding: var(--space-6) var(--gutter);
138
+ }
139
+
140
+ .drawer-header,
141
+ .drawer-footer {
118
142
  display: flex;
119
143
  position: sticky;
144
+ flex-shrink: 0;
145
+ z-index: 1;
146
+ border-style: solid;
147
+ border-color: var(--color-a6);
148
+ background-color: var(--base-background);
149
+ }
150
+
151
+ .drawer-header {
120
152
  top: 0;
121
153
  align-items: center;
122
- border-bottom: 1px solid var(--color-a6);
123
- background-color: var(--base-background);
154
+ border-bottom-width: 1px;
124
155
  padding: 0 var(--gutter);
125
156
  height: 56px;
126
157
 
@@ -129,10 +160,12 @@ const {
129
160
  }
130
161
  }
131
162
 
132
- .drawer-content {
133
- display: flex;
163
+ .drawer-footer {
164
+ bottom: 0;
134
165
  flex-direction: column;
135
- padding: var(--space-6) var(--gutter);
166
+ margin-top: auto;
167
+ border-top-width: 1px;
168
+ padding: var(--space-5) var(--gutter);
136
169
  }
137
170
  }
138
171
  }
@@ -115,7 +115,6 @@ const {
115
115
  background-color: var(--base-2);
116
116
  padding: var(--space-7) var(--gutter);
117
117
  width: 100%;
118
- overflow: auto;
119
118
 
120
119
  @media (min-width: 1024px) {
121
120
  grid-auto-columns: 1fr;
@@ -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,20 +52,17 @@ 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"
59
58
  id="menu"
60
- {menu}
61
59
  {logo}
62
60
  {company}
61
+ {buttons}
62
+ {menu}
63
63
  />
64
- <Drawer
65
- icon="shopping-cart"
66
- class="cart-button"
67
- color="brand"
68
- id="cart"
64
+ <Cart
65
+ if={cart}
69
66
  heading={cart}
70
67
  />
71
68
  </Element>
@@ -42,7 +42,6 @@ const image = images[src as keyof typeof images]
42
42
  image && (
43
43
  <AstroImage
44
44
  class:list={['image', position && `position-${position}`]}
45
- style={{ '--screen': '100vw' }}
46
45
  alt={alt || getAltBySrc()}
47
46
  src={image ? image() : src}
48
47
  width={width}
@@ -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
- value && (
18
- <Element
19
- if={value}
20
- class:list="price"
21
- {...rest}
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 {
@@ -154,7 +154,6 @@ const Component =
154
154
  align-items: flex-start;
155
155
  gap: var(--space-6) var(--space-7);
156
156
  padding: var(--spacer) var(--gutter);
157
- overflow: auto;
158
157
 
159
158
  .segment {
160
159
  display: flex;
@@ -91,7 +91,7 @@ const {
91
91
  top: 0;
92
92
  flex: 1;
93
93
  flex-direction: column;
94
- padding: var(--space-6) var(--gutter);
94
+ padding: var(--space-md-6) var(--gutter);
95
95
  width: 100%;
96
96
  overflow-y: auto;
97
97
  }
@@ -20,7 +20,7 @@ const filteredHeadings = headings?.filter(
20
20
  ---
21
21
 
22
22
  <Element
23
- if={filteredHeadings?.length > 0}
23
+ if={filteredHeadings?.length > 1}
24
24
  class:list="toc"
25
25
  {size}
26
26
  {...rest}
@@ -49,17 +49,14 @@ const filteredHeadings = headings?.filter(
49
49
  @media (min-width: 1024px) {
50
50
  display: flex;
51
51
  position: sticky;
52
- top: var(--header-height, 0px);
52
+ top: calc(var(--header-height, 0px) + var(--space-6));
53
53
  bottom: 0;
54
54
  flex-shrink: 0;
55
55
  flex-direction: column;
56
56
  justify-content: flex-start;
57
57
  gap: var(--space-4);
58
58
  z-index: 10;
59
- padding: var(--space-md-6) var(--gutter);
60
- width: calc(var(--space-7) * 4);
61
- height: calc(100vh - var(--header-height, 0px));
62
- overflow-y: auto;
59
+ min-width: calc(var(--space-7) * 3);
63
60
 
64
61
  .depth-3 {
65
62
  padding-left: var(--space-4);
@@ -3,7 +3,7 @@ import Table from 'fulldev-ui/components/Table.astro'
3
3
  import { readFileSync } from 'node:fs'
4
4
  import { pascal } from 'radash'
5
5
 
6
- const slug = Astro.url.pathname.replace(/\/$/, '').split('/').pop() || ''
6
+ const slug = Astro.params.page || 'index'
7
7
  const componentName = pascal(slug)
8
8
 
9
9
  const allComponents = import.meta.glob('/src/**/*.astro', { eager: true })
@@ -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
- structure?: 'column' | 'row' | 'group' | undefined
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 { structure = 'column', ...rest } = Astro.props
21
+ const { fields, submit, ...rest } = Astro.props
11
22
  ---
12
23
 
13
24
  <Element
14
- class:list={`form ${structure}`}
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">