@insymetri/styleguide 0.1.19 → 0.1.20

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,53 @@
1
+ <script lang="ts">
2
+ import type {Snippet} from 'svelte'
3
+ import {cn} from '../utils/cn'
4
+ import {IIIcon} from '../IIIcon'
5
+
6
+ type Props = {
7
+ expanded: boolean
8
+ onExpandedChange?: (expanded: boolean) => void
9
+ trigger: Snippet<[{expanded: boolean}]>
10
+ children: Snippet
11
+ disabled?: boolean
12
+ class?: string
13
+ }
14
+
15
+ let {
16
+ expanded = $bindable(false),
17
+ onExpandedChange,
18
+ trigger,
19
+ children,
20
+ disabled = false,
21
+ class: className,
22
+ }: Props = $props()
23
+
24
+ function toggle() {
25
+ if (disabled) return
26
+ expanded = !expanded
27
+ onExpandedChange?.(expanded)
28
+ }
29
+ </script>
30
+
31
+ <div class={cn('border border-primary rounded-10 overflow-hidden', className)}>
32
+ <button
33
+ type="button"
34
+ class="flex items-center gap-8 w-full bg-transparent border-none px-16 py-12 cursor-default text-left font-inherit hover:bg-background"
35
+ style="transition: background-color var(--transition-fast)"
36
+ onclick={toggle}
37
+ aria-expanded={expanded}
38
+ {disabled}
39
+ >
40
+ <IIIcon
41
+ iconName="caret-right"
42
+ class={cn('w-14 h-14 text-secondary shrink-0 transition-transform duration-fast', expanded && 'rotate-90')}
43
+ />
44
+ <div class="flex-1 min-w-0">
45
+ {@render trigger({expanded})}
46
+ </div>
47
+ </button>
48
+ {#if expanded}
49
+ <div class="border-t border-primary">
50
+ {@render children()}
51
+ </div>
52
+ {/if}
53
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Props = {
3
+ expanded: boolean;
4
+ onExpandedChange?: (expanded: boolean) => void;
5
+ trigger: Snippet<[{
6
+ expanded: boolean;
7
+ }]>;
8
+ children: Snippet;
9
+ disabled?: boolean;
10
+ class?: string;
11
+ };
12
+ declare const IIAccordion: import("svelte").Component<Props, {}, "expanded">;
13
+ type IIAccordion = ReturnType<typeof IIAccordion>;
14
+ export default IIAccordion;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import IIAccordion from './IIAccordion.svelte'
3
+
4
+ let basicExpanded = $state(false)
5
+ let tier1Expanded = $state(true)
6
+ let tier2Expanded = $state(false)
7
+ let tier3Expanded = $state(false)
8
+ </script>
9
+
10
+ <div class="flex flex-col gap-32">
11
+ <!-- Basic -->
12
+ <section>
13
+ <h2 class="text-default-emphasis text-primary mb-8">Basic</h2>
14
+ <p class="text-small text-secondary mb-12">Click to expand and collapse content.</p>
15
+ <div class="max-w-500">
16
+ <IIAccordion bind:expanded={basicExpanded}>
17
+ {#snippet trigger({expanded})}
18
+ <span class="text-small font-semibold text-body">Section Title</span>
19
+ {/snippet}
20
+ <div class="p-16">
21
+ <p class="text-small text-secondary m-0">
22
+ This is the expandable content area. It can contain any content — forms, lists, details, etc.
23
+ </p>
24
+ </div>
25
+ </IIAccordion>
26
+ </div>
27
+ </section>
28
+
29
+ <!-- Tier List Example -->
30
+ <section>
31
+ <h2 class="text-default-emphasis text-primary mb-8">Tier List (Real-world Example)</h2>
32
+ <p class="text-small text-secondary mb-12">Multiple accordions used as expandable tier rows.</p>
33
+ <div class="max-w-600 flex flex-col gap-4">
34
+ <IIAccordion bind:expanded={tier1Expanded}>
35
+ {#snippet trigger()}
36
+ <div class="flex items-center gap-12">
37
+ <span class="font-semibold text-body text-default">Standard</span>
38
+ <span class="text-small text-secondary">8.99%</span>
39
+ <span class="text-small text-secondary">36 mo</span>
40
+ <span class="text-small text-secondary">$1,000 – $25,000</span>
41
+ </div>
42
+ {/snippet}
43
+ <div class="p-16 bg-background">
44
+ <p class="text-small text-secondary m-0">Tier configuration form goes here.</p>
45
+ </div>
46
+ </IIAccordion>
47
+
48
+ <IIAccordion bind:expanded={tier2Expanded}>
49
+ {#snippet trigger()}
50
+ <div class="flex items-center gap-12">
51
+ <span class="font-semibold text-body text-default">Premium</span>
52
+ <span class="text-small text-secondary">6.49%</span>
53
+ <span class="text-small text-secondary">48 mo</span>
54
+ <span class="text-small text-secondary">$10,000 – $50,000</span>
55
+ </div>
56
+ {/snippet}
57
+ <div class="p-16 bg-background">
58
+ <p class="text-small text-secondary m-0">Tier configuration form goes here.</p>
59
+ </div>
60
+ </IIAccordion>
61
+
62
+ <IIAccordion bind:expanded={tier3Expanded}>
63
+ {#snippet trigger()}
64
+ <div class="flex items-center gap-12">
65
+ <span class="font-semibold text-body text-default">Economy</span>
66
+ <span class="text-small text-secondary">12.99%</span>
67
+ <span class="text-small text-secondary">24 mo</span>
68
+ <span class="text-small text-secondary">$500 – $10,000</span>
69
+ </div>
70
+ {/snippet}
71
+ <div class="p-16 bg-background">
72
+ <p class="text-small text-secondary m-0">Tier configuration form goes here.</p>
73
+ </div>
74
+ </IIAccordion>
75
+ </div>
76
+ </section>
77
+
78
+ <!-- Disabled -->
79
+ <section>
80
+ <h2 class="text-default-emphasis text-primary mb-8">Disabled</h2>
81
+ <div class="max-w-500">
82
+ <IIAccordion expanded={false} disabled>
83
+ {#snippet trigger()}
84
+ <span class="text-small font-semibold text-body">Disabled Section</span>
85
+ {/snippet}
86
+ <div class="p-16">
87
+ <p class="text-small text-secondary m-0">This content won't be reachable.</p>
88
+ </div>
89
+ </IIAccordion>
90
+ </div>
91
+ </section>
92
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const IIAccordionStories: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type IIAccordionStories = ReturnType<typeof IIAccordionStories>;
3
+ export default IIAccordionStories;
@@ -0,0 +1 @@
1
+ export { default as IIAccordion } from './IIAccordion.svelte';
@@ -0,0 +1 @@
1
+ export { default as IIAccordion } from './IIAccordion.svelte';
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ import {cn} from '../utils/cn'
3
+
4
+ type Item = {
5
+ label: string
6
+ value: string
7
+ variant?: 'default' | 'danger'
8
+ }
9
+
10
+ type Props = {
11
+ items: Item[]
12
+ value: string
13
+ onValueChange?: (value: string) => void
14
+ size?: 'sm' | 'md'
15
+ class?: string
16
+ }
17
+
18
+ let {items, value = $bindable(), onValueChange, size = 'sm', class: className}: Props = $props()
19
+
20
+ const sizeClasses = {
21
+ sm: 'px-12 py-4 text-small',
22
+ md: 'px-16 py-6 text-default',
23
+ } as const
24
+
25
+ function select(v: string) {
26
+ value = v
27
+ onValueChange?.(v)
28
+ }
29
+
30
+ function getActiveClass(item: Item): string {
31
+ if (item.variant === 'danger') return 'bg-error text-inverse'
32
+ return 'bg-primary text-inverse'
33
+ }
34
+ </script>
35
+
36
+ <div class={cn('inline-flex border border-primary rounded-control overflow-hidden', className)} role="group">
37
+ {#each items as item (item.value)}
38
+ {@const isActive = value === item.value}
39
+ <button
40
+ type="button"
41
+ class={cn(
42
+ 'border-none font-medium cursor-default whitespace-nowrap text-center not-first:border-l not-first:border-primary',
43
+ sizeClasses[size],
44
+ isActive ? getActiveClass(item) : 'bg-surface text-secondary hover:bg-background'
45
+ )}
46
+ style="transition: all var(--transition-fast)"
47
+ aria-pressed={isActive}
48
+ onclick={() => select(item.value)}
49
+ >
50
+ {item.label}
51
+ </button>
52
+ {/each}
53
+ </div>
@@ -0,0 +1,15 @@
1
+ type Item = {
2
+ label: string;
3
+ value: string;
4
+ variant?: 'default' | 'danger';
5
+ };
6
+ type Props = {
7
+ items: Item[];
8
+ value: string;
9
+ onValueChange?: (value: string) => void;
10
+ size?: 'sm' | 'md';
11
+ class?: string;
12
+ };
13
+ declare const IISegmentedControl: import("svelte").Component<Props, {}, "value">;
14
+ type IISegmentedControl = ReturnType<typeof IISegmentedControl>;
15
+ export default IISegmentedControl;
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ import IISegmentedControl from './IISegmentedControl.svelte'
3
+
4
+ let mode = $state('allow')
5
+ let editorTab = $state('edit')
6
+ let size = $state('monthly')
7
+ </script>
8
+
9
+ <div class="flex flex-col gap-32">
10
+ <!-- Basic -->
11
+ <section>
12
+ <h2 class="text-default-emphasis text-primary mb-8">Basic</h2>
13
+ <p class="text-small text-secondary mb-12">Two-option segmented control for mode switching.</p>
14
+ <IISegmentedControl
15
+ items={[
16
+ {label: 'Allow list', value: 'allow'},
17
+ {label: 'Block list', value: 'block'},
18
+ ]}
19
+ bind:value={mode}
20
+ />
21
+ <p class="text-small text-secondary mt-8">Selected: {mode}</p>
22
+ </section>
23
+
24
+ <!-- With danger variant -->
25
+ <section>
26
+ <h2 class="text-default-emphasis text-primary mb-8">Danger Variant</h2>
27
+ <p class="text-small text-secondary mb-12">Items can use a danger variant for the active state.</p>
28
+ <IISegmentedControl
29
+ items={[
30
+ {label: 'Allow list', value: 'allow'},
31
+ {label: 'Block list', value: 'block', variant: 'danger'},
32
+ ]}
33
+ bind:value={mode}
34
+ />
35
+ </section>
36
+
37
+ <!-- Editor tabs -->
38
+ <section>
39
+ <h2 class="text-default-emphasis text-primary mb-8">Editor Tabs</h2>
40
+ <p class="text-small text-secondary mb-12">Used as an Edit/Preview mode switcher.</p>
41
+ <IISegmentedControl
42
+ items={[
43
+ {label: 'Edit', value: 'edit'},
44
+ {label: 'Preview', value: 'preview'},
45
+ ]}
46
+ bind:value={editorTab}
47
+ />
48
+ <p class="text-small text-secondary mt-8">Mode: {editorTab}</p>
49
+ </section>
50
+
51
+ <!-- Three options -->
52
+ <section>
53
+ <h2 class="text-default-emphasis text-primary mb-8">Three Options</h2>
54
+ <p class="text-small text-secondary mb-12">Supports more than two segments.</p>
55
+ <IISegmentedControl
56
+ items={[
57
+ {label: 'Monthly', value: 'monthly'},
58
+ {label: 'Quarterly', value: 'quarterly'},
59
+ {label: 'Annual', value: 'annual'},
60
+ ]}
61
+ bind:value={size}
62
+ />
63
+ </section>
64
+
65
+ <!-- Sizes -->
66
+ <section>
67
+ <h2 class="text-default-emphasis text-primary mb-8">Sizes</h2>
68
+ <div class="flex items-center gap-16">
69
+ <div class="flex flex-col items-start gap-4">
70
+ <span class="text-tiny text-secondary">sm (default)</span>
71
+ <IISegmentedControl
72
+ size="sm"
73
+ items={[
74
+ {label: 'On', value: 'on'},
75
+ {label: 'Off', value: 'off'},
76
+ ]}
77
+ value="on"
78
+ />
79
+ </div>
80
+ <div class="flex flex-col items-start gap-4">
81
+ <span class="text-tiny text-secondary">md</span>
82
+ <IISegmentedControl
83
+ size="md"
84
+ items={[
85
+ {label: 'On', value: 'on'},
86
+ {label: 'Off', value: 'off'},
87
+ ]}
88
+ value="on"
89
+ />
90
+ </div>
91
+ </div>
92
+ </section>
93
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const IISegmentedControlStories: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type IISegmentedControlStories = ReturnType<typeof IISegmentedControlStories>;
3
+ export default IISegmentedControlStories;
@@ -0,0 +1 @@
1
+ export { default as IISegmentedControl } from './IISegmentedControl.svelte';
@@ -0,0 +1 @@
1
+ export { default as IISegmentedControl } from './IISegmentedControl.svelte';
@@ -11,15 +11,24 @@
11
11
  steps: Step[]
12
12
  currentStep: number
13
13
  onStepClick?: (index: number) => void
14
+ completedSteps?: number[] | Set<number>
14
15
  class?: string
15
16
  }
16
17
 
17
- let {steps, currentStep, onStepClick, class: className}: Props = $props()
18
+ let {steps, currentStep, onStepClick, completedSteps, class: className}: Props = $props()
19
+
20
+ function isStepCompleted(index: number): boolean {
21
+ if (completedSteps) {
22
+ if (completedSteps instanceof Set) return completedSteps.has(index)
23
+ return completedSteps.includes(index)
24
+ }
25
+ return index < currentStep
26
+ }
18
27
  </script>
19
28
 
20
29
  <nav class={cn('flex items-center w-full', className)} aria-label="Progress">
21
30
  {#each steps as step, i}
22
- {@const isCompleted = i < currentStep}
31
+ {@const isCompleted = isStepCompleted(i)}
23
32
  {@const isActive = i === currentStep}
24
33
  {@const isClickable = !!onStepClick && (isCompleted || isActive)}
25
34
 
@@ -6,6 +6,7 @@ type Props = {
6
6
  steps: Step[];
7
7
  currentStep: number;
8
8
  onStepClick?: (index: number) => void;
9
+ completedSteps?: number[] | Set<number>;
9
10
  class?: string;
10
11
  };
11
12
  declare const IIStepper: import("svelte").Component<Props, {}, "">;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import IIStepper from './IIStepper.svelte'
3
+
4
+ const steps = [
5
+ {label: 'Product Info'},
6
+ {label: 'Tiers'},
7
+ {label: 'Eligibility'},
8
+ {label: 'Agreement'},
9
+ {label: 'Review'},
10
+ ]
11
+
12
+ let linearStep = $state(2)
13
+ let nonLinearStep = $state(1)
14
+ let visitedSteps = $state(new Set([0, 1, 3]))
15
+ </script>
16
+
17
+ <div class="flex flex-col gap-32">
18
+ <!-- Linear (default) -->
19
+ <section>
20
+ <h2 class="text-default-emphasis text-primary mb-8">Linear Stepper (Default)</h2>
21
+ <p class="text-small text-secondary mb-12">Steps before the current step are automatically marked as completed.</p>
22
+ <div class="max-w-600">
23
+ <IIStepper
24
+ {steps}
25
+ currentStep={linearStep}
26
+ onStepClick={i => (linearStep = i)}
27
+ />
28
+ </div>
29
+ <p class="text-small text-secondary mt-8">Current step: {linearStep}</p>
30
+ </section>
31
+
32
+ <!-- Non-linear with completedSteps -->
33
+ <section>
34
+ <h2 class="text-default-emphasis text-primary mb-8">Non-Linear with completedSteps</h2>
35
+ <p class="text-small text-secondary mb-12">
36
+ Uses <code class="font-mono text-primary">completedSteps</code> prop to mark specific steps as visited,
37
+ regardless of order. Steps 1, 2, and 4 are marked completed while currently on step 2.
38
+ </p>
39
+ <div class="max-w-600">
40
+ <IIStepper
41
+ {steps}
42
+ currentStep={nonLinearStep}
43
+ completedSteps={visitedSteps}
44
+ onStepClick={i => {
45
+ visitedSteps.add(nonLinearStep)
46
+ nonLinearStep = i
47
+ visitedSteps = visitedSteps
48
+ }}
49
+ />
50
+ </div>
51
+ <p class="text-small text-secondary mt-8">
52
+ Current: {nonLinearStep} | Completed: {[...visitedSteps].sort().join(', ')}
53
+ </p>
54
+ </section>
55
+
56
+ <!-- With descriptions -->
57
+ <section>
58
+ <h2 class="text-default-emphasis text-primary mb-8">With Descriptions</h2>
59
+ <div class="max-w-600">
60
+ <IIStepper
61
+ steps={[
62
+ {label: 'Details', description: 'Basic info'},
63
+ {label: 'Config', description: 'Settings'},
64
+ {label: 'Review', description: 'Confirm'},
65
+ ]}
66
+ currentStep={1}
67
+ />
68
+ </div>
69
+ </section>
70
+
71
+ <!-- No click handler (read-only) -->
72
+ <section>
73
+ <h2 class="text-default-emphasis text-primary mb-8">Read-Only</h2>
74
+ <p class="text-small text-secondary mb-12">Without onStepClick, steps are not interactive.</p>
75
+ <div class="max-w-600">
76
+ <IIStepper
77
+ {steps}
78
+ currentStep={3}
79
+ />
80
+ </div>
81
+ </section>
82
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const IIStepperStories: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type IIStepperStories = ReturnType<typeof IIStepperStories>;
3
+ export default IIStepperStories;
@@ -65,7 +65,7 @@
65
65
  {disabled}
66
66
  {rows}
67
67
  class={cn(
68
- 'py-5 px-12 text-small font-[family-name:var(--font-family)] text-body bg-surface border border-strong rounded-control transition-all duration-fast outline-none w-full box-border resize-none overflow-hidden min-h-16 placeholder:text-tertiary focus:border-primary focus:ring-3 focus:ring-primary disabled:bg-gray-100 disabled:text-secondary disabled:cursor-not-allowed motion-reduce:transition-none',
68
+ 'py-5 px-12 text-small font-[family-name:var(--font-family)] text-body bg-surface border border-strong rounded-[10px] transition-all duration-fast outline-none w-full box-border resize-none overflow-hidden min-h-16 placeholder:text-tertiary focus:border-primary focus:ring-3 focus:ring-primary disabled:bg-gray-100 disabled:text-secondary disabled:cursor-not-allowed motion-reduce:transition-none',
69
69
  showError && 'border-error focus:border-error focus:ring-error'
70
70
  )}
71
71
  oninput={handleInput}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { IIAccordion } from './IIAccordion';
1
2
  export { IIActionGroup } from './IIActionGroup';
2
3
  export { IIAlert } from './IIAlert';
3
4
  export { IIAsyncState } from './IIAsyncState';
@@ -25,6 +26,7 @@ export { IIMultiSelect } from './IIMultiSelect';
25
26
  export { IIOverflowActions } from './IIOverflowActions';
26
27
  export { IIPillTabs } from './IIPillTabs';
27
28
  export { IIPopover } from './IIPopover';
29
+ export { IISegmentedControl } from './IISegmentedControl';
28
30
  export { IISpinner } from './IISpinner';
29
31
  export { IIStatusBadge } from './IIStatusBadge';
30
32
  export { IIStepper } from './IIStepper';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // @insymetri/styleguide
2
2
  // Components
3
+ export { IIAccordion } from './IIAccordion';
3
4
  export { IIActionGroup } from './IIActionGroup';
4
5
  export { IIAlert } from './IIAlert';
5
6
  export { IIAsyncState } from './IIAsyncState';
@@ -27,6 +28,7 @@ export { IIMultiSelect } from './IIMultiSelect';
27
28
  export { IIOverflowActions } from './IIOverflowActions';
28
29
  export { IIPillTabs } from './IIPillTabs';
29
30
  export { IIPopover } from './IIPopover';
31
+ export { IISegmentedControl } from './IISegmentedControl';
30
32
  export { IISpinner } from './IISpinner';
31
33
  export { IIStatusBadge } from './IIStatusBadge';
32
34
  export { IIStepper } from './IIStepper';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insymetri/styleguide",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "Insymetri shared UI component library built with Svelte 5",
5
5
  "type": "module",
6
6
  "scripts": {