@noxlovette/material 0.3.0 → 0.3.2

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.
@@ -0,0 +1,71 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import ContextMenu from './ContextMenu.svelte';
4
+
5
+ const { Story } = defineMeta({
6
+ title: 'Containers/Context Menu',
7
+ component: ContextMenu
8
+ });
9
+ </script>
10
+
11
+ <Story name="Playground" asChild>
12
+ <ContextMenu
13
+ items={[
14
+ { label: 'Edit', value: 'edit', iconProps: { name: 'edit' } },
15
+ { label: 'Duplicate', value: 'duplicate', iconProps: { name: 'content_copy' } },
16
+ { separator: true },
17
+ { label: 'Delete', value: 'delete', iconProps: { name: 'delete' } }
18
+ ]}
19
+ onselect={(value) => console.log('selected', value)}
20
+ >
21
+ <div
22
+ class="border-md-sys-color-outline text-md-sys-color-on-surface-variant flex size-40 items-center justify-center rounded-lg border border-dashed"
23
+ >
24
+ Right click me
25
+ </div>
26
+ </ContextMenu>
27
+ </Story>
28
+
29
+ <Story name="With Submenu" asChild>
30
+ <ContextMenu
31
+ items={[
32
+ { label: 'Edit', value: 'edit', iconProps: { name: 'edit' } },
33
+ { separator: true },
34
+ {
35
+ label: 'Share',
36
+ value: 'share',
37
+ iconProps: { name: 'share' },
38
+ items: [
39
+ { label: 'Email', value: 'email' },
40
+ { label: 'Link', value: 'link' }
41
+ ]
42
+ },
43
+ { separator: true },
44
+ { label: 'Delete', value: 'delete', iconProps: { name: 'delete' } }
45
+ ]}
46
+ onselect={(value) => console.log('selected', value)}
47
+ >
48
+ <div
49
+ class="border-md-sys-color-outline text-md-sys-color-on-surface-variant flex size-40 items-center justify-center rounded-lg border border-dashed"
50
+ >
51
+ Right click me
52
+ </div>
53
+ </ContextMenu>
54
+ </Story>
55
+
56
+ <Story name="With Selected Item" asChild>
57
+ <ContextMenu
58
+ selected="name"
59
+ items={[
60
+ { label: 'Name', value: 'name' },
61
+ { label: 'Date modified', value: 'date' },
62
+ { label: 'Size', value: 'size' }
63
+ ]}
64
+ >
65
+ <div
66
+ class="border-md-sys-color-outline text-md-sys-color-on-surface-variant flex size-40 items-center justify-center rounded-lg border border-dashed"
67
+ >
68
+ Right click me
69
+ </div>
70
+ </ContextMenu>
71
+ </Story>
@@ -0,0 +1,19 @@
1
+ import ContextMenu from './ContextMenu.svelte';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const ContextMenu: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type ContextMenu = InstanceType<typeof ContextMenu>;
19
+ export default ContextMenu;
@@ -76,7 +76,7 @@ Context menus appear on right-click and provide actions related to the clicked e
76
76
  mode: 'scale'
77
77
  }}
78
78
  >
79
- {#each item.items as subItem}
79
+ {#each item.items as subItem (subItem)}
80
80
  {@render MenuItem(subItem)}
81
81
  {/each}
82
82
  </div>
@@ -139,7 +139,7 @@ Context menus appear on right-click and provide actions related to the clicked e
139
139
  mode: 'scale'
140
140
  }}
141
141
  >
