@marianmeres/stuic 2.33.2 → 2.36.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.
@@ -0,0 +1,103 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+
4
+ export interface Props {
5
+ /** Content to display */
6
+ children: Snippet;
7
+ /** Number of lines to show when collapsed (default: 1) */
8
+ lines?: number;
9
+ /** Expanded state (bindable) */
10
+ expanded?: boolean;
11
+ /** Collapsed indicator character (default: "↓") */
12
+ collapsedIndicator?: string;
13
+ /** Expanded indicator character (default: "↑") */
14
+ expandedIndicator?: string;
15
+ /** Container class */
16
+ class?: string;
17
+ /** Content wrapper class */
18
+ classContent?: string;
19
+ /** Toggle button class */
20
+ classToggle?: string;
21
+ /** Opacity class for toggle button (default: "opacity-70") */
22
+ toggleOpacity?: string;
23
+ /** Bind reference to container element */
24
+ el?: HTMLDivElement;
25
+ }
26
+ </script>
27
+
28
+ <script lang="ts">
29
+ import { twMerge } from "../../utils/tw-merge.js";
30
+
31
+ let {
32
+ children,
33
+ lines = 1,
34
+ expanded = $bindable(false),
35
+ collapsedIndicator = "↓",
36
+ expandedIndicator = "↑",
37
+ class: classProp,
38
+ classContent,
39
+ classToggle,
40
+ toggleOpacity = "opacity-75",
41
+ el = $bindable(),
42
+ }: Props = $props();
43
+
44
+ let contentEl: HTMLDivElement | undefined;
45
+ let containerWidth = $state(0);
46
+ let needsCollapse = $state(false);
47
+
48
+ $effect(() => {
49
+ // Only measure when collapsed (line-clamp applied) to detect if truncation is needed
50
+ // containerWidth dependency ensures re-measurement on resize
51
+ if (contentEl && !expanded && containerWidth) {
52
+ needsCollapse = contentEl.scrollHeight > contentEl.clientHeight;
53
+ }
54
+ });
55
+
56
+ // normalize, range validation
57
+ let _lines = $derived.by(() => {
58
+ const l = Math.abs(lines);
59
+ return l > 10 ? 10 : l;
60
+ });
61
+ </script>
62
+
63
+ <div
64
+ bind:this={el}
65
+ bind:clientWidth={containerWidth}
66
+ class={twMerge("stuic-collapsible", classProp)}
67
+ >
68
+ <div class="flex items-end">
69
+ <div
70
+ bind:this={contentEl}
71
+ class={twMerge("flex-1", !expanded && `line-clamp-${_lines}`, classContent)}
72
+ >
73
+ {@render children()}
74
+ </div>
75
+ {#if needsCollapse}
76
+ <button
77
+ type="button"
78
+ class={twMerge(
79
+ toggleOpacity,
80
+ "hover:opacity-100 cursor-pointer px-2 py-1 -my-1 -mr-2",
81
+ classToggle
82
+ )}
83
+ onclick={() => (expanded = !expanded)}
84
+ >
85
+ {expanded ? expandedIndicator : collapsedIndicator}
86
+ </button>
87
+ {/if}
88
+ </div>
89
+ </div>
90
+
91
+ <!--
92
+ DO NOT REMOVE: Food for TW compiler
93
+ line-clamp-1
94
+ line-clamp-2
95
+ line-clamp-3
96
+ line-clamp-4
97
+ line-clamp-5
98
+ line-clamp-6
99
+ line-clamp-7
100
+ line-clamp-8
101
+ line-clamp-9
102
+ line-clamp-10
103
+ -->
@@ -0,0 +1,26 @@
1
+ import type { Snippet } from "svelte";
2
+ export interface Props {
3
+ /** Content to display */
4
+ children: Snippet;
5
+ /** Number of lines to show when collapsed (default: 1) */
6
+ lines?: number;
7
+ /** Expanded state (bindable) */
8
+ expanded?: boolean;
9
+ /** Collapsed indicator character (default: "↓") */
10
+ collapsedIndicator?: string;
11
+ /** Expanded indicator character (default: "↑") */
12
+ expandedIndicator?: string;
13
+ /** Container class */
14
+ class?: string;
15
+ /** Content wrapper class */
16
+ classContent?: string;
17
+ /** Toggle button class */
18
+ classToggle?: string;
19
+ /** Opacity class for toggle button (default: "opacity-70") */
20
+ toggleOpacity?: string;
21
+ /** Bind reference to container element */
22
+ el?: HTMLDivElement;
23
+ }
24
+ declare const Collapsible: import("svelte").Component<Props, {}, "el" | "expanded">;
25
+ type Collapsible = ReturnType<typeof Collapsible>;
26
+ export default Collapsible;
@@ -0,0 +1,81 @@
1
+ # Collapsible
2
+
3
+ A component that truncates content to a specified number of lines with an expand/collapse toggle. Automatically detects if truncation is needed and only shows the toggle when content overflows.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ | -------------------- | --------------- | ------------- | ------------------------------------------------ |
9
+ | `children` | `Snippet` | - | Content to display |
10
+ | `lines` | `number` | `1` | Number of lines to show when collapsed |
11
+ | `expanded` | `boolean` | `false` | Expanded state (bindable) |
12
+ | `collapsedIndicator` | `string` | `"↓"` | Character/text shown when collapsed |
13
+ | `expandedIndicator` | `string` | `"↑"` | Character/text shown when expanded |
14
+ | `class` | `string` | - | Container element class |
15
+ | `classContent` | `string` | - | Content wrapper class |
16
+ | `classToggle` | `string` | - | Toggle button class |
17
+ | `toggleOpacity` | `string` | `"opacity-70"`| Opacity class for toggle button |
18
+ | `el` | `HTMLDivElement`| - | Bind reference to container element |
19
+
20
+ ## Usage
21
+
22
+ ### Basic Usage
23
+
24
+ ```svelte
25
+ <script>
26
+ import { Collapsible } from 'stuic';
27
+ </script>
28
+
29
+ <Collapsible>
30
+ This is a long text that will be truncated to one line with an ellipsis
31
+ and a toggle button to expand it.
32
+ </Collapsible>
33
+ ```
34
+
35
+ ### Multiple Lines
36
+
37
+ ```svelte
38
+ <Collapsible lines={3}>
39
+ This content will be truncated to 3 lines before showing the expand toggle.
40
+ Add more content here to see the effect.
41
+ </Collapsible>
42
+ ```
43
+
44
+ ### Custom Indicators
45
+
46
+ ```svelte
47
+ <Collapsible collapsedIndicator="▼" expandedIndicator="▲">
48
+ Content with custom expand/collapse indicators.
49
+ </Collapsible>
50
+ ```
51
+
52
+ ### Controlled State
53
+
54
+ ```svelte
55
+ <script>
56
+ import { Collapsible } from 'stuic';
57
+
58
+ let expanded = $state(false);
59
+ </script>
60
+
61
+ <button onclick={() => expanded = !expanded}>
62
+ Toggle externally
63
+ </button>
64
+
65
+ <Collapsible bind:expanded>
66
+ This collapsible can be controlled from outside.
67
+ </Collapsible>
68
+ ```
69
+
70
+ ### Custom Styling
71
+
72
+ ```svelte
73
+ <Collapsible
74
+ class="bg-gray-100 p-4 rounded"
75
+ classContent="text-sm text-gray-600"
76
+ classToggle="text-blue-500 font-bold"
77
+ toggleOpacity="opacity-100"
78
+ >
79
+ Styled collapsible content.
80
+ </Collapsible>
81
+ ```
@@ -0,0 +1 @@
1
+ export { default as Collapsible, type Props as CollapsibleProps, } from "./Collapsible.svelte";
@@ -0,0 +1 @@
1
+ export { default as Collapsible, } from "./Collapsible.svelte";
@@ -3,6 +3,7 @@
3
3
  import { slide } from "svelte/transition";
4
4
  import type { ValidationResult } from "../../../actions/validate.svelte.js";
5
5
  import { twMerge } from "../../../utils/tw-merge.js";
6
+ import { Collapsible } from "../../Collapsible/index.js";
6
7
  import Thc, { isTHCNotEmpty, type THC } from "../../Thc/Thc.svelte";
7
8
 
8
9
  type SnippetWithId = Snippet<[{ id: string }]>;
@@ -33,8 +34,11 @@
33
34
  classInputBoxWrap?: string;
34
35
  classInputBoxWrapInvalid?: string;
35
36
  classDescBox?: string;
37
+ classDescBoxToggle?: string;
36
38
  classBelowBox?: string;
37
39
  classValidationBox?: string;
40
+ descriptionCollapsible?: boolean;
41
+ descriptionDefaultExpanded?: boolean;
38
42
  style?: string;
39
43
  }
