Skip to content

Commit 7938b15

Browse files
committed
feat(button-groups): adds button groups
1 parent 6a33674 commit 7938b15

File tree

17 files changed

+625
-118
lines changed

17 files changed

+625
-118
lines changed

README.md

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@
22
Neomorphic ui library for svelte 5
33

44
## TODO
5-
- buttons
6-
- toggle
7-
- skeleton
8-
- border loading
9-
- tags
10-
- badge
5+
- navbar
6+
- card
7+
- border loading
8+
- skeleton
9+
- images
10+
- videos
11+
- carousel
1112
- avatar
13+
- badge
14+
- tags
1215
- badge
13-
- button groups
14-
- card
15-
- border loading
16-
- skeleton
17-
- images
18-
- videos
19-
- carousel
20-
- navbar
2116
- switch
2217
- loading
2318
- border loading
2419
- skeleton
2520
- radio
2621
- checkbox
27-
- tooltip
28-
- popconfirm
29-
- popselect
3022
- Inputs
3123
- validation
3224
- loading
@@ -46,12 +38,15 @@ Neomorphic ui library for svelte 5
4638
- time/date
4739
- floating label
4840

49-
5041
- Progress/Loading
5142
- bar
5243
- circle
5344
- border
5445
- background
46+
- tooltip
47+
- popconfirm
48+
- popselect
49+
5550
- Alerts
5651
- notification
5752
- simple message

