|
| 1 | +import { Tokens } from "../theme/tokens.slint"; |
| 2 | +import { Icon } from "icon.slint"; |
| 3 | +import { LucidePaths } from "lucide-paths.slint"; |
| 4 | + |
| 5 | +// Single show / hide section. Like Accordion but for a standalone block — the |
| 6 | +// trigger row toggles the visibility of `@children`. The chevron flips on |
| 7 | +// open. Useful for filter sections, optional settings, inline FAQ rows. |
| 8 | +export component Collapsible inherits Rectangle { |
| 9 | + // Trigger label shown next to the chevron. |
| 10 | + in property <string> title; |
| 11 | + // Two-way; when true, `@children` are visible. |
| 12 | + in-out property <bool> open: false; |
| 13 | + // Fired with the new `open` value after a toggle click. |
| 14 | + callback toggled(bool); |
| 15 | + |
| 16 | + background: transparent; |
| 17 | + |
| 18 | + VerticalLayout { |
| 19 | + spacing: 0; |
| 20 | + |
| 21 | + // Trigger row — click anywhere to toggle. |
| 22 | + Rectangle { |
| 23 | + height: 40px; |
| 24 | + border-radius: Tokens.radius-sm; |
| 25 | + background: trigger-touch.has-hover |
| 26 | + ? Tokens.color-surface-1 : transparent; |
| 27 | + HorizontalLayout { |
| 28 | + padding-left: 12px; |
| 29 | + padding-right: 12px; |
| 30 | + spacing: 8px; |
| 31 | + Text { |
| 32 | + text: root.title; |
| 33 | + color: Tokens.color-foreground; |
| 34 | + font-size: Tokens.typography-body-size; |
| 35 | + font-weight: Tokens.typography-weight-semibold; |
| 36 | + vertical-alignment: center; |
| 37 | + horizontal-stretch: 1; |
| 38 | + } |
| 39 | + VerticalLayout { |
| 40 | + alignment: center; |
| 41 | + Icon { |
| 42 | + commands: root.open |
| 43 | + ? LucidePaths.chevron-up |
| 44 | + : LucidePaths.chevron-down; |
| 45 | + size: 14px; |
| 46 | + tint: Tokens.color-muted-foreground; |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + trigger-touch := TouchArea { |
| 51 | + mouse-cursor: pointer; |
| 52 | + clicked => { |
| 53 | + root.open = !root.open; |
| 54 | + root.toggled(root.open); |
| 55 | + } |
| 56 | + } |
| 57 | + animate background { |
| 58 | + duration: Tokens.motion-fast; |
| 59 | + easing: Tokens.motion-easing-standard; |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + // Body — `@children` must live at top-level (Slint forbids it inside |
| 64 | + // `if`), so we toggle `visible` + collapse the height instead. |
| 65 | + body := VerticalLayout { |
| 66 | + visible: root.open; |
| 67 | + min-height: root.open ? 0px : 0px; |
| 68 | + max-height: root.open ? body.preferred-height : 0px; |
| 69 | + padding-top: 8px; |
| 70 | + padding-bottom: 8px; |
| 71 | + padding-left: 12px; |
| 72 | + padding-right: 12px; |
| 73 | + spacing: 8px; |
| 74 | + @children |
| 75 | + } |
| 76 | + } |
| 77 | +} |
0 commit comments