40
44
  let {
@@ -64,8 +68,11 @@
64
68
  classInputBoxWrap,
65
69
  classInputBoxWrapInvalid,
66
70
  classDescBox,
71
+ classDescBoxToggle,
67
72
  classBelowBox,
68
73
  classValidationBox,
74
+ descriptionCollapsible = true,
75
+ descriptionDefaultExpanded = false,
69
76
  style,
70
77
  }: Props = $props();
71
78
 
@@ -214,7 +221,16 @@
214
221
  classDescBox
215
222
  )}
216
223
  >
217
- {@render snippetOrThc({ id, value: description })}
224
+ {#if descriptionCollapsible}
225
+ <Collapsible
226
+ expanded={descriptionDefaultExpanded}
227
+ classToggle={classDescBoxToggle}
228
+ >
229
+ {@render snippetOrThc({ id, value: description })}
230
+ </Collapsible>
231
+ {:else}
232
+ {@render snippetOrThc({ id, value: description })}
233
+ {/if}
218
234
  </div>
219
235
  {/if}
220
236
 
@@ -28,8 +28,11 @@ interface Props {
28
28
  classInputBoxWrap?: string;
29
29
  classInputBoxWrapInvalid?: string;
30
30
  classDescBox?: string;
31
+ classDescBoxToggle?: string;
31
32
  classBelowBox?: string;
32
33
  classValidationBox?: string;
34
+ descriptionCollapsible?: boolean;
35
+ descriptionDefaultExpanded?: boolean;
33
36
  style?: string;
34
37
  }
35
38
  declare const InputWrap: import("svelte").Component<Props, {}, "">;
package/dist/index.d.ts CHANGED
@@ -28,6 +28,7 @@ export * from "./components/AvatarInitials/index.js";
28
28
  export * from "./components/Backdrop/index.js";
29
29
  export * from "./components/Button/index.js";
30
30
  export * from "./components/ButtonGroupRadio/index.js";
31
+ export * from "./components/Collapsible/index.js";
31
32
  export * from "./components/ColorScheme/index.js";
32
33
  export * from "./components/CommandMenu/index.js";
33
34
  export * from "./components/DismissibleMessage/index.js";
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ export * from "./components/AvatarInitials/index.js";
29
29
  export * from "./components/Backdrop/index.js";
30
30
  export * from "./components/Button/index.js";
31
31
  export * from "./components/ButtonGroupRadio/index.js";
32
+ export * from "./components/Collapsible/index.js";
32
33
  export * from "./components/ColorScheme/index.js";
33
34
  export * from "./components/CommandMenu/index.js";
34
35
  export * from "./components/DismissibleMessage/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.33.2",
3
+ "version": "2.36.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",