webcoreui 0.3.0 → 0.4.0

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 (127) hide show
  1. package/README.md +5 -2
  2. package/astro.d.ts +7 -1
  3. package/astro.js +6 -0
  4. package/components/Accordion/Accordion.astro +2 -0
  5. package/components/Accordion/Accordion.svelte +2 -0
  6. package/components/Accordion/Accordion.tsx +2 -0
  7. package/components/Alert/Alert.astro +3 -2
  8. package/components/Alert/Alert.svelte +4 -3
  9. package/components/Alert/Alert.tsx +3 -2
  10. package/components/Avatar/Avatar.astro +2 -1
  11. package/components/Avatar/Avatar.svelte +2 -1
  12. package/components/Avatar/Avatar.tsx +3 -3
  13. package/components/Badge/Badge.astro +1 -0
  14. package/components/Badge/Badge.svelte +3 -2
  15. package/components/Badge/Badge.tsx +2 -1
  16. package/components/Button/Button.astro +4 -5
  17. package/components/Button/Button.svelte +2 -1
  18. package/components/Button/Button.tsx +2 -1
  19. package/components/Button/button.ts +1 -1
  20. package/components/Card/Card.astro +11 -3
  21. package/components/Card/Card.svelte +5 -2
  22. package/components/Card/Card.tsx +5 -2
  23. package/components/Card/card.ts +1 -0
  24. package/components/Checkbox/Checkbox.astro +1 -0
  25. package/components/Checkbox/Checkbox.svelte +7 -5
  26. package/components/Checkbox/Checkbox.tsx +4 -2
  27. package/components/Collapsible/Collapsible.astro +2 -1
  28. package/components/Collapsible/Collapsible.svelte +2 -1
  29. package/components/Collapsible/Collapsible.tsx +55 -54
  30. package/components/ConditionalWrapper/ConditionalWrapper.astro +2 -1
  31. package/components/ConditionalWrapper/ConditionalWrapper.tsx +1 -2
  32. package/components/Group/Group.astro +22 -0
  33. package/components/Group/Group.svelte +20 -0
  34. package/components/Group/Group.tsx +22 -0
  35. package/components/Group/group.module.scss +43 -0
  36. package/components/Group/group.ts +8 -0
  37. package/components/Icon/map.ts +2 -0
  38. package/components/Input/Input.astro +8 -1
  39. package/components/Input/Input.svelte +15 -3
  40. package/components/Input/Input.tsx +10 -3
  41. package/components/Input/input.module.scss +4 -1
  42. package/components/Input/input.ts +9 -4
  43. package/components/List/List.astro +169 -0
  44. package/components/List/List.svelte +147 -0
  45. package/components/List/List.tsx +168 -0
  46. package/components/List/list.module.scss +91 -0
  47. package/components/List/list.ts +37 -0
  48. package/components/Menu/Menu.astro +2 -1
  49. package/components/Menu/Menu.svelte +7 -5
  50. package/components/Menu/Menu.tsx +116 -113
  51. package/components/Modal/Modal.astro +6 -4
  52. package/components/Modal/Modal.svelte +8 -6
  53. package/components/Modal/Modal.tsx +79 -76
  54. package/components/Modal/modal.ts +1 -0
  55. package/components/Popover/Popover.astro +4 -1
  56. package/components/Popover/Popover.svelte +4 -2
  57. package/components/Popover/Popover.tsx +55 -27
  58. package/components/Popover/popover.module.scss +1 -0
  59. package/components/Popover/popover.ts +2 -0
  60. package/components/Progress/Progress.astro +2 -1
  61. package/components/Progress/Progress.svelte +2 -1
  62. package/components/Progress/Progress.tsx +3 -2
  63. package/components/Radio/Radio.astro +1 -0
  64. package/components/Radio/Radio.svelte +4 -2
  65. package/components/Radio/Radio.tsx +3 -2
  66. package/components/Rating/Rating.astro +3 -1
  67. package/components/Rating/Rating.svelte +9 -7
  68. package/components/Rating/Rating.tsx +4 -2
  69. package/components/Select/Select.astro +135 -0
  70. package/components/Select/Select.svelte +122 -0
  71. package/components/Select/Select.tsx +142 -0
  72. package/components/Select/select.module.scss +25 -0
  73. package/components/Select/select.ts +21 -0
  74. package/components/Sheet/Sheet.astro +2 -1
  75. package/components/Sheet/Sheet.svelte +2 -1
  76. package/components/Sheet/Sheet.tsx +33 -32
  77. package/components/Slider/Slider.astro +2 -1
  78. package/components/Slider/Slider.svelte +2 -1
  79. package/components/Slider/Slider.tsx +49 -48
  80. package/components/Spinner/Spinner.astro +4 -3
  81. package/components/Spinner/Spinner.svelte +3 -2
  82. package/components/Spinner/Spinner.tsx +4 -3
  83. package/components/Switch/Switch.astro +2 -1
  84. package/components/Switch/Switch.svelte +5 -4
  85. package/components/Switch/Switch.tsx +2 -2
  86. package/components/Switch/switch.module.scss +1 -1
  87. package/components/Table/Table.astro +1 -0
  88. package/components/Table/Table.svelte +2 -1
  89. package/components/Table/Table.tsx +2 -1
  90. package/components/Tabs/Tabs.astro +2 -1
  91. package/components/Tabs/Tabs.svelte +2 -1
  92. package/components/Tabs/Tabs.tsx +4 -3
  93. package/components/Textarea/Textarea.astro +1 -0
  94. package/components/Textarea/Textarea.svelte +3 -1
  95. package/components/Textarea/Textarea.tsx +52 -50
  96. package/components/ThemeSwitcher/ThemeSwitcher.astro +108 -107
  97. package/components/ThemeSwitcher/ThemeSwitcher.svelte +5 -4
  98. package/components/ThemeSwitcher/ThemeSwitcher.tsx +91 -90
  99. package/components/Timeline/Timeline.astro +3 -2
  100. package/components/Timeline/Timeline.svelte +3 -2
  101. package/components/Timeline/Timeline.tsx +3 -2
  102. package/components/TimelineItem/TimelineItem.svelte +2 -1
  103. package/components/TimelineItem/TimelineItem.tsx +2 -1
  104. package/components/Toast/Toast.astro +3 -1
  105. package/components/Toast/Toast.svelte +3 -1
  106. package/components/Toast/Toast.tsx +3 -1
  107. package/icons/moon.svg +1 -1
  108. package/icons/search.svg +3 -0
  109. package/icons.d.ts +1 -0
  110. package/icons.js +1 -0
  111. package/index.d.ts +55 -25
  112. package/package.json +22 -4
  113. package/react.d.ts +6 -0
  114. package/react.js +6 -0
  115. package/scss/config/mixins.scss +12 -10
  116. package/scss/config/variables.scss +1 -0
  117. package/scss/config.scss +1 -0
  118. package/scss/global/utility.scss +2 -0
  119. package/svelte.d.ts +7 -1
  120. package/svelte.js +6 -0
  121. package/utils/cookies.ts +4 -4
  122. package/utils/debounce.ts +1 -1
  123. package/utils/event.ts +2 -2
  124. package/utils/interpolate.ts +5 -5
  125. package/utils/modal.ts +90 -27
  126. package/utils/popover.ts +30 -8
  127. package/utils/toast.ts +6 -2
