@x33025/sveltely 0.1.4 → 0.1.7

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.
@@ -20,5 +20,11 @@
20
20
  <div class="text-3xl font-semibold text-[var(--sveltely-primary-color)]">
21
21
  <AnimatedNumber {value} />
22
22
  </div>
23
- <Button label="Change number" variant="solid" onclick={bump} />
23
+ <Button
24
+ label="Change number"
25
+ background="var(--sveltely-active-color)"
26
+ borderColor="var(--sveltely-active-color)"
27
+ color="var(--sveltely-background-color)"
28
+ onclick={bump}
29
+ />
24
30
  </div>
@@ -3,8 +3,9 @@
3
3
  import type { Component } from 'svelte';
4
4
  import type { HTMLButtonAttributes } from 'svelte/elements';
5
5
  import { tooltip } from '../../../actions/tooltip';
6
+ import Button from '../Button';
6
7
  import Spinner from '../Spinner';
7
- import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
8
+ import { extractStyleProps, type StyleProps } from '../../../style/surface';
8
9
 
9
10
  type Props = {
10
11
  icon?: Component<{ class?: string; size?: number | string }>;
@@ -45,6 +46,12 @@
45
46
  const isControlledLoading = $derived(loading !== undefined);
46
47
  const effectiveLoading = $derived(isControlledLoading ? loading : internalLoading);
47
48
  const effectiveDisabled = $derived(disabled || (disableWhileLoading && effectiveLoading));
49
+ const buttonStyleProps = $derived({
50
+ ...styleProps,
51
+ background: styleProps.background ?? 'var(--sveltely-active-color)',
52
+ borderColor: styleProps.borderColor ?? 'var(--sveltely-active-color)',
53
+ color: styleProps.color ?? 'var(--sveltely-background-color)'
54
+ });
48
55
 
49
56
  const handleClick = async () => {
50
57
  if (effectiveDisabled) return;
@@ -78,8 +85,6 @@
78
85
  const toRem = (value: number | string | undefined) =>
79
86
  value === undefined ? undefined : typeof value === 'number' ? `${value}rem` : value;
80
87
 
81
- const triggerStyle = $derived.by(() => surfaceStyle(styleProps, 'async-button'));
82
-
83
88
  const iconStyle = $derived.by(() => {
84
89
  const declarations: string[] = [];
85
90
 
@@ -101,103 +106,47 @@
101
106
  });
102
107
  </script>
103
108
 
104
- <button
105
- {type}
106
- use:tooltip={{ label: errorLabel ?? '', disabled: !errorLabel }}
107
- class="async-button inline-flex items-center disabled:cursor-not-allowed disabled:opacity-50 {variant ===
108
- 'iconOnly'
109
- ? 'async-button-icon-only'
110
- : ''}"
111
- style={triggerStyle}
112
- disabled={effectiveDisabled}
113
- aria-busy={effectiveLoading}
114
- aria-label={variant === 'iconOnly' ? label : undefined}
115
- data-error={error ? 'true' : 'false'}
116
- {...props}
117
- onclick={handleClick}
118
- >
119
- <span class="async-button-icon-frame inline-grid shrink-0 place-items-center" style={iconStyle}>
120
- {#if effectiveLoading}
121
- <Spinner size={iconSize !== undefined ? toRem(iconSize) : 'var(--async-button-icon-size)'} />
122
- {:else if error}
123
- <CircleAlertIcon class="async-button-icon text-red-600" />
124
- {:else if icon}
125
- {@const Icon = icon}
126
- <Icon class="async-button-icon" />
109
+ <span use:tooltip={{ label: errorLabel ?? '', disabled: !errorLabel }} class="async-button">
110
+ <Button
111
+ {type}
112
+ {...buttonStyleProps}
113
+ disabled={effectiveDisabled}
114
+ aria-busy={effectiveLoading}
115
+ aria-label={variant === 'iconOnly' ? label : undefined}
116
+ data-error={error ? 'true' : 'false'}
117
+ {...props}
118
+ onclick={handleClick}
119
+ >
120
+ <span class="async-button-icon-frame inline-grid shrink-0 place-items-center" style={iconStyle}>
121
+ {#if effectiveLoading}
122
+ <Spinner size={iconSize !== undefined ? toRem(iconSize) : 'var(--button-icon-size)'} />
123
+ {:else if error}
124
+ <CircleAlertIcon class="async-button-icon text-red-600" />
125
+ {:else if icon}
126
+ {@const Icon = icon}
127
+ <Icon class="async-button-icon" />
128
+ {/if}
129
+ </span>
130
+ {#if variant === 'iconAndLabel'}
131
+ <span class="async-button-text" style={labelStyle}>{label}</span>
127
132
  {/if}
128
- </span>
129
- {#if variant === 'iconAndLabel'}
130
- <span class="async-button-text" style={labelStyle}>{label}</span>
131
- {/if}
132
- </button>
133
+ </Button>
134
+ </span>
133
135
 
134
136
  <style>
135
137
  .async-button {
136
- --async-button-font-size: var(--sveltely-font-size);
137
- --async-button-scale: calc(var(--async-button-font-size) / 1rem);
138
- --async-button-icon-size: calc(var(--async-button-font-size) * 1.143);
139
- border: 1px solid var(--async-button-border-color, var(--sveltely-active-color));
140
- border-radius: var(--async-button-border-radius, var(--sveltely-border-radius));
141
- background: var(--async-button-background, var(--sveltely-active-color));
142
- color: var(--async-button-color, var(--sveltely-background-color));
143
- gap: var(--async-button-gap, var(--sveltely-gap));
144
- font-size: var(--async-button-font-size);
145
- line-height: 1.25;
146
- padding: var(
147
- --async-button-padding-y,
148
- calc(var(--sveltely-padding-y) * 0.67 * var(--async-button-scale))
149
- )
150
- var(--async-button-padding-x, calc(var(--sveltely-padding-x) * var(--async-button-scale)));
151
- transition:
152
- color 150ms,
153
- border-color 150ms,
154
- background-color 150ms;
155
- }
156
-
157
- .async-button:hover {
158
- background: var(--async-button-hover-background, var(--sveltely-active-hover-color));
159
- }
160
-
161
- .async-button-icon-only {
162
- padding: var(
163
- --async-button-padding-y,
164
- calc(var(--sveltely-padding-y) * 0.67 * var(--async-button-scale))
165
- )
166
- var(
167
- --async-button-padding-x,
168
- calc(var(--sveltely-padding-x) * 0.67 * var(--async-button-scale))
169
- );
170
- min-width: calc(
171
- (
172
- var(
173
- --async-button-padding-x,
174
- calc(var(--sveltely-padding-x) * 0.67 * var(--async-button-scale))
175
- ) *
176
- 2
177
- ) +
178
- 1rem
179
- );
180
- min-height: calc(
181
- (
182
- var(
183
- --async-button-padding-y,
184
- calc(var(--sveltely-padding-y) * 0.67 * var(--async-button-scale))
185
- ) *
186
- 2
187
- ) +
188
- 1rem
189
- );
138
+ display: inline-flex;
190
139
  }
191
140
 
192
141
  .async-button-icon-frame {
193
142
  display: inline-flex;
194
143
  align-items: center;
195
144
  justify-content: center;
196
- width: var(--async-button-icon-size);
197
- height: var(--async-button-icon-size);
145
+ width: var(--button-icon-size);
146
+ height: var(--button-icon-size);
198
147
  }
199
148
 
200
- .async-button-icon {
149
+ :global(.async-button-icon) {
201
150
  width: 100%;
202
151
  height: 100%;
203
152
  }
@@ -1,7 +1,7 @@
1
1
  <script module lang="ts">
2
2
  export const demo = {
3
3
  name: 'Button',
4
- description: 'Token-aware button primitive with optional icon support.',
4
+ description: 'Token-aware button primitive with composable content.',
5
5
  columnSpan: 2
6
6
  };
7
7
  </script>
@@ -14,6 +14,14 @@
14
14
 
15
15
  <HStack align="center" gap={0.75}>
16
16
  <Button label="Default" />
17
- <Button label="Solid" variant="solid" />
18
- <Button icon={SaveIcon} label="With icon" />
17
+ <Button
18
+ label="Solid"
19
+ background="var(--sveltely-active-color)"
20
+ borderColor="var(--sveltely-active-color)"
21
+ color="var(--sveltely-background-color)"
22
+ />
23
+ <Button>
24
+ <SaveIcon />
25
+ <span>With icon</span>
26
+ </Button>
19
27
  </HStack>
@@ -1,48 +1,33 @@
1
1
  <script lang="ts">
2
- import type { Component, Snippet } from 'svelte';
2
+ import type { Snippet } from 'svelte';
3
3
  import type { HTMLButtonAttributes } from 'svelte/elements';
4
4
  import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
5
5
 
6
6
  type Props = {
7
7
  children?: Snippet;
8
8
  label?: string;
9
- icon?: Component<{ class?: string; size?: number | string }>;
10
- iconSize?: number | string;
11
- iconColor?: string;
12
- variant?: 'default' | 'solid' | 'ghost';
13
9
  } & StyleProps &
14
10
  Omit<HTMLButtonAttributes, 'children' | 'class' | 'style'>;
15
11
 
16
- let {
17
- children,
18
- label,
19
- icon,
20
- iconSize,
21
- iconColor,
22
- variant = 'default',
23
- disabled = false,
24
- type = 'button',
25
- ...restProps
26
- }: Props = $props();
12
+ let { children, label, disabled = false, type = 'button', ...restProps }: Props = $props();
27
13
 
28
14
  const extractedStyleProps = $derived.by(() => extractStyleProps(restProps));
29
15
  const styleProps = $derived(extractedStyleProps.styleProps);
30
16
  const props = $derived(extractedStyleProps.restProps);
31
17
 
32
- const toRem = (value: number | string | undefined) =>
33
- value === undefined ? undefined : typeof value === 'number' ? `${value}rem` : value;
34
-
35
- const rootStyle = $derived.by(() => surfaceStyle(styleProps, 'button'));
36
- const iconStyle = $derived.by(() => {
37
- const declarations: string[] = [];
38
- if (iconColor !== undefined) {
39
- declarations.push(`color: ${iconColor};`);
18
+ const rootStyle = $derived.by(() => {
19
+ const { background, borderColor, color, ...surfaceProps } = styleProps;
20
+ const declarations = [surfaceStyle(surfaceProps, 'button')];
21
+ if (background !== undefined) {
22
+ declarations.push(`--button-background: ${background};`);
23
+ }
24
+ if (borderColor !== undefined) {
25
+ declarations.push(`--button-border-color: ${borderColor};`);
40
26
  }
41
- if (iconSize !== undefined) {
42
- const size = toRem(iconSize);
43
- declarations.push(`width: ${size};`, `height: ${size};`);
27
+ if (color !== undefined) {
28
+ declarations.push(`--button-color: ${color};`);
44
29
  }
45
- return declarations.join(' ');
30
+ return declarations.filter(Boolean).join(' ');
46
31
  });
47
32
  const iconOnly = $derived(!children && !label);
48
33
  </script>
@@ -52,18 +37,10 @@
52
37
  class="button inline-flex items-center justify-center disabled:cursor-not-allowed disabled:opacity-50 {iconOnly
53
38
  ? 'button-icon-only'
54
39
  : ''}"
55
- data-variant={variant}
56
40
  style={rootStyle}
57
41
  {disabled}
58
42
  {...props}
59
43
  >
60
- {#if icon}
61
- {@const Icon = icon}
62
- <span class="button-icon-frame inline-grid shrink-0 place-items-center" style={iconStyle}>
63
- <Icon class="button-icon" />
64
- </span>
65
- {/if}
66
-
67
44
  {#if children}
68
45
  {@render children()}
69
46
  {:else if label}
@@ -91,27 +68,15 @@
91
68
  background-color 150ms;
92
69
  }
93
70
 
94
- .button[data-variant='default']:hover {
95
- background: var(--button-hover-background, var(--sveltely-hover-color));
96
- }
97
-
98
- .button[data-variant='solid'] {
99
- border-color: var(--button-solid-border-color, var(--sveltely-active-color));
100
- background: var(--button-solid-background, var(--sveltely-active-color));
101
- color: var(--button-solid-color, var(--sveltely-background-color));
102
- }
103
-
104
- .button[data-variant='solid']:hover {
105
- background: var(--button-solid-hover-background, var(--sveltely-active-hover-color));
106
- }
107
-
108
- .button[data-variant='ghost'] {
109
- border-color: transparent;
110
- background: transparent;
111
- }
112
-
113
- .button[data-variant='ghost']:hover {
114
- background: var(--button-ghost-hover-background, var(--sveltely-hover-color));
71
+ .button:hover {
72
+ background: var(
73
+ --button-hover-background,
74
+ color-mix(
75
+ in oklab,
76
+ var(--button-background, var(--sveltely-background-color)) 88%,
77
+ var(--button-color, var(--sveltely-primary-color))
78
+ )
79
+ );
115
80
  }
116
81
 
117
82
  .button-icon-only {
@@ -127,16 +92,9 @@
127
92
  );
128
93
  }
129
94
 
130
- .button-icon-frame {
131
- display: inline-flex;
132
- align-items: center;
133
- justify-content: center;
95
+ .button :global(svg) {
134
96
  width: var(--button-icon-size);
135
97
  height: var(--button-icon-size);
136
- }
137
-
138
- .button-icon {
139
- width: 100%;
140
- height: 100%;
98
+ flex-shrink: 0;
141
99
  }
142
100
  </style>
@@ -1,17 +1,10 @@
1
- import type { Component, Snippet } from 'svelte';
1
+ import type { Snippet } from 'svelte';
2
2
  import type { HTMLButtonAttributes } from 'svelte/elements';
3
3
  import { type StyleProps } from '../../../style/surface';
4
4
  type Props = {
5
5
  children?: Snippet;
6
6
  label?: string;
7
- icon?: Component<{
8
- class?: string;
9
- size?: number | string;
10
- }>;
11
- iconSize?: number | string;
12
- iconColor?: string;
13
- variant?: 'default' | 'solid' | 'ghost';
14
7
  } & StyleProps & Omit<HTMLButtonAttributes, 'children' | 'class' | 'style'>;
15
- declare const Button: Component<Props, {}, "">;
8
+ declare const Button: import("svelte").Component<Props, {}, "">;
16
9
  type Button = ReturnType<typeof Button>;
17
10
  export default Button;
@@ -7,7 +7,7 @@
7
7
  </script>
8
8
 
9
9
  <script lang="ts">
10
- import { CheckIcon, ChevronDownIcon, PlusIcon, UserRoundXIcon } from '@lucide/svelte';
10
+ import { CheckIcon, PlusIcon, UserRoundXIcon } from '@lucide/svelte';
11
11
  import SearchField from '../SearchField';
12
12
  import ScrollView, { type ScrollGeometry } from '../ScrollView';
13
13
  import Dropdown from './index';
@@ -117,31 +117,23 @@
117
117
  selectedLabel={selectedWebsite?.name ?? null}
118
118
  showCheck={false}
119
119
  >
120
- {#snippet trigger(dropdown)}
121
- <button
122
- use:dropdown.useTrigger
123
- type="button"
124
- class="hstack w-full items-center gap-2 rounded-full border px-3 py-2 pr-2 text-left text-sm"
125
- style="border-color: var(--sveltely-border-color); background: var(--sveltely-background-color);"
126
- aria-expanded={dropdown.open}
127
- aria-haspopup="dialog"
128
- onclick={dropdown.toggle}
129
- >
120
+ {#snippet trigger()}
121
+ <Dropdown.Trigger>
130
122
  {#if selectedWebsite}
131
- <span class="min-w-0 flex-1 truncate text-[var(--sveltely-primary-color)]"
132
- >{selectedWebsite.name}</span
133
- >
134
- {#if selectedWebsite.domain}
135
- <span class="max-w-32 truncate text-xs text-[var(--sveltely-secondary-color)]"
136
- >{selectedWebsite.domain}</span
123
+ <span class="hstack min-w-0 items-center gap-2">
124
+ <span class="truncate text-[var(--sveltely-primary-color)]"
125
+ >{selectedWebsite.name}</span
137
126
  >
138
- {/if}
127
+ {#if selectedWebsite.domain}
128
+ <span class="truncate text-xs text-[var(--sveltely-secondary-color)]">
129
+ {selectedWebsite.domain}
130
+ </span>
131
+ {/if}
132
+ </span>
139
133
  {:else}
140
- <span class="min-w-0 flex-1 text-[var(--sveltely-secondary-color)]">Select website</span
141
- >
134
+ <span class="text-[var(--sveltely-secondary-color)]">Select website</span>
142
135
  {/if}
143
- <ChevronDownIcon size={14} class="shrink-0 text-[var(--sveltely-secondary-color)]" />
144
- </button>
136
+ </Dropdown.Trigger>
145
137
  {/snippet}
146
138
 
147
139
  <SearchField bind:value={websiteQuery} placeholder="Search websites..." />
@@ -4,7 +4,11 @@
4
4
  import Floating from '../Floating/Floating.svelte';
5
5
  import { surfaceStyle, type StyleProps } from '../../../style/surface';
6
6
  import type { Anchor } from '../../../utils/positioning';
7
- import { setDropdownContext, type DropdownTriggerState } from './context';
7
+ import {
8
+ setDropdownContext,
9
+ setDropdownTriggerContext,
10
+ type DropdownTriggerState
11
+ } from './context';
8
12
 
9
13
  type Props = {
10
14
  value?: TValue | null;
@@ -80,7 +84,7 @@
80
84
  >
81
85
  {#snippet trigger(floating)}
82
86
  {#if triggerContent}
83
- {@render triggerContent({
87
+ {@const triggerState = setDropdownTriggerContext({
84
88
  useTrigger: floating.useTrigger,
85
89
  open: floating.open,
86
90
  value,
@@ -91,20 +95,32 @@
91
95
  openPanel: floating.openPanel,
92
96
  closePanel: floating.closePanel
93
97
  })}
98
+ {@render triggerContent(triggerState)}
94
99
  {:else}
100
+ {@const triggerState = {
101
+ useTrigger: floating.useTrigger,
102
+ open: floating.open,
103
+ value,
104
+ selectedLabel: triggerText,
105
+ placeholder,
106
+ disabled,
107
+ toggle: floating.toggle,
108
+ openPanel: floating.openPanel,
109
+ closePanel: floating.closePanel
110
+ }}
95
111
  <button
96
- use:floating.useTrigger
112
+ use:triggerState.useTrigger
97
113
  type="button"
98
114
  class="dropdown-trigger justify-between"
99
115
  aria-label={label}
100
- aria-disabled={disabled}
101
- {disabled}
116
+ aria-disabled={triggerState.disabled}
117
+ disabled={triggerState.disabled}
102
118
  style={dropdownStyle}
103
- aria-expanded={floating.open}
119
+ aria-expanded={triggerState.open}
104
120
  aria-haspopup="dialog"
105
- onclick={floating.toggle}
121
+ onclick={triggerState.toggle}
106
122
  >
107
- <span class="dropdown-trigger-label">{triggerText}</span>
123
+ <span class="dropdown-trigger-label">{triggerState.selectedLabel}</span>
108
124
  <ChevronDownIcon class="size-4 text-[var(--sveltely-secondary-color)]" />
109
125
  </button>
110
126
  {/if}
@@ -0,0 +1,71 @@
1
+ <script lang="ts">
2
+ import { ChevronDownIcon } from '@lucide/svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import { getDropdownTriggerContext, type DropdownTriggerState } from './context';
5
+
6
+ type Props = {
7
+ children?: Snippet<[DropdownTriggerState]>;
8
+ };
9
+
10
+ let { children }: Props = $props();
11
+
12
+ const dropdown = getDropdownTriggerContext();
13
+ </script>
14
+
15
+ <button
16
+ use:dropdown.useTrigger
17
+ type="button"
18
+ class="dropdown-trigger justify-between"
19
+ aria-disabled={dropdown.disabled}
20
+ disabled={dropdown.disabled}
21
+ aria-expanded={dropdown.open}
22
+ aria-haspopup="dialog"
23
+ onclick={dropdown.toggle}
24
+ >
25
+ <span class="dropdown-trigger-label">
26
+ {#if children}
27
+ {@render children(dropdown)}
28
+ {:else}
29
+ {dropdown.selectedLabel}
30
+ {/if}
31
+ </span>
32
+ <ChevronDownIcon class="size-4 text-[var(--sveltely-secondary-color)]" />
33
+ </button>
34
+
35
+ <style>
36
+ .dropdown-trigger {
37
+ display: inline-flex;
38
+ min-width: 8rem;
39
+ max-width: 100%;
40
+ align-items: center;
41
+ border: 1px solid var(--sveltely-border-color);
42
+ border-radius: var(--sveltely-border-radius);
43
+ background: var(--sveltely-background-color);
44
+ color: var(--sveltely-primary-color);
45
+ gap: var(--sveltely-gap);
46
+ padding: calc(var(--sveltely-padding-y) * 0.67) var(--sveltely-padding-x);
47
+ font-size: 0.875rem;
48
+ line-height: 1.25rem;
49
+ text-align: left;
50
+ transition:
51
+ color 150ms,
52
+ border-color 150ms,
53
+ background-color 150ms;
54
+ }
55
+
56
+ .dropdown-trigger-label {
57
+ min-width: 0;
58
+ flex: 1 1 auto;
59
+ overflow: hidden;
60
+ text-overflow: ellipsis;
61
+ white-space: nowrap;
62
+ }
63
+
64
+ .dropdown-trigger :global(svg) {
65
+ flex: 0 0 auto;
66
+ }
67
+
68
+ .dropdown-trigger:hover {
69
+ background: var(--sveltely-hover-color);
70
+ }
71
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type DropdownTriggerState } from './context';
3
+ type Props = {
4
+ children?: Snippet<[DropdownTriggerState]>;
5
+ };
6
+ declare const Trigger: import("svelte").Component<Props, {}, "">;
7
+ type Trigger = ReturnType<typeof Trigger>;
8
+ export default Trigger;
@@ -32,3 +32,5 @@ export type DropdownContext<TValue = DropdownValue> = {
32
32
  };
33
33
  export declare const setDropdownContext: <TValue>(context: DropdownContext<TValue>) => void;
34
34
  export declare const getDropdownContext: <TValue>() => DropdownContext<TValue>;
35
+ export declare const setDropdownTriggerContext: <TValue>(context: DropdownTriggerState<TValue>) => DropdownTriggerState<TValue>;
36
+ export declare const getDropdownTriggerContext: <TValue>() => DropdownTriggerState<TValue>;
@@ -1,6 +1,12 @@
1
1
  import { getContext, setContext } from 'svelte';
2
2
  const dropdownContextKey = Symbol('dropdown');
3
+ const dropdownTriggerContextKey = Symbol('dropdown-trigger');
3
4
  export const setDropdownContext = (context) => {
4
5
  setContext(dropdownContextKey, context);
5
6
  };
6
7
  export const getDropdownContext = () => getContext(dropdownContextKey);
8
+ export const setDropdownTriggerContext = (context) => {
9
+ setContext(dropdownTriggerContextKey, context);
10
+ return context;
11
+ };
12
+ export const getDropdownTriggerContext = () => getContext(dropdownTriggerContextKey);
@@ -3,11 +3,13 @@ import Action from './Action.svelte';
3
3
  import Divider from './Divider.svelte';
4
4
  import Item from './Item.svelte';
5
5
  import Section from './Section.svelte';
6
+ import Trigger from './Trigger.svelte';
6
7
  declare const Dropdown: typeof DropdownComponent & {
7
8
  Action: typeof Action;
8
9
  Divider: typeof Divider;
9
10
  Item: typeof Item;
10
11
  Section: typeof Section;
12
+ Trigger: typeof Trigger;
11
13
  };
12
14
  export default Dropdown;
13
15
  export type { DropdownActionState, DropdownItemState, DropdownTriggerState } from './context';
@@ -3,9 +3,11 @@ import Action from './Action.svelte';
3
3
  import Divider from './Divider.svelte';
4
4
  import Item from './Item.svelte';
5
5
  import Section from './Section.svelte';
6
+ import Trigger from './Trigger.svelte';
6
7
  const Dropdown = DropdownComponent;
7
8
  Dropdown.Action = Action;
8
9
  Dropdown.Divider = Divider;
9
10
  Dropdown.Item = Item;
10
11
  Dropdown.Section = Section;
12
+ Dropdown.Trigger = Trigger;
11
13
  export default Dropdown;
@@ -32,7 +32,6 @@
32
32
  shrink,
33
33
  basis,
34
34
  border,
35
- overflow,
36
35
  align,
37
36
  justify,
38
37
  fontSize,
@@ -58,7 +57,6 @@
58
57
  shrink,
59
58
  basis,
60
59
  border,
61
- overflow,
62
60
  align,
63
61
  justify
64
62
  });
@@ -1,7 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { tick } from 'svelte';
3
3
  import type { Snippet } from 'svelte';
4
- import { surfaceStyle, type StyleProps } from '../../../style/surface';
4
+ import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
5
+ import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
5
6
  import type { ScrollAxis } from '../../../style/scroll';
6
7
 
7
8
  export type ScrollGeometry = {
@@ -38,7 +39,8 @@
38
39
  onScroll?: (geometry: ScrollGeometry) => void;
39
40
  scrollGradient?: boolean;
40
41
  scrollGradientSize?: number | string;
41
- } & StyleProps;
42
+ } & LayoutProps &
43
+ StyleProps;
42
44
 
43
45
  let {
44
46
  children,
@@ -48,36 +50,25 @@
48
50
  onScroll,
49
51
  scrollGradient = true,
50
52
  scrollGradientSize = '1rem',
51
- fontSize,
52
- paddingX,
53
- paddingY,
54
- gap,
55
- borderRadius,
56
- inset,
57
- background,
58
- borderColor,
59
- color
53
+ ...restProps
60
54
  }: Props = $props();
61
55
 
62
- const styleProps = $derived({
63
- fontSize,
64
- paddingX,
65
- paddingY,
66
- gap,
67
- borderRadius,
68
- inset,
69
- background,
70
- borderColor,
71
- color
72
- });
56
+ const extractedLayoutProps = $derived.by(() => extractLayoutProps(restProps));
57
+ const layoutProps = $derived(extractedLayoutProps.layoutProps);
58
+ const afterLayoutProps = $derived(extractedLayoutProps.restProps);
59
+ const extractedStyleProps = $derived.by(() => extractStyleProps(afterLayoutProps));
60
+ const styleProps = $derived(extractedStyleProps.styleProps);
73
61
  const viewportStyle = $derived.by(() => surfaceStyle(styleProps, 'scroll-view'));
74
62
  const contentStyle = $derived.by(() => surfaceStyle(contentStyles, 'scroll-view-content'));
75
- let canScrollToTop = $state(false);
76
- let canScrollToBottom = $state(false);
77
- const scrollGradientEnabled = $derived(scrollGradient && axis !== 'horizontal');
78
63
  const scrollGradientStyle = $derived(
79
64
  `--scroll-view-gradient-size: ${typeof scrollGradientSize === 'number' ? `${scrollGradientSize}rem` : scrollGradientSize};`
80
65
  );
66
+ const rootStyle = $derived.by(() =>
67
+ [layoutStyle(layoutProps), viewportStyle, scrollGradientStyle].filter(Boolean).join(' ')
68
+ );
69
+ let canScrollToTop = $state(false);
70
+ let canScrollToBottom = $state(false);
71
+ const scrollGradientEnabled = $derived(scrollGradient && axis !== 'horizontal');
81
72
 
82
73
  function syncScrollGradient(geometry: ScrollGeometry) {
83
74
  if (!scrollGradientEnabled) return;
@@ -146,10 +137,10 @@
146
137
  class:scroll-view-vertical={axis === 'vertical'}
147
138
  class:scroll-view-horizontal={axis === 'horizontal'}
148
139
  class:scroll-view-both={axis === 'both'}
149
- class:scroll-view-gradient={scrollGradientEnabled}
140
+ class:scroll-view-has-gradient={scrollGradientEnabled}
150
141
  class:scroll-view-can-scroll-up={canScrollToTop}
151
142
  class:scroll-view-can-scroll-down={canScrollToBottom}
152
- style={[viewportStyle, scrollGradientStyle].filter(Boolean).join(' ')}
143
+ style={rootStyle}
153
144
  onscroll={handleScroll}
154
145
  onwheel={handleWheel}
155
146
  >
@@ -1,4 +1,5 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import { type LayoutProps } from '../../../style/layout';
2
3
  import { type StyleProps } from '../../../style/surface';
3
4
  import type { ScrollAxis } from '../../../style/scroll';
4
5
  export type ScrollGeometry = {
@@ -34,7 +35,7 @@ type Props = {
34
35
  onScroll?: (geometry: ScrollGeometry) => void;
35
36
  scrollGradient?: boolean;
36
37
  scrollGradientSize?: number | string;
37
- } & StyleProps;
38
+ } & LayoutProps & StyleProps;
38
39
  declare const ScrollView: import("svelte").Component<Props, {}, "viewport">;
39
40
  type ScrollView = ReturnType<typeof ScrollView>;
40
41
  export default ScrollView;
@@ -19,20 +19,31 @@
19
19
  let { demos }: Props = $props();
20
20
  </script>
21
21
 
22
- <Grid
23
- columns="repeat(auto-fit, minmax(min(100%, 18rem), 1fr))"
24
- autoRows={14}
25
- gap={1}
26
- paddingX="calc(var(--sveltely-padding-x) * 2)"
27
- paddingY="calc(var(--sveltely-padding-y) * 2)"
28
- overflow="auto"
29
- >
30
- {#each demos as entry}
31
- {@const DemoComponent = entry.component}
32
- <GridItem columnSpan={entry.columnSpan ?? 1} rowSpan={entry.rowSpan ?? 1}>
33
- <HeroCard title={entry.name} description={entry.description}>
34
- <DemoComponent />
35
- </HeroCard>
36
- </GridItem>
37
- {/each}
38
- </Grid>
22
+ <div class="component-grid-scroll">
23
+ <Grid
24
+ columns="repeat(auto-fit, minmax(min(100%, 18rem), 1fr))"
25
+ autoRows={14}
26
+ gap={1}
27
+ paddingX="calc(var(--sveltely-padding-x) * 2)"
28
+ paddingY="calc(var(--sveltely-padding-y) * 2)"
29
+ >
30
+ {#each demos as entry (entry.name)}
31
+ {@const DemoComponent = entry.component}
32
+ <GridItem columnSpan={entry.columnSpan ?? 1} rowSpan={entry.rowSpan ?? 1}>
33
+ <HeroCard title={entry.name} description={entry.description}>
34
+ <DemoComponent />
35
+ </HeroCard>
36
+ </GridItem>
37
+ {/each}
38
+ </Grid>
39
+ </div>
40
+
41
+ <style>
42
+ .component-grid-scroll {
43
+ width: 100%;
44
+ height: 100%;
45
+ min-width: 0;
46
+ min-height: 0;
47
+ overflow: auto;
48
+ }
49
+ </style>
@@ -32,13 +32,13 @@
32
32
  <p class="max-w-3xl text-sm text-[var(--sveltely-secondary-color)]">{description}</p>
33
33
  {/if}
34
34
  {/if}
35
- <VStack grow shrink overflow="hidden" paddingX={0} paddingY={0}>
35
+ <div class="hero-card-content">
36
36
  {#if children}
37
37
  <HStack minWidth="min-content" minHeight="100%" align="center" justify="center">
38
38
  {@render children()}
39
39
  </HStack>
40
40
  {/if}
41
- </VStack>
41
+ </div>
42
42
  </VStack>
43
43
 
44
44
  <style>
@@ -50,4 +50,11 @@
50
50
  p {
51
51
  max-width: none;
52
52
  }
53
+
54
+ .hero-card-content {
55
+ min-width: 0;
56
+ min-height: 0;
57
+ flex: 1 1 0;
58
+ overflow: hidden;
59
+ }
53
60
  </style>
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@ export { motion, hover } from './actions/motion';
2
2
  export { portalHost, portalContent } from './actions/portal';
3
3
  export { tooltip } from './actions/tooltip';
4
4
  export type { TooltipOptions } from './actions/tooltip';
5
- export { LayoutAlignment, LayoutJustification, LayoutOverflow, LayoutSize } from './style/layout';
6
- export type { LayoutAlignmentValue, LayoutJustificationValue, LayoutOverflowValue, LayoutProps, LayoutSizeValue } from './style/layout';
5
+ export { LayoutAlignment, LayoutJustification, LayoutSize } from './style/layout';
6
+ export type { LayoutAlignmentValue, LayoutJustificationValue, LayoutProps, LayoutSizeValue } from './style/layout';
7
7
  export { LabelOrientation } from './style/label';
8
8
  export type { LabelOrientationValue } from './style/label';
9
9
  export { ImageFit, ImageLoading } from './style/media';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { motion, hover } from './actions/motion';
2
2
  export { portalHost, portalContent } from './actions/portal';
3
3
  export { tooltip } from './actions/tooltip';
4
- export { LayoutAlignment, LayoutJustification, LayoutOverflow, LayoutSize } from './style/layout';
4
+ export { LayoutAlignment, LayoutJustification, LayoutSize } from './style/layout';
5
5
  export { LabelOrientation } from './style/label';
6
6
  export { ImageFit, ImageLoading } from './style/media';
7
7
  export { ScrollAxis } from './style/scroll';
@@ -3,13 +3,6 @@ export declare const LayoutSize: {
3
3
  readonly full: "full";
4
4
  readonly fit: "fit";
5
5
  };
6
- export declare const LayoutOverflow: {
7
- readonly visible: "visible";
8
- readonly hidden: "hidden";
9
- readonly clip: "clip";
10
- readonly auto: "auto";
11
- readonly scroll: "scroll";
12
- };
13
6
  export declare const LayoutAlignment: {
14
7
  readonly start: "start";
15
8
  readonly center: "center";
@@ -26,11 +19,9 @@ export declare const LayoutJustification: {
26
19
  readonly evenly: "evenly";
27
20
  };
28
21
  export type LayoutSizeValue = (typeof LayoutSize)[keyof typeof LayoutSize];
29
- export type LayoutOverflowValue = (typeof LayoutOverflow)[keyof typeof LayoutOverflow];
30
22
  export type LayoutAlignmentValue = (typeof LayoutAlignment)[keyof typeof LayoutAlignment];
31
23
  export type LayoutJustificationValue = (typeof LayoutJustification)[keyof typeof LayoutJustification];
32
24
  export type LayoutSize = StringWithAutocomplete<LayoutSizeValue> | number;
33
- export type LayoutOverflow = LayoutOverflowValue;
34
25
  export type LayoutAlignment = LayoutAlignmentValue;
35
26
  export type LayoutJustification = LayoutJustificationValue;
36
27
  export type LayoutProps = {
@@ -45,7 +36,6 @@ export type LayoutProps = {
45
36
  shrink?: boolean | number;
46
37
  basis?: number | string;
47
38
  border?: boolean | string;
48
- overflow?: LayoutOverflow;
49
39
  align?: LayoutAlignment;
50
40
  justify?: LayoutJustification;
51
41
  };
@@ -2,13 +2,6 @@ export const LayoutSize = {
2
2
  full: 'full',
3
3
  fit: 'fit'
4
4
  };
5
- export const LayoutOverflow = {
6
- visible: 'visible',
7
- hidden: 'hidden',
8
- clip: 'clip',
9
- auto: 'auto',
10
- scroll: 'scroll'
11
- };
12
5
  export const LayoutAlignment = {
13
6
  start: 'start',
14
7
  center: 'center',
@@ -36,7 +29,6 @@ const LAYOUT_PROP_KEYS = new Set([
36
29
  'shrink',
37
30
  'basis',
38
31
  'border',
39
- 'overflow',
40
32
  'align',
41
33
  'justify'
42
34
  ]);
@@ -115,9 +107,6 @@ export const layoutStyle = (styles) => {
115
107
  : `1px solid ${styles.border}`;
116
108
  declarations.push(`border: ${borderValue};`);
117
109
  }
118
- if (styles.overflow !== undefined) {
119
- declarations.push(`overflow: ${styles.overflow};`);
120
- }
121
110
  if (styles.align !== undefined) {
122
111
  declarations.push(`align-items: ${alignValues[styles.align]};`);
123
112
  }
package/dist/style.css CHANGED
@@ -298,9 +298,6 @@
298
298
  .max-w-3xl {
299
299
  max-width: var(--container-3xl);
300
300
  }
301
- .max-w-32 {
302
- max-width: calc(var(--spacing) * 32);
303
- }
304
301
  .max-w-md {
305
302
  max-width: var(--container-md);
306
303
  }
@@ -412,9 +409,6 @@
412
409
  .rounded {
413
410
  border-radius: 0.25rem;
414
411
  }
415
- .rounded-full {
416
- border-radius: calc(infinity * 1px);
417
- }
418
412
  .rounded-md {
419
413
  border-radius: var(--radius-md);
420
414
  }
@@ -458,18 +452,9 @@
458
452
  .px-2 {
459
453
  padding-inline: calc(var(--spacing) * 2);
460
454
  }
461
- .px-3 {
462
- padding-inline: calc(var(--spacing) * 3);
463
- }
464
455
  .py-1 {
465
456
  padding-block: calc(var(--spacing) * 1);
466
457
  }
467
- .py-2 {
468
- padding-block: calc(var(--spacing) * 2);
469
- }
470
- .pr-2 {
471
- padding-right: calc(var(--spacing) * 2);
472
- }
473
458
  .text-left {
474
459
  text-align: left;
475
460
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x33025/sveltely",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",