webcoreui 0.3.0 → 0.4.1
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.
- package/README.md +5 -2
- package/astro.d.ts +7 -1
- package/astro.js +6 -0
- package/components/Accordion/Accordion.astro +2 -0
- package/components/Accordion/Accordion.svelte +2 -0
- package/components/Accordion/Accordion.tsx +2 -0
- package/components/Alert/Alert.astro +3 -2
- package/components/Alert/Alert.svelte +4 -3
- package/components/Alert/Alert.tsx +3 -2
- package/components/Avatar/Avatar.astro +2 -1
- package/components/Avatar/Avatar.svelte +2 -1
- package/components/Avatar/Avatar.tsx +3 -3
- package/components/Badge/Badge.astro +1 -0
- package/components/Badge/Badge.svelte +3 -2
- package/components/Badge/Badge.tsx +2 -1
- package/components/Button/Button.astro +4 -5
- package/components/Button/Button.svelte +2 -1
- package/components/Button/Button.tsx +2 -1
- package/components/Button/button.ts +1 -1
- package/components/Card/Card.astro +11 -3
- package/components/Card/Card.svelte +5 -2
- package/components/Card/Card.tsx +5 -2
- package/components/Card/card.ts +1 -0
- package/components/Checkbox/Checkbox.astro +1 -0
- package/components/Checkbox/Checkbox.svelte +7 -5
- package/components/Checkbox/Checkbox.tsx +4 -2
- package/components/Collapsible/Collapsible.astro +2 -1
- package/components/Collapsible/Collapsible.svelte +2 -1
- package/components/Collapsible/Collapsible.tsx +55 -54
- package/components/ConditionalWrapper/ConditionalWrapper.astro +2 -1
- package/components/ConditionalWrapper/ConditionalWrapper.tsx +1 -2
- package/components/Group/Group.astro +22 -0
- package/components/Group/Group.svelte +20 -0
- package/components/Group/Group.tsx +22 -0
- package/components/Group/group.module.scss +43 -0
- package/components/Group/group.ts +8 -0
- package/components/Icon/map.ts +2 -0
- package/components/Input/Input.astro +8 -1
- package/components/Input/Input.svelte +15 -3
- package/components/Input/Input.tsx +10 -3
- package/components/Input/input.module.scss +4 -1
- package/components/Input/input.ts +9 -4
- package/components/List/List.astro +169 -0
- package/components/List/List.svelte +147 -0
- package/components/List/List.tsx +168 -0
- package/components/List/list.module.scss +91 -0
- package/components/List/list.ts +37 -0
- package/components/Menu/Menu.astro +2 -1
- package/components/Menu/Menu.svelte +7 -5
- package/components/Menu/Menu.tsx +116 -113
- package/components/Modal/Modal.astro +6 -4
- package/components/Modal/Modal.svelte +8 -6
- package/components/Modal/Modal.tsx +79 -76
- package/components/Modal/modal.ts +1 -0
- package/components/Popover/Popover.astro +4 -1
- package/components/Popover/Popover.svelte +4 -2
- package/components/Popover/Popover.tsx +55 -27
- package/components/Popover/popover.module.scss +1 -0
- package/components/Popover/popover.ts +2 -0
- package/components/Progress/Progress.astro +2 -1
- package/components/Progress/Progress.svelte +2 -1
- package/components/Progress/Progress.tsx +3 -2
- package/components/Radio/Radio.astro +1 -0
- package/components/Radio/Radio.svelte +4 -2
- package/components/Radio/Radio.tsx +3 -2
- package/components/Rating/Rating.astro +3 -1
- package/components/Rating/Rating.svelte +9 -7
- package/components/Rating/Rating.tsx +4 -2
- package/components/Select/Select.astro +135 -0
- package/components/Select/Select.svelte +122 -0
- package/components/Select/Select.tsx +142 -0
- package/components/Select/select.module.scss +25 -0
- package/components/Select/select.ts +21 -0
- package/components/Sheet/Sheet.astro +2 -1
- package/components/Sheet/Sheet.svelte +2 -1
- package/components/Sheet/Sheet.tsx +33 -32
- package/components/Slider/Slider.astro +2 -1
- package/components/Slider/Slider.svelte +2 -1
- package/components/Slider/Slider.tsx +49 -48
- package/components/Spinner/Spinner.astro +4 -3
- package/components/Spinner/Spinner.svelte +3 -2
- package/components/Spinner/Spinner.tsx +4 -3
- package/components/Switch/Switch.astro +2 -1
- package/components/Switch/Switch.svelte +5 -4
- package/components/Switch/Switch.tsx +2 -2
- package/components/Switch/switch.module.scss +1 -1
- package/components/Table/Table.astro +1 -0
- package/components/Table/Table.svelte +2 -1
- package/components/Table/Table.tsx +2 -1
- package/components/Tabs/Tabs.astro +2 -1
- package/components/Tabs/Tabs.svelte +2 -1
- package/components/Tabs/Tabs.tsx +4 -3
- package/components/Textarea/Textarea.astro +1 -0
- package/components/Textarea/Textarea.svelte +3 -1
- package/components/Textarea/Textarea.tsx +52 -50
- package/components/ThemeSwitcher/ThemeSwitcher.astro +108 -107
- package/components/ThemeSwitcher/ThemeSwitcher.svelte +5 -4
- package/components/ThemeSwitcher/ThemeSwitcher.tsx +91 -90
- package/components/Timeline/Timeline.astro +3 -2
- package/components/Timeline/Timeline.svelte +3 -2
- package/components/Timeline/Timeline.tsx +3 -2
- package/components/TimelineItem/TimelineItem.svelte +2 -1
- package/components/TimelineItem/TimelineItem.tsx +2 -1
- package/components/Toast/Toast.astro +3 -1
- package/components/Toast/Toast.svelte +3 -1
- package/components/Toast/Toast.tsx +3 -1
- package/icons/moon.svg +1 -1
- package/icons/search.svg +3 -0
- package/icons.d.ts +1 -0
- package/icons.js +1 -0
- package/index.d.ts +55 -25
- package/package.json +22 -2
- package/react.d.ts +6 -0
- package/react.js +6 -0
- package/scss/config/mixins.scss +12 -10
- package/scss/config/variables.scss +1 -0
- package/scss/config.scss +1 -0
- package/scss/global/utility.scss +2 -0
- package/svelte.d.ts +7 -1
- package/svelte.js +6 -0
- package/utils/cookies.ts +4 -4
- package/utils/debounce.ts +1 -1
- package/utils/event.ts +2 -2
- package/utils/interpolate.ts +5 -5
- package/utils/modal.ts +90 -27
- package/utils/popover.ts +30 -8
- package/utils/toast.ts +6 -2
|
@@ -1,54 +1,55 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import type { ReactCollapsibleProps } from './collapsible'
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
...(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import type { ReactCollapsibleProps } from './collapsible'
|
|
3
|
+
|
|
4
|
+
import { classNames } from '../../utils/classNames'
|
|
5
|
+
|
|
6
|
+
import styles from './collapsible.module.scss'
|
|
7
|
+
|
|
8
|
+
const Collapsible = ({
|
|
9
|
+
initialHeight,
|
|
10
|
+
maxHeight,
|
|
11
|
+
toggled,
|
|
12
|
+
on,
|
|
13
|
+
off,
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
togglesClassName
|
|
17
|
+
}: ReactCollapsibleProps) => {
|
|
18
|
+
const [toggle, setToggled] = useState(toggled)
|
|
19
|
+
|
|
20
|
+
const classes = classNames([
|
|
21
|
+
styles.collapsible,
|
|
22
|
+
maxHeight && styles.animated,
|
|
23
|
+
className
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
const styleVariables = {
|
|
27
|
+
...(initialHeight && { '--w-collapsible-initial-height': initialHeight }),
|
|
28
|
+
...(maxHeight && { '--w-collapsible-max-height': maxHeight })
|
|
29
|
+
} as React.CSSProperties
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
className={classes}
|
|
34
|
+
data-toggled={toggle ? 'true' : undefined}
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
className={styles.wrapper}
|
|
38
|
+
style={styleVariables}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
</div>
|
|
42
|
+
<div
|
|
43
|
+
onClick={() => setToggled(!toggle)}
|
|
44
|
+
onKeyUp={() => setToggled(!toggle)}
|
|
45
|
+
role="button"
|
|
46
|
+
tabIndex={0}
|
|
47
|
+
className={togglesClassName}
|
|
48
|
+
>
|
|
49
|
+
{toggle ? off : on}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default Collapsible
|
|
@@ -10,7 +10,8 @@ const children = await Astro.slots.render('default')
|
|
|
10
10
|
const wrapped = wrapper?.replace('children', children)
|
|
11
11
|
|
|
12
12
|
if (!Astro.slots.has('wrapper')) {
|
|
13
|
-
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.error('Missing wrapper. Add slot="wrapper" to one of the elements.')
|
|
14
15
|
}
|
|
15
16
|
---
|
|
16
17
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import type { ReactConditionalWrapperProps } from './conditionalwrapper'
|
|
3
2
|
|
|
4
3
|
const ConditionalWrapper = ({ condition, wrapper, children }: ReactConditionalWrapperProps) =>
|
|
5
|
-
|
|
4
|
+
condition ? wrapper(children) : children
|
|
6
5
|
|
|
7
6
|
export default ConditionalWrapper
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { GroupProps } from './group'
|
|
3
|
+
|
|
4
|
+
import styles from './group.module.scss'
|
|
5
|
+
|
|
6
|
+
interface Props extends GroupProps {}
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
withSeparator,
|
|
10
|
+
className
|
|
11
|
+
} = Astro.props
|
|
12
|
+
|
|
13
|
+
const classes = [
|
|
14
|
+
styles.group,
|
|
15
|
+
withSeparator && styles.separator,
|
|
16
|
+
className
|
|
17
|
+
]
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<div class:list={classes}>
|
|
21
|
+
<slot />
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { GroupProps } from './group'
|
|
3
|
+
|
|
4
|
+
import { classNames } from '../../utils/classNames'
|
|
5
|
+
|
|
6
|
+
import styles from './group.module.scss'
|
|
7
|
+
|
|
8
|
+
export let withSeparator: GroupProps['withSeparator'] = false
|
|
9
|
+
export let className: GroupProps['className'] = ''
|
|
10
|
+
|
|
11
|
+
const classes = classNames([
|
|
12
|
+
styles.group,
|
|
13
|
+
withSeparator && styles.separator,
|
|
14
|
+
className
|
|
15
|
+
])
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<div class={classes}>
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { ReactGroupProps } from './group'
|
|
3
|
+
|
|
4
|
+
import { classNames } from '../../utils/classNames'
|
|
5
|
+
|
|
6
|
+
import styles from './group.module.scss'
|
|
7
|
+
|
|
8
|
+
const Group = ({
|
|
9
|
+
withSeparator,
|
|
10
|
+
className,
|
|
11
|
+
children
|
|
12
|
+
}: ReactGroupProps) => {
|
|
13
|
+
const classes = classNames([
|
|
14
|
+
styles.group,
|
|
15
|
+
withSeparator && styles.separator,
|
|
16
|
+
className
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
return <div className={classes}>{children}</div>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default Group
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
@import '../../scss/config.scss';
|
|
2
|
+
|
|
3
|
+
.group {
|
|
4
|
+
@include layout(flex, none);
|
|
5
|
+
@include visibility(auto);
|
|
6
|
+
|
|
7
|
+
button,
|
|
8
|
+
span {
|
|
9
|
+
@include border-radius(none);
|
|
10
|
+
|
|
11
|
+
&:first-child {
|
|
12
|
+
@include border-radius(left, md);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&:last-child {
|
|
16
|
+
@include border-radius(right, md);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&.separator button:not(:last-child),
|
|
21
|
+
&.separator span:not(:last-child) {
|
|
22
|
+
@include border(1px, right, primary-70);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&.outline button
|
|
26
|
+
&.outline span {
|
|
27
|
+
@include border(1px, primary-20);
|
|
28
|
+
box-shadow: none;
|
|
29
|
+
|
|
30
|
+
&:hover {
|
|
31
|
+
@include border(1px, primary);
|
|
32
|
+
box-shadow: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:first-child {
|
|
36
|
+
@include border(right, 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:last-child {
|
|
40
|
+
@include border(left, 0);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/components/Icon/map.ts
CHANGED
|
@@ -6,6 +6,7 @@ import Close from '../../icons/close.svg?raw'
|
|
|
6
6
|
import Github from '../../icons/github.svg?raw'
|
|
7
7
|
import Info from '../../icons/info.svg?raw'
|
|
8
8
|
import Moon from '../../icons/moon.svg?raw'
|
|
9
|
+
import Search from '../../icons/search.svg?raw'
|
|
9
10
|
import Sun from '../../icons/sun.svg?raw'
|
|
10
11
|
import Warning from '../../icons/warning.svg?raw'
|
|
11
12
|
|
|
@@ -18,6 +19,7 @@ const iconMap = {
|
|
|
18
19
|
'github': Github,
|
|
19
20
|
'info': Info,
|
|
20
21
|
'moon': Moon,
|
|
22
|
+
'search': Search,
|
|
21
23
|
'sun': Sun,
|
|
22
24
|
'warning': Warning
|
|
23
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { InputProps } from './input'
|
|
3
|
+
|
|
3
4
|
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
|
|
4
5
|
|
|
5
6
|
import styles from './input.module.scss'
|
|
@@ -12,6 +13,7 @@ const {
|
|
|
12
13
|
label,
|
|
13
14
|
subText,
|
|
14
15
|
className,
|
|
16
|
+
labelClassName,
|
|
15
17
|
...rest
|
|
16
18
|
} = Astro.props
|
|
17
19
|
|
|
@@ -21,11 +23,16 @@ const classes = [
|
|
|
21
23
|
className
|
|
22
24
|
]
|
|
23
25
|
|
|
26
|
+
const labelClasses = [
|
|
27
|
+
styles['input-label'],
|
|
28
|
+
labelClassName
|
|
29
|
+
]
|
|
30
|
+
|
|
24
31
|
const useLabel = !!(label || subText || Astro.slots.has('default'))
|
|
25
32
|
---
|
|
26
33
|
|
|
27
34
|
<ConditionalWrapper condition={useLabel}>
|
|
28
|
-
<label slot="wrapper" class={
|
|
35
|
+
<label slot="wrapper" class:list={labelClasses}>
|
|
29
36
|
{label && (
|
|
30
37
|
<div class={styles.label}>{label}</div>
|
|
31
38
|
)}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SvelteInputProps } from './input'
|
|
3
|
+
|
|
3
4
|
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
|
|
4
5
|
|
|
5
|
-
import styles from './input.module.scss'
|
|
6
6
|
import { classNames } from '../../utils/classNames'
|
|
7
7
|
|
|
8
|
+
import styles from './input.module.scss'
|
|
9
|
+
|
|
8
10
|
export let type: SvelteInputProps['type'] = 'text'
|
|
9
11
|
export let theme: SvelteInputProps['theme'] = null
|
|
10
12
|
export let label: SvelteInputProps['label'] = ''
|
|
11
13
|
export let subText: SvelteInputProps['subText'] = ''
|
|
12
14
|
export let className: SvelteInputProps['className'] = ''
|
|
15
|
+
export let labelClassName: SvelteInputProps['labelClassName'] = ''
|
|
13
16
|
export let onChange: SvelteInputProps['onChange'] = () => {}
|
|
14
17
|
export let onKeyUp: SvelteInputProps['onKeyUp'] = () => {}
|
|
18
|
+
export let onInput: SvelteInputProps['onInput'] = () => {}
|
|
19
|
+
export let onClick: SvelteInputProps['onClick'] = () => {}
|
|
15
20
|
|
|
16
21
|
const classes = classNames([
|
|
17
22
|
styles.input,
|
|
@@ -19,13 +24,18 @@
|
|
|
19
24
|
className
|
|
20
25
|
])
|
|
21
26
|
|
|
27
|
+
const labelClasses = classNames([
|
|
28
|
+
styles['input-label'],
|
|
29
|
+
labelClassName
|
|
30
|
+
])
|
|
31
|
+
|
|
22
32
|
const useLabel = !!(label || subText || $$slots.default)
|
|
23
33
|
</script>
|
|
24
34
|
|
|
25
35
|
<ConditionalWrapper
|
|
26
36
|
condition={useLabel}
|
|
27
37
|
element="label"
|
|
28
|
-
class={
|
|
38
|
+
class={labelClasses}
|
|
29
39
|
>
|
|
30
40
|
{#if label}
|
|
31
41
|
<div class={styles.label}>{label}</div>
|
|
@@ -41,10 +51,12 @@
|
|
|
41
51
|
class={classes}
|
|
42
52
|
on:change={onChange}
|
|
43
53
|
on:keyup={onKeyUp}
|
|
54
|
+
on:input={onInput}
|
|
55
|
+
on:click={onClick}
|
|
44
56
|
{...$$restProps}
|
|
45
57
|
/>
|
|
46
58
|
</ConditionalWrapper>
|
|
47
|
-
{#if
|
|
59
|
+
{#if subText}
|
|
48
60
|
<div class={styles.subtext}>
|
|
49
61
|
{@html subText}
|
|
50
62
|
</div>
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { ReactInputProps } from './input'
|
|
3
|
+
|
|
3
4
|
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
|
|
4
5
|
|
|
5
|
-
import styles from './input.module.scss'
|
|
6
6
|
import { classNames } from '../../utils/classNames'
|
|
7
7
|
|
|
8
|
+
import styles from './input.module.scss'
|
|
9
|
+
|
|
8
10
|
const Input = ({
|
|
9
11
|
type = 'text',
|
|
10
12
|
theme,
|
|
11
13
|
label,
|
|
12
14
|
subText,
|
|
13
|
-
icon,
|
|
14
15
|
value,
|
|
15
16
|
className,
|
|
17
|
+
labelClassName,
|
|
16
18
|
children,
|
|
17
19
|
...rest
|
|
18
20
|
}: ReactInputProps) => {
|
|
@@ -22,11 +24,16 @@ const Input = ({
|
|
|
22
24
|
className
|
|
23
25
|
])
|
|
24
26
|
|
|
27
|
+
const labelClasses = classNames([
|
|
28
|
+
styles['input-label'],
|
|
29
|
+
labelClassName
|
|
30
|
+
])
|
|
31
|
+
|
|
25
32
|
const useLabel = !!(label || subText || children)
|
|
26
33
|
|
|
27
34
|
return (
|
|
28
35
|
<ConditionalWrapper condition={useLabel} wrapper={children => (
|
|
29
|
-
<label className={
|
|
36
|
+
<label className={labelClasses}>
|
|
30
37
|
{children}
|
|
31
38
|
</label>
|
|
32
39
|
)}>
|
|
@@ -35,16 +35,21 @@ export type InputProps = {
|
|
|
35
35
|
autofocus?: boolean
|
|
36
36
|
autocomplete?: 'on' | 'off'
|
|
37
37
|
className?: string
|
|
38
|
+
labelClassName?: string
|
|
38
39
|
[key: string]: any
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export type SvelteInputProps = {
|
|
42
|
-
onChange?: (
|
|
43
|
-
onKeyUp?: (
|
|
43
|
+
onChange?: (event: Event & { currentTarget: HTMLInputElement }) => void
|
|
44
|
+
onKeyUp?: (event: KeyboardEvent & { currentTarget: HTMLInputElement }) => void
|
|
45
|
+
onInput?: (event: any) => void
|
|
46
|
+
onClick?: (event: MouseEvent & { currentTarget: HTMLInputElement }) => void
|
|
44
47
|
} & InputProps
|
|
45
48
|
|
|
46
49
|
export type ReactInputProps = {
|
|
47
|
-
onChange?: (
|
|
48
|
-
onKeyUp?: (
|
|
50
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
51
|
+
onKeyUp?: (event: React.KeyboardEvent<HTMLInputElement>) => void
|
|
52
|
+
onInput?: (event: React.FormEvent<HTMLInputElement>) => void
|
|
53
|
+
onClick?: (event: React.MouseEvent<HTMLInputElement>) => void
|
|
49
54
|
children?: React.ReactNode
|
|
50
55
|
} & InputProps
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ListProps } from './list'
|
|
3
|
+
|
|
4
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
|
|
5
|
+
import Input from '../Input/Input.astro'
|
|
6
|
+
|
|
7
|
+
import searchIcon from '../../icons/search.svg?raw'
|
|
8
|
+
|
|
9
|
+
import styles from './list.module.scss'
|
|
10
|
+
|
|
11
|
+
interface Props extends ListProps {}
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
showSearchBar,
|
|
15
|
+
showSearchBarIcon,
|
|
16
|
+
searchBarPlaceholder,
|
|
17
|
+
noResultsLabel = 'No results.',
|
|
18
|
+
maxHeight,
|
|
19
|
+
id,
|
|
20
|
+
className,
|
|
21
|
+
wrapperClassName,
|
|
22
|
+
itemGroups
|
|
23
|
+
} = Astro.props
|
|
24
|
+
|
|
25
|
+
const classes = [
|
|
26
|
+
styles.list,
|
|
27
|
+
!showSearchBar && styles.container,
|
|
28
|
+
className
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
const wrapperClasses = [
|
|
32
|
+
styles.container,
|
|
33
|
+
wrapperClassName
|
|
34
|
+
]
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<ConditionalWrapper condition={!!showSearchBar}>
|
|
38
|
+
<div slot="wrapper" class:list={wrapperClasses} data-id="w-list-wrapper">
|
|
39
|
+
<Input
|
|
40
|
+
type="search"
|
|
41
|
+
placeholder={searchBarPlaceholder}
|
|
42
|
+
data-id="w-list-search"
|
|
43
|
+
>
|
|
44
|
+
<Fragment
|
|
45
|
+
set:html={searchIcon}
|
|
46
|
+
slot={showSearchBarIcon ? 'default' : null}
|
|
47
|
+
/>
|
|
48
|
+
</Input>
|
|
49
|
+
children
|
|
50
|
+
</div>
|
|
51
|
+
<ul class:list={classes} id={id} data-id="w-list" style={maxHeight && `max-height: ${maxHeight}`}>
|
|
52
|
+
{itemGroups.map((group: ListProps['itemGroups'][0]) => (
|
|
53
|
+
<Fragment>
|
|
54
|
+
{group.title && (
|
|
55
|
+
<li class={styles.title} data-id="w-list-title">
|
|
56
|
+
{group.title}
|
|
57
|
+
</li>
|
|
58
|
+
)}
|
|
59
|
+
{group.items.map(item => (
|
|
60
|
+
<li
|
|
61
|
+
tabindex={item.href || item.disabled ? undefined : 0}
|
|
62
|
+
data-value={item.value}
|
|
63
|
+
data-name={item.name}
|
|
64
|
+
data-disabled={item.disabled ? 'true' : undefined}
|
|
65
|
+
data-selected={item.selected}
|
|
66
|
+
>
|
|
67
|
+
<ConditionalWrapper condition={!!item.href}>
|
|
68
|
+
<a
|
|
69
|
+
slot="wrapper"
|
|
70
|
+
href={item.href}
|
|
71
|
+
target={item.target}
|
|
72
|
+
>
|
|
73
|
+
children
|
|
74
|
+
</a>
|
|
75
|
+
|
|
76
|
+
<ConditionalWrapper condition={!!(item.icon && item.subText)}>
|
|
77
|
+
<div slot="wrapper">children</div>
|
|
78
|
+
{item.icon && <Fragment set:html={item.icon} />}
|
|
79
|
+
{item.name}
|
|
80
|
+
</ConditionalWrapper>
|
|
81
|
+
{item.subText && <span>{item.subText}</span>}
|
|
82
|
+
</ConditionalWrapper>
|
|
83
|
+
</li>
|
|
84
|
+
))}
|
|
85
|
+
</Fragment>
|
|
86
|
+
))}
|
|
87
|
+
{showSearchBar && (
|
|
88
|
+
<li data-id="w-no-results" data-hidden>{noResultsLabel}</li>
|
|
89
|
+
)}
|
|
90
|
+
</ul>
|
|
91
|
+
</ConditionalWrapper>
|
|
92
|
+
|
|
93
|
+
<script>
|
|
94
|
+
import { dispatch } from '../../utils/event'
|
|
95
|
+
|
|
96
|
+
const lists = document.querySelectorAll('[data-id="w-list"]')
|
|
97
|
+
const searchInputs = document.querySelectorAll('[data-id="w-list-search"]')
|
|
98
|
+
|
|
99
|
+
const handleClick = (list: Element, items: Element[], event: Event) => {
|
|
100
|
+
const target = event.target as HTMLElement
|
|
101
|
+
|
|
102
|
+
if (target.dataset.value && !target.dataset.disabled) {
|
|
103
|
+
dispatch('listOnSelect', {
|
|
104
|
+
value: target.dataset.value,
|
|
105
|
+
name: target.dataset.name,
|
|
106
|
+
list
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
items.forEach(item => item.removeAttribute('data-selected'))
|
|
110
|
+
target.dataset.selected = 'true'
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Array.from(lists).forEach(list => {
|
|
115
|
+
const items = Array.from(list.children)
|
|
116
|
+
|
|
117
|
+
list.addEventListener('click', event => handleClick(list, items, event))
|
|
118
|
+
list.addEventListener('keyup', event => {
|
|
119
|
+
if ((event as KeyboardEvent).key === 'Enter') {
|
|
120
|
+
handleClick(list, items, event)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
Array.from(searchInputs).forEach(element => {
|
|
126
|
+
element.addEventListener('input', event => {
|
|
127
|
+
const target = event.target as HTMLInputElement
|
|
128
|
+
const ul = target.closest('[data-id="w-list-wrapper"]')
|
|
129
|
+
?.querySelector('ul') as HTMLUListElement
|
|
130
|
+
|
|
131
|
+
const noResults = ul.querySelector('[data-id="w-no-results"]')
|
|
132
|
+
const items = Array.from(ul.children)
|
|
133
|
+
const value = target.value.toLowerCase()
|
|
134
|
+
|
|
135
|
+
items.forEach(item => {
|
|
136
|
+
const li = item as HTMLLIElement
|
|
137
|
+
const hideItem = (!li.dataset.value?.toLowerCase().includes(value)
|
|
138
|
+
&& !li.innerText.toLowerCase().includes(value))
|
|
139
|
+
|| li.dataset.id === 'w-list-title'
|
|
140
|
+
|| li.dataset.id === 'w-no-results'
|
|
141
|
+
|
|
142
|
+
if (hideItem) {
|
|
143
|
+
li.dataset.hidden = 'true'
|
|
144
|
+
} else if (li.dataset.id !== 'w-no-results') {
|
|
145
|
+
li.removeAttribute('data-hidden')
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const numberOfResults = items.filter(item => {
|
|
150
|
+
const li = item as HTMLLIElement
|
|
151
|
+
return li.dataset.name && !li.dataset.hidden
|
|
152
|
+
}).length
|
|
153
|
+
|
|
154
|
+
if (!numberOfResults) {
|
|
155
|
+
noResults?.removeAttribute('data-hidden')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!value) {
|
|
159
|
+
items.forEach(item => {
|
|
160
|
+
const li = item as HTMLLIElement
|
|
161
|
+
|
|
162
|
+
li.dataset.id === 'w-no-results'
|
|
163
|
+
? li.dataset.hidden = 'true'
|
|
164
|
+
: li.removeAttribute('data-hidden')
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
</script>
|