storyloco 1.0.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/LICENSE +21 -0
- package/README.md +210 -0
- package/package.json +77 -0
- package/packages/heading/README.md +68 -0
- package/packages/heading/src/app.svelte +64 -0
- package/packages/mux/README.md +68 -0
- package/packages/mux/src/app.svelte +576 -0
- package/packages/shared/README.md +58 -0
- package/packages/shared/dist/components/ui/input/index.d.ts +2 -0
- package/packages/shared/dist/components/ui/input/index.js +4 -0
- package/packages/shared/dist/components/ui/input/input.svelte +51 -0
- package/packages/shared/dist/components/ui/input/input.svelte.d.ts +13 -0
- package/packages/shared/dist/components/ui/label/index.d.ts +2 -0
- package/packages/shared/dist/components/ui/label/index.js +4 -0
- package/packages/shared/dist/components/ui/label/label.svelte +16 -0
- package/packages/shared/dist/components/ui/label/label.svelte.d.ts +4 -0
- package/packages/shared/dist/components/ui/skeleton/index.d.ts +2 -0
- package/packages/shared/dist/components/ui/skeleton/index.js +4 -0
- package/packages/shared/dist/components/ui/skeleton/skeleton.svelte +17 -0
- package/packages/shared/dist/components/ui/skeleton/skeleton.svelte.d.ts +5 -0
- package/packages/shared/dist/components/ui/switch/index.d.ts +2 -0
- package/packages/shared/dist/components/ui/switch/index.js +4 -0
- package/packages/shared/dist/components/ui/switch/switch.svelte +29 -0
- package/packages/shared/dist/components/ui/switch/switch.svelte.d.ts +4 -0
- package/packages/shared/dist/global.css +161 -0
- package/packages/shared/dist/index.d.ts +5 -0
- package/packages/shared/dist/index.js +7 -0
- package/packages/shared/dist/setup.d.ts +2 -0
- package/packages/shared/dist/setup.js +25 -0
- package/packages/shared/dist/utils.d.ts +12 -0
- package/packages/shared/dist/utils.js +5 -0
- package/packages/theme/README.md +68 -0
- package/packages/theme/src/app.svelte +114 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Label as LabelPrimitive } from 'bits-ui'
|
|
3
|
+
import { cn } from '../../../utils.js'
|
|
4
|
+
|
|
5
|
+
let { ref = $bindable(null), class: classname, ...rest }: LabelPrimitive.RootProps = $props()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<LabelPrimitive.Root
|
|
9
|
+
bind:ref
|
|
10
|
+
data-slot="label"
|
|
11
|
+
class={cn(
|
|
12
|
+
'flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50',
|
|
13
|
+
classname
|
|
14
|
+
)}
|
|
15
|
+
{...rest}
|
|
16
|
+
/>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef, type WithoutChildren } from '../../../utils.js'
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: classname,
|
|
8
|
+
...rest
|
|
9
|
+
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> = $props()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
bind:this={ref}
|
|
14
|
+
data-slot="skeleton"
|
|
15
|
+
class={cn('bg-muted animate-pulse rounded-md', classname)}
|
|
16
|
+
{...rest}
|
|
17
|
+
></div>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WithElementRef } from '../../../utils.js';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
declare const Skeleton: import("svelte").Component<Omit<WithElementRef<HTMLAttributes<HTMLDivElement>>, "children">, {}, "ref">;
|
|
4
|
+
type Skeleton = ReturnType<typeof Skeleton>;
|
|
5
|
+
export default Skeleton;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Switch as SwitchPrimitive } from 'bits-ui'
|
|
3
|
+
import { cn, type WithoutChildrenOrChild } from '../../../utils.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: classname,
|
|
8
|
+
checked = $bindable(false),
|
|
9
|
+
...rest
|
|
10
|
+
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<SwitchPrimitive.Root
|
|
14
|
+
bind:ref
|
|
15
|
+
bind:checked
|
|
16
|
+
data-slot="switch"
|
|
17
|
+
class={cn(
|
|
18
|
+
'data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 shadow-xs peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
|
|
19
|
+
classname
|
|
20
|
+
)}
|
|
21
|
+
{...rest}
|
|
22
|
+
>
|
|
23
|
+
<SwitchPrimitive.Thumb
|
|
24
|
+
data-slot="switch-thumb"
|
|
25
|
+
class={cn(
|
|
26
|
+
'bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0'
|
|
27
|
+
)}
|
|
28
|
+
/>
|
|
29
|
+
</SwitchPrimitive.Root>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
|
|
3
|
+
@import 'tw-animate-css';
|
|
4
|
+
|
|
5
|
+
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
--white: oklch(1 0 0);
|
|
9
|
+
--radius: 0.5rem;
|
|
10
|
+
--background: oklch(97.015% 0.00011 271.152);
|
|
11
|
+
--foreground: oklch(0.129 0.042 264.695);
|
|
12
|
+
--card: oklch(1 0 0);
|
|
13
|
+
--card-foreground: oklch(0.129 0.042 264.695);
|
|
14
|
+
--popover: oklch(1 0 0);
|
|
15
|
+
--popover-foreground: oklch(0.129 0.042 264.695);
|
|
16
|
+
--primary: oklch(54.32% 0.09196 193.854);
|
|
17
|
+
--primary-foreground: oklch(0.984 0.003 247.858);
|
|
18
|
+
--secondary: oklch(0.968 0.007 247.896);
|
|
19
|
+
--secondary-foreground: oklch(0.208 0.042 265.755);
|
|
20
|
+
--muted: oklch(0.968 0.007 247.896);
|
|
21
|
+
--muted-foreground: oklch(0.554 0.046 257.417);
|
|
22
|
+
--accent: oklch(0.968 0.007 247.896);
|
|
23
|
+
--accent-foreground: oklch(0.208 0.042 265.755);
|
|
24
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
25
|
+
--border: oklch(0.929 0.013 255.508);
|
|
26
|
+
--input: oklch(0.929 0.013 255.508);
|
|
27
|
+
--ring: var(--primary);
|
|
28
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
29
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
30
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
31
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
32
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
33
|
+
--sidebar: oklch(0.984 0.003 247.858);
|
|
34
|
+
--sidebar-foreground: oklch(0.129 0.042 264.695);
|
|
35
|
+
--sidebar-primary: oklch(0.208 0.042 265.755);
|
|
36
|
+
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
|
37
|
+
--sidebar-accent: oklch(0.968 0.007 247.896);
|
|
38
|
+
--sidebar-accent-foreground: oklch(0.208 0.042 265.755);
|
|
39
|
+
--sidebar-border: oklch(0.929 0.013 255.508);
|
|
40
|
+
--sidebar-ring: oklch(0.704 0.04 256.788);
|
|
41
|
+
|
|
42
|
+
--font-sans: 'Roboto', sans-serif;
|
|
43
|
+
--text-base: 14px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[data-modal-open='true'] {
|
|
47
|
+
--background: oklch(1 0 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-theme='dark'] {
|
|
51
|
+
--background: oklch(0.32 0 0);
|
|
52
|
+
--foreground: oklch(0.984 0.003 247.858);
|
|
53
|
+
--card: oklch(26.239% 0.00928 285.704);
|
|
54
|
+
--card-foreground: oklch(0.984 0.003 247.858);
|
|
55
|
+
--popover: oklch(0.208 0.042 265.755);
|
|
56
|
+
--popover-foreground: oklch(0.984 0.003 247.858);
|
|
57
|
+
--primary: oklch(54.32% 0.09196 193.854);
|
|
58
|
+
--primary-foreground: oklch(1 0 0);
|
|
59
|
+
--secondary: oklch(0.279 0.041 260.031);
|
|
60
|
+
--secondary-foreground: oklch(0.984 0.003 247.858);
|
|
61
|
+
--muted: oklch(32.303% 0.00881 285.843);
|
|
62
|
+
--muted-foreground: oklch(66.488% 0.02269 273.875);
|
|
63
|
+
--accent: oklch(0.279 0.041 260.031);
|
|
64
|
+
--accent-foreground: oklch(0.984 0.003 247.858);
|
|
65
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
66
|
+
--border: oklch(1 0 0 / 10%);
|
|
67
|
+
--input: oklch(1 0 0 / 15%);
|
|
68
|
+
--ring: var(--primary);
|
|
69
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
70
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
71
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
72
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
73
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
74
|
+
--sidebar: oklch(0.208 0.042 265.755);
|
|
75
|
+
--sidebar-foreground: oklch(0.984 0.003 247.858);
|
|
76
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
77
|
+
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
|
78
|
+
--sidebar-accent: oklch(0.279 0.041 260.031);
|
|
79
|
+
--sidebar-accent-foreground: oklch(0.984 0.003 247.858);
|
|
80
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
81
|
+
--sidebar-ring: oklch(0.551 0.027 264.364);
|
|
82
|
+
|
|
83
|
+
&[data-modal-open='true'] {
|
|
84
|
+
--background: oklch(26.239% 0.00928 285.704);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@theme inline {
|
|
89
|
+
--font-sans: var(--font-sans);
|
|
90
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
91
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
92
|
+
--radius-lg: var(--radius);
|
|
93
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
94
|
+
--color-background: var(--background);
|
|
95
|
+
--color-foreground: var(--foreground);
|
|
96
|
+
--color-card: var(--card);
|
|
97
|
+
--color-card-foreground: var(--card-foreground);
|
|
98
|
+
--color-popover: var(--popover);
|
|
99
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
100
|
+
--color-primary: var(--primary);
|
|
101
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
102
|
+
--color-secondary: var(--secondary);
|
|
103
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
104
|
+
--color-muted: var(--muted);
|
|
105
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
106
|
+
--color-accent: var(--accent);
|
|
107
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
108
|
+
--color-destructive: var(--destructive);
|
|
109
|
+
--color-border: var(--border);
|
|
110
|
+
--color-input: var(--input);
|
|
111
|
+
--color-ring: var(--ring);
|
|
112
|
+
--color-chart-1: var(--chart-1);
|
|
113
|
+
--color-chart-2: var(--chart-2);
|
|
114
|
+
--color-chart-3: var(--chart-3);
|
|
115
|
+
--color-chart-4: var(--chart-4);
|
|
116
|
+
--color-chart-5: var(--chart-5);
|
|
117
|
+
--color-sidebar: var(--sidebar);
|
|
118
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
119
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
120
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
121
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
122
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
123
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
124
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@layer base {
|
|
128
|
+
* {
|
|
129
|
+
@apply border-border outline-ring/50;
|
|
130
|
+
}
|
|
131
|
+
body {
|
|
132
|
+
@apply bg-background text-foreground text-base;
|
|
133
|
+
}
|
|
134
|
+
button {
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
select {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
padding: 0 30px 0 14px;
|
|
142
|
+
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
|
143
|
+
background-repeat: no-repeat;
|
|
144
|
+
background-position: right 0.53125rem center;
|
|
145
|
+
background-size: 1rem;
|
|
146
|
+
appearance: none;
|
|
147
|
+
color: var(--text-color);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
[data-theme='dark'] {
|
|
151
|
+
select {
|
|
152
|
+
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@layer utilities {
|
|
158
|
+
.rounded {
|
|
159
|
+
border-radius: var(--radius);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { Input } from './components/ui/input/index.js';
|
|
2
|
+
export { Label } from './components/ui/label/index.js';
|
|
3
|
+
export { Skeleton } from './components/ui/skeleton/index.js';
|
|
4
|
+
export { Switch } from './components/ui/switch/index.js';
|
|
5
|
+
export { setup } from './setup.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// UI Components
|
|
2
|
+
export { Input } from './components/ui/input/index.js';
|
|
3
|
+
export { Label } from './components/ui/label/index.js';
|
|
4
|
+
export { Skeleton } from './components/ui/skeleton/index.js';
|
|
5
|
+
export { Switch } from './components/ui/switch/index.js';
|
|
6
|
+
// Global
|
|
7
|
+
export { setup } from './setup.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { mount } from 'svelte';
|
|
2
|
+
function mode_setter() {
|
|
3
|
+
function set_theme(event) {
|
|
4
|
+
document.documentElement.setAttribute('data-theme', event.matches ? 'dark' : 'default');
|
|
5
|
+
}
|
|
6
|
+
const match_media = window.matchMedia('(prefers-color-scheme: dark)');
|
|
7
|
+
set_theme(match_media);
|
|
8
|
+
match_media.addEventListener('change', set_theme);
|
|
9
|
+
return function cleanup() {
|
|
10
|
+
match_media.removeEventListener('change', set_theme);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function setup(app) {
|
|
14
|
+
let target = document.querySelector('#app');
|
|
15
|
+
if (!target) {
|
|
16
|
+
// In production, `#app` may or may not exist.
|
|
17
|
+
target = document.createElement('div');
|
|
18
|
+
target.id = 'app';
|
|
19
|
+
document.body.appendChild(target);
|
|
20
|
+
}
|
|
21
|
+
mode_setter();
|
|
22
|
+
mount(app, { target });
|
|
23
|
+
// This error replaces another error which message is harder to understand and impossible to avoid util the issue https://github.com/storyblok/field-plugin/issues/107 has been resolved.
|
|
24
|
+
throw new Error(`This error can be safely ignored. It is caused by the legacy field plugin API. See issue https://github.com/storyblok/field-plugin/issues/107`);
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ClassValue } from 'clsx';
|
|
2
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
3
|
+
export type WithoutChild<T> = T extends {
|
|
4
|
+
child?: any;
|
|
5
|
+
} ? Omit<T, 'child'> : T;
|
|
6
|
+
export type WithoutChildren<T> = T extends {
|
|
7
|
+
children?: any;
|
|
8
|
+
} ? Omit<T, 'children'> : T;
|
|
9
|
+
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
|
|
10
|
+
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & {
|
|
11
|
+
ref?: U | null;
|
|
12
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Storyblok Field Plugin with "Vanilla" JavaScript
|
|
2
|
+
|
|
3
|
+
This project was generated with `@storyblok/field-plugin-cli`.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
For development, run the application locally with
|
|
8
|
+
|
|
9
|
+
```shell
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
and open the [Sandbox](https://plugin-sandbox.storyblok.com/field-plugin/).
|
|
14
|
+
|
|
15
|
+
To build the project, run
|
|
16
|
+
|
|
17
|
+
```shell
|
|
18
|
+
npm run build
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Deploy the field plugin with the CLI. Issue a [personal access token](https://app.storyblok.com/#/me/account?tab=token), rename `.env.local.example` to `.env.example`, open the file, set the value `STORYBLOK_PERSONAL_ACCESS_TOKEN`, and run
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
npm run deploy
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Manifest File for Field Plugins
|
|
28
|
+
|
|
29
|
+
The manifest file is a configuration that enhances the functionality of your field plugin. This JSON file, named `field-plugin.config.json` and located in your project's root folder, is optional but highly recommended.
|
|
30
|
+
|
|
31
|
+
The manifest file allows you to configure [options](https://www.storyblok.com/docs/plugins/field-plugins/introduction#options) for your field plugin. When developing your field plugin with the [Sandbox](https://plugin-sandbox.storyblok.com/field-plugin/), the options are applied by default. Also, the deploy command automatically applies the options in production. So, you no longer need to configure the options manually.
|
|
32
|
+
|
|
33
|
+
### Configuring a Manifest File
|
|
34
|
+
|
|
35
|
+
The options list within the file `field-plugin.config.json` should consist of key-value objects representing the essential options required for your field plugin to function correctly, along with their corresponding values. This is an example of how it should be structured:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"options": [
|
|
40
|
+
{
|
|
41
|
+
"name": "myPluginInitialValue",
|
|
42
|
+
"value": 100
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Now, you just need to access these options in your code like in the example below:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
console.log(data.options.myPluginInitialValue)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Clean up the boilerplate
|
|
55
|
+
|
|
56
|
+
To start from a blank state, delete the following files:
|
|
57
|
+
|
|
58
|
+
- src/components/components.js
|
|
59
|
+
|
|
60
|
+
And search for comments `DELETE THIS BOILERPLATE` comments and delete the code around them.
|
|
61
|
+
|
|
62
|
+
## Continuous delivery
|
|
63
|
+
|
|
64
|
+
Set up [continuous delivery](https://www.storyblok.com/docs/plugins/field-plugins/continuous-delivery) with the CLI. Define an environmental variable `STORYBLOK_PERSONAL_ACCESS_TOKEN`, and use the `--name` and `--skipPrompts` options as such:
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
npm run deploy --name $NAME --skipPrompts
|
|
68
|
+
```
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createFieldPlugin, type FieldPluginResponse } from '@storyblok/field-plugin'
|
|
3
|
+
import { onMount } from 'svelte'
|
|
4
|
+
import { snake } from 'radashi'
|
|
5
|
+
import Color from 'color'
|
|
6
|
+
import { cn } from 'shared/utils'
|
|
7
|
+
|
|
8
|
+
type Theme = {
|
|
9
|
+
name: string
|
|
10
|
+
hex: string
|
|
11
|
+
value: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type Plugin = FieldPluginResponse<string | undefined>
|
|
15
|
+
|
|
16
|
+
let plugin: Plugin | null = $state(null)
|
|
17
|
+
let content: string | undefined = $state()
|
|
18
|
+
|
|
19
|
+
onMount(() => {
|
|
20
|
+
createFieldPlugin({
|
|
21
|
+
enablePortalModal: true,
|
|
22
|
+
validateContent: (content) => {
|
|
23
|
+
if (content === null || typeof content !== 'string') {
|
|
24
|
+
return { content: undefined }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { content }
|
|
28
|
+
},
|
|
29
|
+
async onUpdateState(state) {
|
|
30
|
+
plugin = state as Plugin
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
function update() {
|
|
36
|
+
if (plugin?.type !== 'loaded') return
|
|
37
|
+
const state = $state.snapshot(content)
|
|
38
|
+
plugin.actions.setContent(state)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const themes: Array<Theme> = $derived.by(() => {
|
|
42
|
+
if (plugin?.type !== 'loaded') return []
|
|
43
|
+
|
|
44
|
+
return Object.entries(plugin.data.options).map(([key, value]) => {
|
|
45
|
+
return {
|
|
46
|
+
name: key,
|
|
47
|
+
hex: value,
|
|
48
|
+
value: snake(key),
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
function is_light(hex: string): boolean {
|
|
54
|
+
try {
|
|
55
|
+
return Color(hex).isLight()
|
|
56
|
+
} catch {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
{#if themes?.length}
|
|
63
|
+
<fieldset class="flex items-center gap-2">
|
|
64
|
+
{#each themes as { value, name, hex }, index}
|
|
65
|
+
{#snippet label(value: string)}
|
|
66
|
+
<input
|
|
67
|
+
class="sr-only [&:checked+label]:border-[var(--color)] [&:checked+label]:bg-[var(--color-hover)]/10"
|
|
68
|
+
id={value}
|
|
69
|
+
type="radio"
|
|
70
|
+
{value}
|
|
71
|
+
bind:group={content}
|
|
72
|
+
onchange={update}
|
|
73
|
+
/>
|
|
74
|
+
<label
|
|
75
|
+
for={value}
|
|
76
|
+
class={cn([
|
|
77
|
+
'flex items-center gap-2 p-3 px-4 border rounded relative border-transparent transition-colors cursor-pointer',
|
|
78
|
+
{
|
|
79
|
+
'bg-muted-foreground/10 hover:bg-[var(--color-hover)]/10': value !== 'none',
|
|
80
|
+
},
|
|
81
|
+
])}
|
|
82
|
+
class:selected={content === value}
|
|
83
|
+
style={value === 'none'
|
|
84
|
+
? ''
|
|
85
|
+
: `
|
|
86
|
+
--color: ${hex};
|
|
87
|
+
--color-hover: ${is_light(hex) ? Color(hex).darken(0.8).hex() : hex};
|
|
88
|
+
`}
|
|
89
|
+
>
|
|
90
|
+
{#if value === 'none'}
|
|
91
|
+
None
|
|
92
|
+
{:else}
|
|
93
|
+
<svg
|
|
94
|
+
class="size-4"
|
|
95
|
+
viewBox="0 0 183 184"
|
|
96
|
+
fill="none"
|
|
97
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
98
|
+
>
|
|
99
|
+
<path
|
|
100
|
+
d="M133.476 6.95414C111.783 -4.24286 86.6097 0.589255 78.855 15.6182L78.816 15.6571C66.9435 3.6288 40.6656 6.34363 23.3116 23.5158C5.97051 40.675 4.10002 65.8488 16.0244 77.8772L15.9855 77.9161C0.865623 85.502 -4.40814 111.871 6.58103 133.693C17.5442 155.49 40.6527 164.492 55.7465 156.88L55.6426 156.984C59.7733 170.623 78.9849 184.483 100.794 183.638C125.189 182.677 145.725 160.14 143.166 143.423C159.858 146.137 178.082 130.017 182.031 105.909C185.967 81.813 174.12 58.8994 157.416 56.1456L157.455 56.1066C165.21 41.0777 155.208 18.1122 133.502 6.92815"
|
|
101
|
+
fill="var(--color)"
|
|
102
|
+
/>
|
|
103
|
+
</svg>
|
|
104
|
+
{name}
|
|
105
|
+
{/if}
|
|
106
|
+
</label>
|
|
107
|
+
{/snippet}
|
|
108
|
+
{#if index === 0}
|
|
109
|
+
{@render label('none')}
|
|
110
|
+
{/if}
|
|
111
|
+
{@render label(value)}
|
|
112
|
+
{/each}
|
|
113
|
+
</fieldset>
|
|
114
|
+
{/if}
|
package/src/index.ts
ADDED