demo/App.svelte

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,84 @@
22
import '~/styles/reset.scss';
33
import '~/styles/theme.scss';
44
5-
import DemoButtons from './components/DemoButtons.svelte';
5+
import { wait } from '@dvcol/common-utils/common/promise';
6+
7+
import { RouterView } from '@dvcol/svelte-simple-router/components';
8+
import { fade } from 'svelte/transition';
9+
10+
import { router } from './router/router.js';
11+
12+
import { Route } from './router/routes';
13+
14+
import type { TransitionProps } from '@dvcol/svelte-simple-router/models';
615
716
import NeoButton from '~/buttons/NeoButton.svelte';
17+
import NeoButtonGroup from '~/buttons/NeoButtonGroup.svelte';
818
import IconMoon from '~/icons/IconMoon.svelte';
919
import IconSun from '~/icons/IconSun.svelte';
1020
11-
let transitionIn = $state(false);
12-
let transitionOut = $state(false);
13-
const onTransition = () => {
14-
transitionOut = true;
15-
setTimeout(() => {
16-
transitionOut = false;
17-
transitionIn = true;
18-
19-
setTimeout(() => {
20-
transitionIn = false;
21-
}, 750);
22-
}, 750);
21+
const transition: TransitionProps = {
22+
in: fade,
23+
out: fade,
24+
params: { in: { delay: 200, duration: 200 }, out: { duration: 200 } },
25+
skipFirst: true,
26+
};
27+
28+
const active = $derived(router.route?.name);
29+
let transitioning = $state(false);
30+
31+
const onChange = async () => {
32+
transitioning = true;
33+
await wait(150);
34+
};
35+
36+
const onLoaded = async () => {
37+
await wait(350);
38+
transitioning = false;
2339
};
2440
25-
let dark = $state(window.matchMedia('(prefers-color-scheme: dark)').matches);
41+
const initial = localStorage.getItem('theme');
42+
let dark = $state(initial ? initial === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches);
43+
let remember = $state(!!localStorage.getItem('theme'));
2644
2745
$effect(() => {
28-
if (dark) {
29-
document.documentElement.setAttribute('theme', 'dark');
30-
} else {
31-
document.documentElement.setAttribute('theme', 'light');
32-
}
46+
const theme = dark ? 'dark' : 'light';
47+
document.documentElement.setAttribute('theme', theme);
48+
if (remember) localStorage.setItem('theme', dark ? 'dark' : 'light');
49+
else localStorage.removeItem('theme');
3350
});
51+
52+
const routes = [Route.Buttons, Route.ButtonGroups];
3453
</script>
3554

3655
<div class="container">
3756
<div class="row">
38-
<NeoButton onclick={onTransition}>transition</NeoButton>
39-
<NeoButton toggle bind:checked={dark}>
40-
{#snippet icon()}
41-
{#if dark}
42-
<IconMoon />
43-
{:else}
44-
<IconSun />
45-
{/if}
46-
{/snippet}
47-
<span>{dark ? 'Dark' : 'Light'} Theme</span>
48-
</NeoButton>
57+
<NeoButtonGroup>
58+
{#each routes as route}
59+
<NeoButton checked={active === route} onclick={() => router.push({ name: route })}>
60+
{route}
61+
</NeoButton>
62+
{/each}
63+
</NeoButtonGroup>
64+
65+
<NeoButtonGroup>
66+
<NeoButton toggle bind:checked={dark}>
67+
{#snippet icon()}
68+
{#if dark}
69+
<IconMoon />
70+
{:else}
71+
<IconSun />
72+
{/if}
73+
{/snippet}
74+
<span>{dark ? 'Dark' : 'Light'} Theme</span>
75+
</NeoButton>
76+
<NeoButton toggle bind:checked={remember}>Remember</NeoButton>
77+
</NeoButtonGroup>
4978
</div>
5079

51-
<div class="row" class:transition-in={transitionIn} class:transition-out={transitionOut}>
52-
<DemoButtons />
53-
</div>
80+
<main class="row view" class:transition={transitioning}>
81+
<RouterView {router} {transition} {onChange} {onLoaded} />
82+
</main>
5483
</div>
5584

5685
<style lang="scss">
@@ -61,26 +90,19 @@
6190
@include flex.row($center: true, $gap: var(--gap-xl));
6291
}
6392
93+
.view {
94+
min-height: 70vh;
95+
}
96+
6497
.container {
6598
@include flex.column($gap: var(--gap-xl));
6699
67100
padding: 1rem;
68101
69-
:global(.transition-out *),
70-
:global(.transition-out *::before),
71-
:global(.transition-out *::after) {
102+
:global(.transition *),
103+
:global(.transition *::before),
104+
:global(.transition *::after) {
72105
box-shadow: var(--box-shadow-flat) !important;
73-
transition:
74-
all 0.5s ease,
75-
box-shadow 0.5s ease-in-out;
76-
}
77-
78-
:global(.transition-in *),
79-
:global(.transition-in *::before),
80-
:global(.transition-in *::after) {
81-
transition:
82-
all 0.5s ease,
83-
box-shadow 0.5s ease-in-out;
84106
}
85107
86108
:global(.rotate) {
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<script lang="ts">
2+
import { useWatchMedia } from '@dvcol/svelte-utils/media';
3+
4+
import SphereBackdrop from '../utils/SphereBackdrop.svelte';
5+
6+
import NeoButton from '~/buttons/NeoButton.svelte';
7+
import NeoButtonGroup from '~/buttons/NeoButtonGroup.svelte';
8+
import IconAccount from '~/icons/IconAccount.svelte';
9+
10+
const onClick = (...args: any) => {
11+
console.info(...args);
12+
};
13+
14+
let loading = $state(false);
15+
const onLoading = (e: MouseEvent, checked?: boolean, duration = 5000) => {
16+
loading = !loading;
17+
setTimeout(() => {
18+
loading = !loading;
19+
}, duration);
20+
onClick(e);
21+
};
22+
23+
let skeleton = $state(false);
24+
const onSkeleton = (e: MouseEvent, checked?: boolean, duration = 5000) => {
25+
skeleton = !skeleton;
26+
setTimeout(() => {
27+
skeleton = !skeleton;
28+
}, duration);
29+
onClick(e);
30+
};
31+
32+
const { matches } = useWatchMedia('(max-width: 1550px)');
33+
const vertical = $derived.by(matches);
34+
</script>
35+
36+
{#snippet icon()}
37+
<IconAccount />
38+
{/snippet}
39+
40+
<div class="row">
41+
<div class="column">
42+
<span class="label">Default</span>
43+
<NeoButtonGroup {vertical} {skeleton}>
44+
<NeoButton onclick={onClick}>Button</NeoButton>
45+
<NeoButton toggle onclick={onClick}>Toggle</NeoButton>
46+
<NeoButton disabled onclick={onClick}>Disabled</NeoButton>
47+
<NeoButton {loading} onclick={onLoading}>Loading</NeoButton>
48+
<NeoButton {loading} onclick={onLoading} {icon} />
49+
<NeoButton onclick={onClick} {icon}>Icon</NeoButton>
50+
<NeoButton reverse onclick={onClick} {icon}>Reversed</NeoButton>
51+
<NeoButton {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
52+
</NeoButtonGroup>
53+
</div>
54+
55+
<div class="column">
56+
<span class="label">Rounded</span>
57+
<NeoButtonGroup {vertical} {skeleton} rounded>
58+
<NeoButton onclick={onClick}>Button</NeoButton>
59+
<NeoButton toggle onclick={onClick}>Toggle</NeoButton>
60+
<NeoButton disabled onclick={onClick}>Disabled</NeoButton>
61+
<NeoButton {loading} onclick={onLoading}>Loading</NeoButton>
62+
<NeoButton {loading} onclick={onLoading} {icon} />
63+
<NeoButton onclick={onClick} {icon}>Icon</NeoButton>
64+
<NeoButton reverse onclick={onClick} {icon}>Reversed</NeoButton>
65+
<NeoButton {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
66+
</NeoButtonGroup>
67+
</div>
68+
69+
<div class="column">
70+
<span class="label">Flat</span>
71+
<NeoButtonGroup {vertical} {skeleton} flat>
72+
<NeoButton flat onclick={onClick}>Button</NeoButton>
73+
<NeoButton flat toggle onclick={onClick}>Toggle</NeoButton>
74+
<NeoButton flat disabled onclick={onClick}>Disabled</NeoButton>
75+
<NeoButton flat {loading} onclick={onLoading}>Loading</NeoButton>
76+
<NeoButton flat {loading} onclick={onLoading} {icon} />
77+
<NeoButton flat onclick={onClick} {icon}>Icon</NeoButton>
78+
<NeoButton flat reverse onclick={onClick} {icon}>Reversed</NeoButton>
79+
<NeoButton flat {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
80+
</NeoButtonGroup>
81+
</div>
82+
83+
<div class="column">
84+
<span class="label">Text</span>
85+
<NeoButtonGroup {vertical} {skeleton} text>
86+
<NeoButton text onclick={onClick}>Button</NeoButton>
87+
<NeoButton text toggle onclick={onClick}>Toggle</NeoButton>
88+
<NeoButton text disabled onclick={onClick}>Disabled</NeoButton>
89+
<NeoButton text {loading} onclick={onLoading}>Loading</NeoButton>
90+
<NeoButton text {loading} onclick={onLoading} {icon} />
91+
<NeoButton text onclick={onClick} {icon}>Icon</NeoButton>
92+
<NeoButton text reverse onclick={onClick} {icon}>Reversed</NeoButton>
93+
<NeoButton text {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
94+
</NeoButtonGroup>
95+
</div>
96+
97+
<div class="column">
98+
<span class="label">Glass</span>
99+
<SphereBackdrop>
100+
<NeoButtonGroup {vertical} {skeleton} glass>
101+
<NeoButton onclick={onClick}>Button</NeoButton>
102+
<NeoButton toggle onclick={onClick}>Toggle</NeoButton>
103+
<NeoButton disabled onclick={onClick}>Disabled</NeoButton>
104+
<NeoButton {loading} onclick={onLoading}>Loading</NeoButton>
105+
<NeoButton {loading} onclick={onLoading} {icon} />
106+
<NeoButton onclick={onClick} {icon}>Icon</NeoButton>
107+
<NeoButton reverse onclick={onClick} {icon}>Reversed</NeoButton>
108+
<NeoButton {skeleton} onclick={onSkeleton}>Skeleton</NeoButton>
109+
</NeoButtonGroup>
110+
</SphereBackdrop>
111+
</div>
112+
113+
<div class="column">
114+
<span class="label">Pulse</span>
115+
<NeoButtonGroup {skeleton} pulse>
116+
<NeoButton onclick={onClick}>Button</NeoButton>
117+
<NeoButton toggle onclick={onClick}>Toggle</NeoButton>
118+
<NeoButton {loading} onclick={onLoading} {icon} />
119+
</NeoButtonGroup>
120+
121+
<span class="label">Coalesce</span>
122+
<NeoButtonGroup {skeleton} coalesce>
123+
<NeoButton onclick={onClick}>Button</NeoButton>
124+
<NeoButton disabled onclick={onClick}>Disabled</NeoButton>
125+
<NeoButton {loading} onclick={onLoading}>Loading</NeoButton>
126+
</NeoButtonGroup>
127+
</div>
128+
</div>
129+
130+
<style lang="scss">
131+
@use 'src/lib/styles/common/flex' as flex;
132+
133+
.column {
134+
@include flex.column($center: true, $gap: var(--gap-lg));
135+
}
136+
137+
.row {
138+
@include flex.row($gap: var(--gap-xl));
139+
}
140+
141+
@media (width > 1550px) {
142+
.column {
143+
@include flex.row($gap: var(--gap-xxl));
144+
}
145+
146+
.row {
147+
@include flex.column($center: true, $gap: var(--gap-xl));
148+
}
149+
150+
.label {
151+
min-width: 4.5rem;
152+
}
153+
}
154+
</style>

demo/components/DemoButtons.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@
127127
@use 'src/lib/styles/common/flex' as flex;
128128
129129
.column {
130-
@include flex.column($center: true, $gap: var(--gap-md));
130+
@include flex.column($center: true, $gap: var(--gap-lg));
131131
}
132132
133133
.row {
134-
@include flex.row;
134+
@include flex.row($gap: var(--gap-xl));
135135
}
136136
137137
@media (width > 1550px) {
@@ -140,7 +140,7 @@
140140
}
141141
142142
.row {
143-
@include flex.column($center: true, $gap: var(--gap-lg));
143+
@include flex.column($center: true, $gap: var(--gap-xl));
144144
}
145145
146146
.label {

demo/router/router.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Router } from '@dvcol/svelte-simple-router';
2+
3+
import { options } from './routes.js';
4+
5+
export const router = new Router(options);

0 commit comments

Comments
 (0)