webcoreui 0.0.11 → 0.1.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.
- package/README.md +42 -11
- package/astro.d.ts +4 -0
- package/astro.js +4 -0
- package/components/Accordion/Accordion.astro +10 -8
- package/components/Accordion/Accordion.svelte +8 -8
- package/components/Accordion/Accordion.tsx +7 -6
- package/components/Accordion/accordion.module.scss +65 -0
- package/components/Alert/Alert.astro +11 -7
- package/components/Alert/Alert.svelte +9 -7
- package/components/Alert/Alert.tsx +11 -7
- package/components/Alert/alert.module.scss +51 -0
- package/components/Alert/alert.ts +3 -1
- package/components/Avatar/Avatar.astro +4 -4
- package/components/Avatar/Avatar.svelte +13 -7
- package/components/Avatar/Avatar.tsx +11 -9
- package/components/Avatar/{avatar.scss → avatar.module.scss} +11 -8
- package/components/Badge/Badge.astro +8 -8
- package/components/Badge/Badge.svelte +25 -13
- package/components/Badge/Badge.tsx +26 -8
- package/components/Badge/badge.module.scss +86 -0
- package/components/Badge/badge.ts +8 -1
- package/components/Button/Button.astro +9 -7
- package/components/Button/Button.svelte +13 -11
- package/components/Button/Button.tsx +10 -8
- package/components/Button/button.module.scss +87 -0
- package/components/Button/button.ts +6 -2
- package/components/Card/Card.astro +7 -15
- package/components/Card/Card.svelte +16 -13
- package/components/Card/Card.tsx +15 -11
- package/components/Card/card.module.scss +38 -0
- package/components/Checkbox/Checkbox.astro +10 -10
- package/components/Checkbox/Checkbox.svelte +19 -18
- package/components/Checkbox/Checkbox.tsx +12 -11
- package/components/Checkbox/checkbox.module.scss +70 -0
- package/components/Checkbox/checkbox.ts +5 -1
- package/components/ConditionalWrapper/ConditionalWrapper.svelte +1 -1
- package/components/Icon/Icon.astro +2 -2
- package/components/Icon/Icon.svelte +1 -8
- package/components/Icon/Icon.tsx +1 -8
- package/components/Icon/map.ts +23 -0
- package/components/Input/Input.astro +50 -52
- package/components/Input/Input.svelte +52 -52
- package/components/Input/Input.tsx +59 -59
- package/components/Input/input.module.scss +79 -0
- package/components/Input/input.ts +8 -2
- package/components/Menu/Menu.astro +107 -0
- package/components/Menu/Menu.svelte +88 -0
- package/components/Menu/Menu.tsx +107 -0
- package/components/Menu/menu.module.scss +141 -0
- package/components/Menu/menu.ts +21 -0
- package/components/Progress/Progress.astro +42 -40
- package/components/Progress/Progress.svelte +40 -38
- package/components/Progress/Progress.tsx +48 -47
- package/components/Progress/progress.module.scss +70 -0
- package/components/Radio/Radio.astro +57 -57
- package/components/Radio/Radio.svelte +58 -56
- package/components/Radio/Radio.tsx +69 -68
- package/components/Radio/radio.module.scss +84 -0
- package/components/Radio/radio.ts +4 -0
- package/components/Rating/Rating.astro +13 -10
- package/components/Rating/Rating.svelte +15 -15
- package/components/Rating/Rating.tsx +22 -11
- package/components/Rating/rating.module.scss +45 -0
- package/components/Rating/rating.ts +1 -1
- package/components/Spinner/Spinner.astro +44 -42
- package/components/Spinner/Spinner.svelte +40 -38
- package/components/Spinner/Spinner.tsx +45 -44
- package/components/Spinner/{spinner.scss → spinner.module.scss} +50 -41
- package/components/Switch/Switch.astro +13 -11
- package/components/Switch/Switch.svelte +26 -24
- package/components/Switch/Switch.tsx +14 -12
- package/components/Switch/switch.module.scss +82 -0
- package/components/Switch/switch.ts +8 -4
- package/components/Table/Table.astro +60 -60
- package/components/Table/Table.svelte +56 -54
- package/components/Table/Table.tsx +64 -63
- package/components/Table/{table.scss → table.module.scss} +60 -65
- package/components/Tabs/Tabs.astro +76 -76
- package/components/Tabs/Tabs.svelte +56 -54
- package/components/Tabs/Tabs.tsx +70 -69
- package/components/Tabs/tabs.module.scss +125 -0
- package/components/ThemeSwitcher/ThemeSwitcher.astro +106 -0
- package/components/ThemeSwitcher/ThemeSwitcher.svelte +76 -0
- package/components/ThemeSwitcher/ThemeSwitcher.tsx +89 -0
- package/components/ThemeSwitcher/themeswitcher.module.scss +76 -0
- package/components/ThemeSwitcher/themeswitcher.ts +13 -0
- package/components/Timeline/Timeline.astro +36 -34
- package/components/Timeline/Timeline.svelte +32 -30
- package/components/Timeline/Timeline.tsx +38 -37
- package/components/Timeline/{timeline.scss → timeline.module.scss} +76 -71
- package/components/TimelineItem/TimelineItem.astro +27 -26
- package/components/TimelineItem/TimelineItem.svelte +24 -22
- package/components/TimelineItem/TimelineItem.tsx +33 -32
- package/components/TimelineItem/timelineitem.module.scss +29 -0
- package/components/Toast/Toast.astro +41 -30
- package/components/Toast/Toast.svelte +32 -21
- package/components/Toast/Toast.tsx +38 -28
- package/components/Toast/toast.module.scss +40 -0
- package/components/Toast/toast.ts +8 -6
- package/icons/moon.svg +3 -0
- package/icons/sun.svg +3 -0
- package/icons.d.ts +11 -0
- package/icons.js +9 -0
- package/index.js +3 -0
- package/package.json +5 -1
- package/react.d.ts +4 -0
- package/react.js +4 -0
- package/scss/config/color-palette.scss +23 -0
- package/scss/config/css-values.scss +44 -0
- package/scss/config/layout.scss +41 -0
- package/scss/config/mixins.scss +395 -9
- package/scss/config/typography.scss +66 -0
- package/scss/config.scss +6 -1
- package/scss/global/elements.scss +22 -2
- package/scss/global/scrollbar.scss +24 -0
- package/scss/global/theme.scss +140 -0
- package/scss/global/tooltip.scss +47 -39
- package/scss/global/utility.scss +33 -4
- package/scss/global.scss +2 -1
- package/scss/resets.scss +64 -11
- package/scss/setup.scss +25 -31
- package/svelte.d.ts +16 -12
- package/svelte.js +4 -0
- package/utils/classNames.ts +4 -0
- package/utils/cookies.ts +28 -0
- package/utils/event.ts +17 -0
- package/utils/toast.ts +15 -11
- package/components/Accordion/accordion.scss +0 -63
- package/components/Alert/alert.scss +0 -53
- package/components/Badge/badge.scss +0 -85
- package/components/Button/button.scss +0 -91
- package/components/Card/card.scss +0 -37
- package/components/Checkbox/checkbox.scss +0 -85
- package/components/Input/input.scss +0 -83
- package/components/Progress/progress.scss +0 -66
- package/components/Radio/radio.scss +0 -92
- package/components/Rating/rating.scss +0 -37
- package/components/Switch/switch.scss +0 -84
- package/components/Tabs/tabs.scss +0 -134
- package/components/TimelineItem/timelineitem.scss +0 -31
- package/components/Toast/toast.scss +0 -43
package/components/Tabs/Tabs.tsx
CHANGED
|
@@ -1,69 +1,70 @@
|
|
|
1
|
-
import React, { useState, useRef } from 'react'
|
|
2
|
-
import type { ReactTabsProps } from './tabs'
|
|
3
|
-
|
|
4
|
-
import './tabs.scss'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1
|
+
import React, { useState, useRef } from 'react'
|
|
2
|
+
import type { ReactTabsProps } from './tabs'
|
|
3
|
+
|
|
4
|
+
import styles from './tabs.module.scss'
|
|
5
|
+
import { classNames } from '../../utils/classNames'
|
|
6
|
+
|
|
7
|
+
const Tabs = ({
|
|
8
|
+
items,
|
|
9
|
+
theme,
|
|
10
|
+
vertical,
|
|
11
|
+
even,
|
|
12
|
+
className,
|
|
13
|
+
children
|
|
14
|
+
}: ReactTabsProps) => {
|
|
15
|
+
const tabContainer = useRef<HTMLDivElement>(null)
|
|
16
|
+
const [active, setActive] = useState('')
|
|
17
|
+
|
|
18
|
+
const classes = classNames([
|
|
19
|
+
styles.tabs,
|
|
20
|
+
theme && styles[theme],
|
|
21
|
+
vertical && styles.vertical,
|
|
22
|
+
even && styles.even,
|
|
23
|
+
className
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
const setTab = (tab: string) => {
|
|
27
|
+
const tabs = tabContainer.current!.querySelectorAll('[data-tab]')
|
|
28
|
+
|
|
29
|
+
Array.from(tabs).forEach((item: any) => {
|
|
30
|
+
item.dataset.active = false
|
|
31
|
+
|
|
32
|
+
if (item.dataset.tab === tab) {
|
|
33
|
+
item.dataset.active = true
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
setActive(tab)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const isActive = (item: ReactTabsProps['items'][0]) => {
|
|
41
|
+
if (!active) {
|
|
42
|
+
return item.active ? 'true' : undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return active === item.value ? 'true' : undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<section className={classes}>
|
|
50
|
+
<div className={styles.wrapper}>
|
|
51
|
+
<div className={styles.items}>
|
|
52
|
+
{items.map((item, index) => (
|
|
53
|
+
<button
|
|
54
|
+
key={index}
|
|
55
|
+
disabled={item.disabled}
|
|
56
|
+
dangerouslySetInnerHTML={{ __html: item.label }}
|
|
57
|
+
onClick={() => setTab(item.value)}
|
|
58
|
+
data-active={isActive(item)}
|
|
59
|
+
/>
|
|
60
|
+
))}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<div className={styles.content} ref={tabContainer}>
|
|
64
|
+
{children}
|
|
65
|
+
</div>
|
|
66
|
+
</section>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default Tabs
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
@use '../../scss/config.scss' as *;
|
|
2
|
+
|
|
3
|
+
.tabs {
|
|
4
|
+
&.boxed .items,
|
|
5
|
+
&.outline .items {
|
|
6
|
+
@include background(primary-50);
|
|
7
|
+
@include border-radius(md);
|
|
8
|
+
@include spacing(p-xs);
|
|
9
|
+
@include size(wmax-content);
|
|
10
|
+
|
|
11
|
+
button {
|
|
12
|
+
@include transition();
|
|
13
|
+
@include border-radius(md);
|
|
14
|
+
@include spacing(p-sm);
|
|
15
|
+
|
|
16
|
+
&[data-active="true"] {
|
|
17
|
+
@include border(0);
|
|
18
|
+
@include background(primary-70);
|
|
19
|
+
padding-bottom: 10px;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&.outline .items {
|
|
25
|
+
@include background(transparent);
|
|
26
|
+
@include border(primary-50);
|
|
27
|
+
|
|
28
|
+
button {
|
|
29
|
+
margin-bottom: 0;
|
|
30
|
+
|
|
31
|
+
&[data-active="true"] {
|
|
32
|
+
@include background(primary-50);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&.even .items button {
|
|
38
|
+
@include layout(h-center);
|
|
39
|
+
flex: 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&.vertical {
|
|
43
|
+
@include layout(flex, default);
|
|
44
|
+
|
|
45
|
+
&.boxed .items button,
|
|
46
|
+
&.outline .items button {
|
|
47
|
+
@include border(bottom, 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.items {
|
|
51
|
+
@include layout(column, xs);
|
|
52
|
+
|
|
53
|
+
button {
|
|
54
|
+
@include spacing(p-sm);
|
|
55
|
+
@include border(2px, bottom, primary-50);
|
|
56
|
+
|
|
57
|
+
&[data-active="true"] {
|
|
58
|
+
padding-bottom: 10px;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.content {
|
|
64
|
+
@include spacing(m0);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.wrapper {
|
|
69
|
+
@include visibility(auto);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.items {
|
|
73
|
+
@include border(2px, bottom, primary-50);
|
|
74
|
+
@include layout(flex, sm);
|
|
75
|
+
|
|
76
|
+
button {
|
|
77
|
+
@include transition(color);
|
|
78
|
+
@include background(transparent);
|
|
79
|
+
@include typography(md, primary-20);
|
|
80
|
+
@include border(0);
|
|
81
|
+
@include spacing(p-md);
|
|
82
|
+
@include layout(flex, v-center, sm);
|
|
83
|
+
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
flex-shrink: 0;
|
|
86
|
+
|
|
87
|
+
svg {
|
|
88
|
+
@include size(20px);
|
|
89
|
+
pointer-events: none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&[disabled] {
|
|
93
|
+
@include typography(primary-30);
|
|
94
|
+
cursor: no-drop;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
&[data-active="true"] {
|
|
98
|
+
@include typography(primary);
|
|
99
|
+
@include border(2px, bottom, primary);
|
|
100
|
+
|
|
101
|
+
padding-bottom: 13px;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.content {
|
|
107
|
+
@include spacing(mt-default);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[data-tab] {
|
|
111
|
+
@include visibility(none);
|
|
112
|
+
|
|
113
|
+
&[data-active="true"] {
|
|
114
|
+
@include visibility(block);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@include media('xs') {
|
|
120
|
+
.tabs {
|
|
121
|
+
&.even .items {
|
|
122
|
+
@include size(wauto);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ThemeSwitcherProps } from './themeswitcher'
|
|
3
|
+
import styles from './themeswitcher.module.scss'
|
|
4
|
+
|
|
5
|
+
interface Props extends ThemeSwitcherProps {}
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
themes,
|
|
9
|
+
toggle,
|
|
10
|
+
size,
|
|
11
|
+
className
|
|
12
|
+
} = Astro.props
|
|
13
|
+
|
|
14
|
+
const classes = [
|
|
15
|
+
styles.switcher,
|
|
16
|
+
toggle && styles.toggle,
|
|
17
|
+
className
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const primaryTheme = themes[Object.keys(themes)[0]]
|
|
21
|
+
const secondaryTheme = themes[Object.keys(themes)[1]]
|
|
22
|
+
const useIcons = Astro.slots.has('primaryIcon') && Astro.slots.has('secondaryIcon')
|
|
23
|
+
|
|
24
|
+
const buttonClasses = [
|
|
25
|
+
styles.switch,
|
|
26
|
+
useIcons && styles.icons
|
|
27
|
+
]
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<div
|
|
31
|
+
class:list={classes}
|
|
32
|
+
style={size ? `--w-theme-switcher-size: ${size}px;` : null}
|
|
33
|
+
data-id="w-theme-switcher"
|
|
34
|
+
data-primary-theme={primaryTheme}
|
|
35
|
+
data-secondary-theme={toggle ? secondaryTheme : undefined}
|
|
36
|
+
>
|
|
37
|
+
{Object.keys(themes as {}).map((theme, index) => (
|
|
38
|
+
<button
|
|
39
|
+
style={!useIcons ? `background:${theme};` : undefined}
|
|
40
|
+
data-theme={themes[theme]}
|
|
41
|
+
class:list={buttonClasses}
|
|
42
|
+
>
|
|
43
|
+
{index === 0 && <slot name="primaryIcon" />}
|
|
44
|
+
{index !== 0 && <slot name="secondaryIcon" />}
|
|
45
|
+
</button>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<script>
|
|
50
|
+
import { setCookie, getCookie } from '../../utils/cookies'
|
|
51
|
+
import { listen, dispatch } from '../../utils/event'
|
|
52
|
+
|
|
53
|
+
const switchers = document.querySelectorAll('[data-id="w-theme-switcher"]')
|
|
54
|
+
|
|
55
|
+
let currentTheme = localStorage.getItem('w-theme')
|
|
56
|
+
|| getCookie('w-theme')
|
|
57
|
+
|| (switchers[0] as HTMLDivElement).dataset.primaryTheme
|
|
58
|
+
|| ''
|
|
59
|
+
|
|
60
|
+
const setActiveButton = (buttons: HTMLButtonElement[]) => {
|
|
61
|
+
buttons.forEach(button => button.dataset.active = 'false')
|
|
62
|
+
|
|
63
|
+
buttons.forEach(button => {
|
|
64
|
+
if (button.dataset.theme === currentTheme) {
|
|
65
|
+
button.dataset.active = 'true'
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Array.from(switchers).forEach(switcher => {
|
|
71
|
+
const buttons = Array.from(switcher.querySelectorAll('button'))
|
|
72
|
+
|
|
73
|
+
setActiveButton(buttons)
|
|
74
|
+
|
|
75
|
+
listen('theme-switched', theme => {
|
|
76
|
+
currentTheme = theme
|
|
77
|
+
|
|
78
|
+
setActiveButton(buttons)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
switcher.addEventListener('click', event => {
|
|
82
|
+
const target = event.target as HTMLButtonElement
|
|
83
|
+
|
|
84
|
+
if (target.nodeName === 'BUTTON') {
|
|
85
|
+
const toggleTheme = (switcher as HTMLDivElement).dataset.secondaryTheme
|
|
86
|
+
|
|
87
|
+
const theme = toggleTheme
|
|
88
|
+
? toggleTheme === target.dataset.theme
|
|
89
|
+
? (target.previousElementSibling as HTMLButtonElement).dataset.theme as string
|
|
90
|
+
: (target.nextElementSibling as HTMLButtonElement).dataset.theme as string
|
|
91
|
+
: target.dataset.theme as string
|
|
92
|
+
|
|
93
|
+
document.body.classList.replace(currentTheme, theme)
|
|
94
|
+
|
|
95
|
+
currentTheme = theme
|
|
96
|
+
|
|
97
|
+
setActiveButton(buttons)
|
|
98
|
+
setCookie('w-theme', currentTheme, 30)
|
|
99
|
+
localStorage.setItem('w-theme', currentTheme)
|
|
100
|
+
|
|
101
|
+
dispatch('theme-switched', currentTheme)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
</script>
|
|
106
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte'
|
|
3
|
+
import type { ThemeSwitcherProps } from './themeswitcher'
|
|
4
|
+
|
|
5
|
+
import { classNames } from '../../utils/classNames'
|
|
6
|
+
import { listen, dispatch } from '../../utils/event'
|
|
7
|
+
import { setCookie, getCookie } from '../../utils/cookies'
|
|
8
|
+
|
|
9
|
+
import styles from './themeswitcher.module.scss'
|
|
10
|
+
|
|
11
|
+
export let themes: ThemeSwitcherProps['themes'] = {}
|
|
12
|
+
export let toggle: ThemeSwitcherProps['toggle'] = false
|
|
13
|
+
export let size: ThemeSwitcherProps['size'] = 20
|
|
14
|
+
export let className: ThemeSwitcherProps['className'] = ''
|
|
15
|
+
|
|
16
|
+
let currentTheme = ''
|
|
17
|
+
let toggled = false
|
|
18
|
+
|
|
19
|
+
const classes = classNames([
|
|
20
|
+
styles.switcher,
|
|
21
|
+
toggle && styles.toggle,
|
|
22
|
+
className
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
const primaryTheme = themes[Object.keys(themes)[0]]
|
|
26
|
+
const secondaryTheme = themes[Object.keys(themes)[1]]
|
|
27
|
+
const useIcons = $$slots.primaryIcon && $$slots.secondaryIcon
|
|
28
|
+
|
|
29
|
+
const setTheme = (theme: string | number) => {
|
|
30
|
+
if (typeof theme === 'number') {
|
|
31
|
+
theme = toggled ? primaryTheme : secondaryTheme
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
document.body.classList.replace(currentTheme, theme)
|
|
35
|
+
|
|
36
|
+
setCookie('w-theme', theme, 30)
|
|
37
|
+
localStorage.setItem('w-theme', theme)
|
|
38
|
+
|
|
39
|
+
dispatch('theme-switched', theme)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onMount(() => {
|
|
43
|
+
currentTheme = localStorage.getItem('w-theme')
|
|
44
|
+
|| getCookie('w-theme')
|
|
45
|
+
|| themes[Object.keys(themes)[0]]
|
|
46
|
+
|
|
47
|
+
if (toggle && currentTheme === secondaryTheme) {
|
|
48
|
+
toggled = true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
listen('theme-switched', theme => {
|
|
52
|
+
currentTheme = theme
|
|
53
|
+
toggled = currentTheme === secondaryTheme
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div
|
|
59
|
+
class={classes}
|
|
60
|
+
style={size ? `--w-theme-switcher-size: ${size}px;` : null}
|
|
61
|
+
>
|
|
62
|
+
{#each Object.keys(themes) as theme, index}
|
|
63
|
+
<button
|
|
64
|
+
style={!useIcons ? `background:${theme};` : undefined}
|
|
65
|
+
data-active={currentTheme === themes[theme]}
|
|
66
|
+
on:click={() => setTheme(toggle ? index : themes[theme])}
|
|
67
|
+
class={classNames([
|
|
68
|
+
styles.switch,
|
|
69
|
+
useIcons && styles.icons
|
|
70
|
+
])}
|
|
71
|
+
>
|
|
72
|
+
{#if index === 0}<slot name="primaryIcon" />{/if}
|
|
73
|
+
{#if index !== 0}<slot name="secondaryIcon" />{/if}
|
|
74
|
+
</button>
|
|
75
|
+
{/each}
|
|
76
|
+
</div>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react'
|
|
2
|
+
import type { ReactThemeSwitcherProps } from './themeswitcher'
|
|
3
|
+
|
|
4
|
+
import { setCookie, getCookie } from '../../utils/cookies'
|
|
5
|
+
import { listen, dispatch } from '../../utils/event'
|
|
6
|
+
import { classNames } from '../../utils/classNames'
|
|
7
|
+
|
|
8
|
+
import styles from './themeswitcher.module.scss'
|
|
9
|
+
|
|
10
|
+
const ThemeSwitcher = ({
|
|
11
|
+
themes,
|
|
12
|
+
toggle,
|
|
13
|
+
size,
|
|
14
|
+
primaryIcon,
|
|
15
|
+
secondaryIcon,
|
|
16
|
+
className
|
|
17
|
+
}: ReactThemeSwitcherProps) => {
|
|
18
|
+
const [currentTheme, setCurrentTheme] = useState('')
|
|
19
|
+
const [toggled, setToggled] = useState(false)
|
|
20
|
+
|
|
21
|
+
const classes = classNames([
|
|
22
|
+
styles.switcher,
|
|
23
|
+
toggle && styles.toggle,
|
|
24
|
+
className
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
const sizeStyles = size
|
|
28
|
+
? { '--w-theme-switcher-size': `${size}px` } as React.CSSProperties
|
|
29
|
+
: undefined
|
|
30
|
+
|
|
31
|
+
const primaryTheme = themes[Object.keys(themes)[0]]
|
|
32
|
+
const secondaryTheme = themes[Object.keys(themes)[1]]
|
|
33
|
+
const useIcons = primaryIcon && secondaryIcon
|
|
34
|
+
|
|
35
|
+
const setTheme = (theme: string | number) => {
|
|
36
|
+
if (typeof theme === 'number') {
|
|
37
|
+
theme = toggled ? primaryTheme : secondaryTheme
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
document.body.classList.replace(currentTheme, theme)
|
|
41
|
+
|
|
42
|
+
setCookie('w-theme', theme, 30)
|
|
43
|
+
localStorage.setItem('w-theme', theme)
|
|
44
|
+
|
|
45
|
+
dispatch('theme-switched', theme)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
setCurrentTheme(
|
|
50
|
+
localStorage.getItem('w-theme')
|
|
51
|
+
|| getCookie('w-theme')
|
|
52
|
+
|| themes[Object.keys(themes)[0]]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if (toggle && currentTheme === secondaryTheme) {
|
|
56
|
+
setToggled(true)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
listen('theme-switched', theme => {
|
|
60
|
+
setCurrentTheme(theme)
|
|
61
|
+
setToggled(theme === secondaryTheme)
|
|
62
|
+
})
|
|
63
|
+
}, [])
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
className={classes}
|
|
68
|
+
style={sizeStyles}
|
|
69
|
+
>
|
|
70
|
+
{Object.keys(themes as {}).map((theme, index) => (
|
|
71
|
+
<button
|
|
72
|
+
key={index}
|
|
73
|
+
data-active={currentTheme === themes[theme]}
|
|
74
|
+
onClick={() => setTheme(toggle ? index : themes[theme])}
|
|
75
|
+
style={!useIcons ? { background: theme } : undefined}
|
|
76
|
+
className={classNames([
|
|
77
|
+
styles.switch,
|
|
78
|
+
useIcons && styles.icons
|
|
79
|
+
])}
|
|
80
|
+
>
|
|
81
|
+
{index === 0 && primaryIcon}
|
|
82
|
+
{index !== 0 && secondaryIcon}
|
|
83
|
+
</button>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default ThemeSwitcher
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
@use '../../scss/config.scss' as *;
|
|
2
|
+
|
|
3
|
+
body {
|
|
4
|
+
--w-theme-switcher-size: 20px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.switcher {
|
|
8
|
+
@include layout(flex, sm);
|
|
9
|
+
|
|
10
|
+
&.toggle {
|
|
11
|
+
@include position(relative);
|
|
12
|
+
width: var(--w-theme-switcher-size);
|
|
13
|
+
height: var(--w-theme-switcher-size);
|
|
14
|
+
|
|
15
|
+
.switch {
|
|
16
|
+
position: absolute;
|
|
17
|
+
|
|
18
|
+
&:first-child {
|
|
19
|
+
@include layer(overlay);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&:nth-child(2) {
|
|
23
|
+
@include layer(fg);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&[data-active="true"] {
|
|
27
|
+
@include layer(popup);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.switch {
|
|
34
|
+
@include border(0);
|
|
35
|
+
@include border-radius(max);
|
|
36
|
+
@include position(relative);
|
|
37
|
+
|
|
38
|
+
width: var(--w-theme-switcher-size);
|
|
39
|
+
height: var(--w-theme-switcher-size);
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
|
|
42
|
+
&.icons {
|
|
43
|
+
@include transition(opacity);
|
|
44
|
+
@include typography(primary);
|
|
45
|
+
@include background(transparent);
|
|
46
|
+
@include spacing(p0);
|
|
47
|
+
@include visibility(0);
|
|
48
|
+
|
|
49
|
+
&[data-active="true"] {
|
|
50
|
+
@include visibility(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
svg, img {
|
|
54
|
+
pointer-events: none;
|
|
55
|
+
width: var(--w-theme-switcher-size);
|
|
56
|
+
height: var(--w-theme-switcher-size);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&:not(.icons)::after {
|
|
61
|
+
@include position(absolute, center);
|
|
62
|
+
@include border(primary);
|
|
63
|
+
@include border-radius(max);
|
|
64
|
+
@include layer(bg);
|
|
65
|
+
@include transition();
|
|
66
|
+
@include size(0);
|
|
67
|
+
|
|
68
|
+
content: '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&[data-active="true"]::after {
|
|
72
|
+
@include layer(default);
|
|
73
|
+
width: calc(var(--w-theme-switcher-size) + 5px);
|
|
74
|
+
height: calc(var(--w-theme-switcher-size) + 5px);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ThemeSwitcherProps = {
|
|
2
|
+
themes: {
|
|
3
|
+
[key: string]: string
|
|
4
|
+
}
|
|
5
|
+
toggle?: boolean
|
|
6
|
+
size?: number
|
|
7
|
+
className?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ReactThemeSwitcherProps = {
|
|
11
|
+
primaryIcon?: React.ReactNode
|
|
12
|
+
secondaryIcon?: React.ReactNode
|
|
13
|
+
} & ThemeSwitcherProps
|