@@ -0,0 +1,147 @@
1
+ <script lang="ts">
2
+ import type { ListEventType,SvelteListProps } from './list'
3
+
4
+ import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
5
+ import Input from '../Input/Input.svelte'
6
+
7
+ import { classNames } from '../../utils/classNames'
8
+
9
+ import searchIcon from '../../icons/search.svg?raw'
10
+
11
+ import styles from './list.module.scss'
12
+
13
+ export let showSearchBar: SvelteListProps['showSearchBar'] = false
14
+ export let showSearchBarIcon: SvelteListProps['showSearchBarIcon'] = false
15
+ export let searchBarPlaceholder: SvelteListProps['searchBarPlaceholder'] = ''
16
+ export let noResultsLabel: SvelteListProps['noResultsLabel'] = 'No results.'
17
+ export let maxHeight: SvelteListProps['maxHeight'] = ''
18
+ export let id: SvelteListProps['id'] = ''
19
+ export let className: SvelteListProps['className'] = ''
20
+ export let wrapperClassName: SvelteListProps['wrapperClassName'] = ''
21
+ export let itemGroups: SvelteListProps['itemGroups'] = []
22
+ export let onSelect: SvelteListProps['onSelect'] = () => {}
23
+
24
+ let searchValue = ''
25
+ let numberOfResults = 1
26
+
27
+ const classes = classNames([
28
+ styles.list,
29
+ !showSearchBar && styles.container,
30
+ className
31
+ ])
32
+
33
+ const wrapperClasses = classNames([
34
+ styles.container,
35
+ wrapperClassName
36
+ ])
37
+
38
+ const search = (event: InputEvent) => {
39
+ searchValue = (event.target as HTMLInputElement).value
40
+
41
+ numberOfResults = itemGroups
42
+ .map(group => group.items)
43
+ .flat()
44
+ .filter(item => {
45
+ return item.value?.toLowerCase().includes(searchValue)
46
+ || item.subText?.toLowerCase().includes(searchValue)
47
+ || item.name.toLowerCase().includes(searchValue)
48
+ }).length
49
+ }
50
+
51
+ const select = (event: MouseEvent | KeyboardEvent) => {
52
+ const li = event.target as HTMLLIElement
53
+
54
+ itemGroups = itemGroups.map(group => {
55
+ group.items = group.items.map(item => {
56
+ item.selected = li.dataset.name === item.name
57
+
58
+ return item
59
+ })
60
+
61
+ return group
62
+ })
63
+
64
+ onSelect?.({
65
+ ...li.dataset,
66
+ list: li.parentElement
67
+ } as ListEventType)
68
+ }
69
+
70
+ const selectByKey = (event: KeyboardEvent) => {
71
+ if (event.key === 'Enter') {
72
+ select(event)
73
+ }
74
+ }
75
+ </script>
76
+
77
+ <ConditionalWrapper
78
+ condition={!!showSearchBar}
79
+ class={wrapperClasses}
80
+ >
81
+ {#if showSearchBar}
82
+ <Input
83
+ type="search"
84
+ placeholder={searchBarPlaceholder}
85
+ onInput={search}
86
+ >
87
+ {#if showSearchBarIcon}
88
+ {@html searchIcon}
89
+ {/if}
90
+ </Input>
91
+ {/if}
92
+ <ul
93
+ class={classes}
94
+ id={id || null}
95
+ style={maxHeight ? `max-height: ${maxHeight}` : null}
96
+ >
97
+ {#each itemGroups as group}
98
+ {#if group.title}
99
+ <li class={styles.title}
100
+ data-hidden={searchValue ? true : null}
101
+ >
102
+ {group.title}
103
+ </li>
104
+ {/if}
105
+ {#each group.items as item}
106
+ <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
107
+ <li
108
+ tabIndex={item.href || item.disabled ? undefined : 0}
109
+ data-value={item.value}
110
+ data-name={item.name}
111
+ data-disabled={item.disabled}
112
+ data-selected={item.selected ? true : undefined}
113
+ data-hidden={(
114
+ !item.value?.toLowerCase().includes(searchValue)
115
+ && !item.subText?.toLowerCase().includes(searchValue)
116
+ && !item.name.toLowerCase().includes(searchValue)
117
+ ) ? true : null}
118
+ on:click={item.disabled ? null : select}
119
+ on:keyup={item.disabled ? null : selectByKey}
120
+ >
121
+ <ConditionalWrapper
122
+ condition={!!item.href}
123
+ element="a"
124
+ href={item.href}
125
+ target={item.target}
126
+ >
127
+ <ConditionalWrapper
128
+ condition={!!(item.icon && item.subText)}
129
+ >
130
+ {#if item.icon}
131
+ {@html item.icon}
132
+ {/if}
133
+ {item.name}
134
+ </ConditionalWrapper>
135
+ {#if item.subText}
136
+ <span>{item.subText}</span>
137
+ {/if}
138
+ </ConditionalWrapper>
139
+ </li>
140
+ {/each}
141
+ {/each}
142
+
143
+ {#if showSearchBar && !numberOfResults}
144
+ <li data-id="w-no-results">{noResultsLabel}</li>
145
+ {/if}
146
+ </ul>
147
+ </ConditionalWrapper>
@@ -0,0 +1,168 @@
1
+ import React, { useState } from 'react'
2
+ import type { ListEventType, ReactListProps } from './list'
3
+
4
+ import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
5
+ import Input from '../Input/Input.tsx'
6
+
7
+ import { classNames } from '../../utils/classNames'
8
+
9
+ import searchIcon from '../../icons/search.svg?raw'
10
+
11
+ import styles from './list.module.scss'
12
+
13
+ const List = ({
14
+ showSearchBar,
15
+ showSearchBarIcon,
16
+ searchBarPlaceholder,
17
+ noResultsLabel = 'No results.',
18
+ maxHeight,
19
+ id,
20
+ className,
21
+ wrapperClassName,
22
+ itemGroups,
23
+ onSelect
24
+ }: ReactListProps) => {
25
+ const [searchValue, setSearchValue] = useState('')
26
+ const [numberOfResults, setNumberOfResults] = useState(1)
27
+ const [itemGroupsWithActive, setItemGroups] = useState(itemGroups)
28
+
29
+ const classes = classNames([
30
+ styles.list,
31
+ !showSearchBar && styles.container,
32
+ className
33
+ ])
34
+
35
+ const wrapperClasses = classNames([
36
+ styles.container,
37
+ wrapperClassName
38
+ ])
39
+
40
+ const style = maxHeight
41
+ ? { maxHeight } as React.CSSProperties
42
+ : undefined
43
+
44
+ const search = (event: React.FormEvent<HTMLInputElement>) => {
45
+ const value = (event.target as HTMLInputElement).value
46
+
47
+ setSearchValue(value)
48
+ setNumberOfResults(
49
+ itemGroups
50
+ .map(group => group.items)
51
+ .flat()
52
+ .filter(item => {
53
+ return item.value?.toLowerCase().includes(value)
54
+ || item.subText?.toLowerCase().includes(value)
55
+ || item.name.toLowerCase().includes(value)
56
+ }).length
57
+ )
58
+ }
59
+
60
+ const select = (event: any) => {
61
+ const li = event.target as HTMLLIElement
62
+
63
+ setItemGroups(
64
+ itemGroupsWithActive.map(group => {
65
+ return {
66
+ ...group,
67
+ items: group.items.map(item => {
68
+ return {
69
+ ...item,
70
+ selected: li.dataset.name === item.name
71
+ }
72
+ })
73
+ }
74
+ })
75
+ )
76
+
77
+ onSelect?.({
78
+ ...li.dataset,
79
+ list: li.parentElement
80
+ } as ListEventType)
81
+ }
82
+
83
+ const selectByKey = (event: any) => {
84
+ if (event.key === 'Enter') {
85
+ select(event)
86
+ }
87
+ }
88
+
89
+ return (
90
+ <ConditionalWrapper condition={!!showSearchBar} wrapper={children => (
91
+ <div className={wrapperClasses}>
92
+ <Input
93
+ type="search"
94
+ placeholder={searchBarPlaceholder}
95
+ onInput={search}
96
+ >
97
+ {showSearchBarIcon && (
98
+ <span
99
+ dangerouslySetInnerHTML={{ __html: searchIcon }}
100
+ style={{ height: '18px', position: 'absolute' }}
101
+ />
102
+ )}
103
+ </Input>
104
+ {children}
105
+ </div>
106
+ )}>
107
+ <ul className={classes} id={id} style={style}>
108
+ {itemGroupsWithActive.map((group: ReactListProps['itemGroups'][0], index) => (
109
+ <React.Fragment key={index}>
110
+ {group.title && (
111
+ <li className={styles.title}
112
+ data-hidden={searchValue ? true : null}
113
+ >
114
+ {group.title}
115
+ </li>
116
+ )}
117
+ {group.items.map((item, index) => (
118
+ <li
119
+ key={index}
120
+ tabIndex={item.href || item.disabled ? undefined : 0}
121
+ data-value={item.value}
122
+ data-name={item.name}
123
+ data-disabled={item.disabled}
124
+ data-selected={item.selected ? true : undefined}
125
+ data-hidden={(
126
+ !item.value?.toLowerCase().includes(searchValue)
127
+ && !item.subText?.toLowerCase().includes(searchValue)
128
+ && !item.name.toLowerCase().includes(searchValue)
129
+ ) ? true : null}
130
+ onClick={item.disabled ? undefined : select}
131
+ onKeyUp={item.disabled ? undefined : selectByKey}
132
+ >
133
+ <ConditionalWrapper condition={!!item.href} wrapper={children => (
134
+ <a
135
+ href={item.href}
136
+ target={item.target}
137
+ >
138
+ {children}
139
+ </a>
140
+ )}>
141
+
142
+ <ConditionalWrapper condition={!!(item.icon && item.subText)} wrapper={children => (
143
+ <div>{children}</div>
144
+ )}>
145
+ {item.icon && (
146
+ <span
147
+ dangerouslySetInnerHTML={{ __html: item.icon }}
148
+ style={{ height: '18px' }}
149
+ />
150
+ )}
151
+ {item.name}
152
+ </ConditionalWrapper>
153
+ {item.subText && <span>{item.subText}</span>}
154
+ </ConditionalWrapper>
155
+ </li>
156
+ ))}
157
+ </React.Fragment>
158
+ ))}
159
+
160
+ {showSearchBar && !numberOfResults && (
161
+ <li data-id="w-no-results">{noResultsLabel}</li>
162
+ )}
163
+ </ul>
164
+ </ConditionalWrapper>
165
+ )
166
+ }
167
+
168
+ export default List
@@ -0,0 +1,91 @@
1
+ @import '../../scss/config.scss';
2
+
3
+ .list {
4
+ @include spacing(0);
5
+
6
+ overflow-y: auto;
7
+ list-style-type: none;
8
+
9
+ &:hover li[data-selected] {
10
+ @include background(transparent);
11
+ }
12
+
13
+ li {
14
+ @include transition(background);
15
+ @include spacing(px-sm, py-xs, m-xs);
16
+ @include layout(flex, sm, v-center);
17
+ @include border-radius(md);
18
+
19
+ &:not(.title, [data-disabled]) {
20
+ cursor: pointer;
21
+ }
22
+
23
+ &:not(.title, [data-disabled]):hover,
24
+ &[data-selected] {
25
+ @include background(primary-40);
26
+ }
27
+
28
+ &[data-disabled],
29
+ &[data-disabled] span {
30
+ @include typography(primary-30);
31
+
32
+ cursor: default;
33
+ }
34
+
35
+ &[data-hidden] {
36
+ @include visibility(none);
37
+ }
38
+
39
+ &[data-id="w-no-results"] {
40
+ @include layout(h-center);
41
+ cursor: default;
42
+
43
+ &:hover {
44
+ @include background(transparent);
45
+ }
46
+ }
47
+
48
+ &.title {
49
+ @include typography(md, primary-20);
50
+ @include spacing(m0);
51
+
52
+ padding-bottom: 0;
53
+
54
+ &:not(:first-child) {
55
+ @include border(top, primary-50);
56
+ }
57
+ }
58
+
59
+ svg {
60
+ @include size(18px);
61
+ pointer-events: none;
62
+ }
63
+
64
+ a,
65
+ div {
66
+ @include layout(flex, sm, v-center);
67
+ @include typography(none);
68
+ flex: 1;
69
+ }
70
+
71
+ div {
72
+ pointer-events: none;
73
+ }
74
+
75
+ span {
76
+ @include typography(md, primary-20);
77
+ pointer-events: none;
78
+ }
79
+ }
80
+ }
81
+
82
+ .container {
83
+ @include border-radius(md);
84
+ @include border(primary-50);
85
+
86
+ input {
87
+ @include border(top, 0);
88
+ @include border(left, 0);
89
+ @include border(right, 0);
90
+ }
91
+ }
@@ -0,0 +1,37 @@
1
+ export type ListEventType = {
2
+ value: string
3
+ name: string
4
+ list: HTMLUListElement
5
+ }
6
+
7
+ export type ListProps = {
8
+ showSearchBar?: boolean
9
+ showSearchBarIcon?: boolean
10
+ searchBarPlaceholder?: string
11
+ noResultsLabel?: string
12
+ maxHeight?: string
13
+ id?: string
14
+ className?: string
15
+ wrapperClassName?: string
16
+ itemGroups: {
17
+ title?: string
18
+ items: {
19
+ name: string
20
+ value?: string
21
+ href?: string
22
+ target?: '_self' | '_blank' | '_parent' | '_top' | '_unfencedTop'
23
+ selected?: boolean
24
+ disabled?: boolean
25
+ icon?: string
26
+ subText?: string
27
+ }[]
28
+ }[]
29
+ }
30
+
31
+ export type SvelteListProps = {
32
+ onSelect?: (event: ListEventType) => void
33
+ } & ListProps
34
+
35
+ export type ReactListProps = {
36
+ onSelect?: (event: ListEventType) => void
37
+ } & ListProps
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import type { MenuProps } from './menu'
3
+
3
4
  import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
4
5
 
5
6
  import styles from './menu.module.scss'
@@ -107,5 +108,5 @@ const wrapMenu = (logo?.url || logo?.html) && items?.length && Astro.slots.has('
107
108
  header.dataset.active = header.dataset.active === 'true'
108
109
  ? 'false'
109
110
  : 'true'
110
- })
111
+ })
111
112
  </script>
@@ -1,10 +1,12 @@
1
1
  <script lang="ts">
2
2
  import type { MenuProps } from './menu'
3
+
3
4
  import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
4
5
 
5
- import styles from './menu.module.scss'
6
6
  import { classNames } from '../../utils/classNames'
7
7
 
8
+ import styles from './menu.module.scss'
9
+
8
10
  export let items: MenuProps['items'] = []
9
11
  export let logo: MenuProps['logo'] = null
10
12
  export let centerLogo: MenuProps['centerLogo'] = false
@@ -47,7 +49,7 @@
47
49
  {@html logo.html}
48
50
  </a>
49
51
  {/if}
50
-
52
+
51
53
  {#if items?.length}
52
54
  <ul>
53
55
  {#each items as item}
@@ -64,7 +66,7 @@
64
66
  </ul>
65
67
  {/if}
66
68
  </ConditionalWrapper>
67
-
69
+
68
70
  {#if items?.length}
69
71
  <button class={styles.hamburger} on:click={toggleMenu}>
70
72
  <span class={styles.meat}></span>
@@ -73,7 +75,7 @@
73
75
  <span class={styles.meat}></span>
74
76
  </button>
75
77
  {/if}
76
-
78
+
77
79
  {#if centerLogo && logo?.html}
78
80
  <a href="/" aria-label={logo.alt || 'Logo'}>
79
81
  {@html logo.html}
@@ -90,7 +92,7 @@
90
92
  />
91
93
  </a>
92
94
  {/if}
93
-
95
+
94
96
  <slot />
95
97
  </div>
96
98
  </header>