142
- {#each items as item}
142
+ {#each items as item (item)}
143
143
  {@render MenuItem(item)}
144
144
  {/each}
145
145
  </div>
@@ -0,0 +1,62 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import Divider from './Divider.svelte';
4
+ import { Body } from '../../../index.js';
5
+
6
+ const { Story } = defineMeta({
7
+ title: 'Containers/Divider',
8
+ component: Divider,
9
+ argTypes: {
10
+ variant: { control: 'select', options: ['full', 'inset', 'wavy'] },
11
+ orientation: { control: 'select', options: ['horizontal', 'vertical'] }
12
+ },
13
+ args: {
14
+ variant: 'full',
15
+ orientation: 'horizontal'
16
+ }
17
+ });
18
+ </script>
19
+
20
+ <Story name="Playground">
21
+ {#snippet template(args)}
22
+ <div class="w-96">
23
+ <Divider variant={args.variant} orientation={args.orientation} />
24
+ </div>
25
+ {/snippet}
26
+ </Story>
27
+
28
+ <Story name="Full" asChild>
29
+ <div class="w-96">
30
+ <Divider variant="full" />
31
+ </div>
32
+ </Story>
33
+
34
+ <Story name="Inset" asChild>
35
+ <div class="w-96">
36
+ <Divider variant="inset" />
37
+ </div>
38
+ </Story>
39
+
40
+ <Story name="Wavy" asChild>
41
+ <div class="w-96">
42
+ <Divider variant="wavy" />
43
+ </div>
44
+ </Story>
45
+
46
+ <Story name="With Label" asChild>
47
+ <div class="w-96">
48
+ <Divider>
49
+ {#snippet children()}
50
+ OR
51
+ {/snippet}
52
+ </Divider>
53
+ </div>
54
+ </Story>
55
+
56
+ <Story name="Vertical" asChild>
57
+ <div class="flex h-24 items-center gap-4">
58
+ <Body>Left</Body>
59
+ <Divider orientation="vertical" />
60
+ <Body>Right</Body>
61
+ </div>
62
+ </Story>
@@ -0,0 +1,19 @@
1
+ import Divider from './Divider.svelte';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const Divider: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type Divider = InstanceType<typeof Divider>;
19
+ export default Divider;
@@ -9,7 +9,7 @@ Powered by bits-ui for accessibility.
9
9
  @see https://m3.material.io/components/divider/overview
10
10
  -->
11
11
  <script lang="ts">
12
- import { Separator } from 'bits-ui';
12
+ import { Separator, type SeparatorRootProps } from 'bits-ui';
13
13
  import clsx from 'clsx';
14
14
  import { hr } from './theme.js';
15
15
  import type { HrProps } from './types.js';
@@ -33,7 +33,6 @@ Powered by bits-ui for accessibility.
33
33
  );
34
34
  let insetClass = $derived(variant === 'inset' ? 'mr-2 ml-4' : '');
35
35
 
36
- let host = $state<HTMLDivElement | null>(null);
37
36
  let width = $state(0);
38
37
  let waveHeight = 12;
39
38
  let waveThickness = 1;
@@ -43,23 +42,20 @@ Powered by bits-ui for accessibility.
43
42
  let right = $derived(Math.max(left, width - waveThickness * 0.5));
44
43
  let wavePath = $derived(width > 0 ? linear(waveAmp, waveCenter, left, right, 0) : '');
45
44
 
46
- $effect(() => {
47
- if (variant !== 'wavy' || !host || typeof window === 'undefined') return;
45
+ function trackWidth(node: HTMLElement) {
48
46
  const observer = new ResizeObserver((entries) => {
49
47
  const entry = entries[0];
50
48
  width = Math.max(0, entry?.contentRect.width ?? 0);
51
49
  });
52
- observer.observe(host);
53
- return () => {
54
- observer.disconnect();
55
- };
56
- });
50
+ observer.observe(node);
51
+ return () => observer.disconnect();
52
+ }
57
53
  </script>
58
54
 
59
55
  {#if variant === 'wavy'}
60
56
  <div
61
57
  {...restProps}
62
- bind:this={host}
58
+ {@attach trackWidth}
63
59
  class={clsx('w-full', insetClass, className)}
64
60
  role={decorative ? 'none' : 'separator'}
65
61
  aria-orientation={orientation}
@@ -96,5 +92,10 @@ Powered by bits-ui for accessibility.
96
92
  <div class={lineClass}></div>
97
93
  </div>
98
94
  {:else}
99
- <Separator.Root {...restProps} {orientation} {decorative} class={clsx(styling, className)} />
95
+ <Separator.Root
96
+ {...restProps as SeparatorRootProps}
97
+ {orientation}
98
+ {decorative}
99
+ class={clsx(styling, className)}
100
+ />
100
101
  {/if}
@@ -1,11 +1,11 @@
1
- import type { SeparatorRootProps } from 'bits-ui';
1
+ import type { DivAttrs } from '../../../utils/types.js';
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HrVariants } from './theme.js';
4
4
  /**
5
5
  * Props for the Divider component.
6
6
  * Dividers are thin lines that group content in lists and layouts.
7
7
  */
8
- export interface HrProps extends HrVariants, Omit<SeparatorRootProps, 'children' | 'orientation'> {
8
+ export interface HrProps extends HrVariants, DivAttrs {
9
9
  /**
10
10
  * Optional content to be rendered within the divider (horizontal only).
11
11
  * If provided, the divider will show the content centered between two lines.
@@ -24,4 +24,9 @@ export interface HrProps extends HrVariants, Omit<SeparatorRootProps, 'children'
24
24
  * @default 'full'
25
25
  */
26
26
  variant?: HrVariants['variant'];
27
+ /**
28
+ * Whether the separator is decorative and should be hidden from assistive technologies.
29
+ * @default true
30
+ */
31
+ decorative?: boolean;
27
32
  }
@@ -62,7 +62,6 @@ SupportingPane provides a layout with a main content area and a supporting side
62
62
  mobileTriggerClass
63
63
  }: SupportingPaneProps = $props();
64
64
 
65
- let entered = $state(false);
66
65
  let supportingVisible = $state(true);
67
66
  let mobileSheetOpen = $state(false);
68
67
  let isLargeScreen = $state(false);
@@ -140,7 +139,6 @@ SupportingPane provides a layout with a main content area and a supporting side
140
139
  class={supportingCls({
141
140
  class: clsx(supportingClass, inlineSupportingClass)
142
141
  })}
143
- data-entered={entered}
144
142
  data-position={position}
145
143
  >
146
144
  {@render supporting()}
@@ -88,6 +88,7 @@ In mobile (navbar) mode the same item renders as a bottom-bar tab.
88
88
  <a
89
89
  {...props}
90
90
  {...rest}
91
+ href={hrefValue}
91
92
  {target}
92
93
  {rel}
93
94
  aria-disabled={ariaDisabled}
@@ -30,7 +30,7 @@ Use TabContent for content-panel tabs; omit it for navigation (href) tabs.
30
30
 
31
31
  <Tabs.Root bind:value {onValueChange} {activationMode}>
32
32
  <Tabs.List class={base()}>
33
- {#each items as item}
33
+ {#each items as item (item)}
34
34
  <Tab {...item} {variant} />
35
35
  {/each}
36
36
 
@@ -33,10 +33,11 @@
33
33
  <Story name="Playground">
34
34
  {#snippet template(args)}
35
35
  <div class="flex flex-col gap-4">
36
- <TabHolder {...args} {items} />
37
- <TabContent value="flights"><Body>Flights panel content.</Body></TabContent>
38
- <TabContent value="trips"><Body>Trips panel content.</Body></TabContent>
39
- <TabContent value="explore"><Body>Explore panel content.</Body></TabContent>
36
+ <TabHolder {...args} {items}>
37
+ <TabContent value="flights"><Body>Flights panel content.</Body></TabContent>
38
+ <TabContent value="trips"><Body>Trips panel content.</Body></TabContent>
39
+ <TabContent value="explore"><Body>Explore panel content.</Body></TabContent>
40
+ </TabHolder>
40
41
  </div>
41
42
  {/snippet}
42
43
  </Story>
@@ -24,6 +24,7 @@ It follows the Material 3 design for time input pickers.
24
24
 
25
25
  const cls = timepicker();
26
26
 
27
+ // svelte-ignore state_referenced_locally (timeValue is a one-time editable seed from `time`, then mutated locally via bind:value)
27
28
  let timeValue = $state<Time>(parseTime(time?.includes(':') ? time : '00:00'));
28
29
 
29
30
  const output = $derived(
@@ -120,7 +120,8 @@
120
120
 
121
121
  document.addEventListener('pointerdown', (e) => {
122
122
  if (e.button !== 0) return;
123
- const layer = (e.target as Element).closest('.m3-layer');
123
+ if (!(e.target instanceof Element)) return;
124
+ const layer = e.target.closest('.m3-layer');
124
125
  if (!layer || !isEnabled(layer)) return;
125
126
  const rect = layer.getBoundingClientRect();
126
127
  const cancel = createRippleSvg(
@@ -142,8 +143,8 @@
142
143
 
143
144
  document.addEventListener('keydown', (e) => {
144
145
  if (e.repeat) return;
145
- const target = e.target as Element;
146
- const layer = target.closest('.m3-layer');
146
+ if (!(e.target instanceof Element)) return;
147
+ const layer = e.target.closest('.m3-layer');
147
148
  if (!layer || !isEnabled(layer)) return;
148
149
  const isActivate = e.key === 'Enter' || (e.key === ' ' && layer.tagName === 'BUTTON');
149
150
  if (!isActivate) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noxlovette/material",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {