phlex_kit 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +210 -49
- data/THIRD_PARTY_LICENSES +104 -0
- data/app/assets/stylesheets/phlex_kit/_tokens.css +72 -0
- data/app/assets/stylesheets/phlex_kit/phlex_kit.css +1 -1
- data/app/assets/stylesheets/phlex_kit/themes/claude.css +86 -0
- data/app/assets/stylesheets/phlex_kit/themes/neutral.css +95 -0
- data/app/assets/stylesheets/phlex_kit/themes/zinc.css +85 -0
- data/app/components/phlex_kit/accordion/accordion.css +13 -0
- data/app/components/phlex_kit/accordion/accordion.rb +11 -2
- data/app/components/phlex_kit/accordion/accordion_icon.rb +1 -3
- data/app/components/phlex_kit/accordion/accordion_item.rb +5 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog.css +45 -4
- data/app/components/phlex_kit/alert_dialog/alert_dialog_action.rb +3 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog_content.rb +6 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog_media.rb +14 -0
- data/app/components/phlex_kit/attachment/attachment.css +102 -2
- data/app/components/phlex_kit/attachment/attachment.rb +18 -6
- data/app/components/phlex_kit/attachment/attachment_action.rb +1 -6
- data/app/components/phlex_kit/attachment/attachment_group.rb +21 -0
- data/app/components/phlex_kit/attachment/attachment_media.rb +13 -4
- data/app/components/phlex_kit/attachment/attachment_trigger.rb +22 -0
- data/app/components/phlex_kit/breadcrumb/breadcrumb_ellipsis.rb +1 -5
- data/app/components/phlex_kit/breadcrumb/breadcrumb_separator.rb +1 -1
- data/app/components/phlex_kit/calendar/calendar_next.rb +1 -15
- data/app/components/phlex_kit/calendar/calendar_prev.rb +1 -15
- data/app/components/phlex_kit/card/card.css +50 -14
- data/app/components/phlex_kit/card/card.rb +6 -2
- data/app/components/phlex_kit/card/card_action.rb +14 -0
- data/app/components/phlex_kit/carousel/carousel_next.rb +1 -15
- data/app/components/phlex_kit/carousel/carousel_previous.rb +1 -15
- data/app/components/phlex_kit/codeblock/codeblock.css +1 -1
- data/app/components/phlex_kit/combobox/combobox_clear_button.rb +1 -15
- data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +1 -12
- data/app/components/phlex_kit/combobox/combobox_item_indicator.rb +1 -15
- data/app/components/phlex_kit/combobox/combobox_search_input.rb +1 -14
- data/app/components/phlex_kit/combobox/combobox_trigger.rb +1 -14
- data/app/components/phlex_kit/command/command.css +2 -2
- data/app/components/phlex_kit/command/command_input.rb +1 -13
- data/app/components/phlex_kit/context_menu/context_menu_item.rb +1 -3
- data/app/components/phlex_kit/data_table/data_table_column_toggle.rb +1 -13
- data/app/components/phlex_kit/data_table/data_table_expand_toggle.rb +1 -12
- data/app/components/phlex_kit/data_table/data_table_sort_head.rb +2 -28
- data/app/components/phlex_kit/dialog/dialog.css +8 -2
- data/app/components/phlex_kit/dialog/dialog_content.rb +1 -3
- data/app/components/phlex_kit/drawer/drawer.css +1 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu.css +64 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_checkbox_item.rb +31 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_group.rb +11 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_item.rb +5 -2
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_group.rb +10 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_item.rb +31 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_shortcut.rb +10 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub.rb +11 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_content.rb +12 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_trigger.rb +18 -0
- data/app/components/phlex_kit/icon/icon.rb +51 -0
- data/app/components/phlex_kit/marker/marker.css +25 -0
- data/app/components/phlex_kit/marker/marker.rb +18 -0
- data/app/components/phlex_kit/marker/marker_content.rb +7 -0
- data/app/components/phlex_kit/marker/marker_icon.rb +7 -0
- data/app/components/phlex_kit/native_select/native_select_icon.rb +1 -10
- data/app/components/phlex_kit/navigation_menu/navigation_menu_trigger.rb +1 -5
- data/app/components/phlex_kit/pagination/pagination_ellipsis.rb +1 -5
- data/app/components/phlex_kit/select/select_item.rb +1 -11
- data/app/components/phlex_kit/select/select_trigger.rb +1 -14
- data/app/components/phlex_kit/sheet/sheet.css +1 -1
- data/app/components/phlex_kit/sheet/sheet_content.rb +1 -3
- data/app/components/phlex_kit/sidebar/sidebar.css +129 -11
- data/app/components/phlex_kit/sidebar/sidebar.rb +19 -6
- data/app/components/phlex_kit/sidebar/sidebar_group_action.rb +14 -0
- data/app/components/phlex_kit/sidebar/sidebar_group_content.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_group_label.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_input.rb +14 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_action.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_badge.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_skeleton.rb +19 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_sub.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_sub_button.rb +18 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_sub_item.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_separator.rb +14 -0
- data/app/components/phlex_kit/spinner/spinner.rb +5 -10
- data/app/components/phlex_kit/toast/toast_close.rb +1 -15
- data/app/components/phlex_kit/toast/toast_icon.rb +12 -36
- data/app/components/phlex_kit/typography/heading.rb +1 -1
- data/app/components/phlex_kit/typography/typography.css +7 -3
- data/app/javascript/phlex_kit/controllers/accordion_controller.js +17 -1
- data/app/javascript/phlex_kit/controllers/index.js +2 -0
- data/app/javascript/phlex_kit/controllers/scroll_fade_controller.js +31 -0
- data/lib/phlex_kit/configuration.rb +7 -0
- data/lib/phlex_kit/icons/lucide.rb +88 -0
- data/lib/phlex_kit/icons/phosphor.rb +88 -0
- data/lib/phlex_kit/icons/remix.rb +88 -0
- data/lib/phlex_kit/icons/tabler.rb +88 -0
- data/lib/phlex_kit/icons.rb +40 -0
- data/lib/phlex_kit/version.rb +1 -1
- data/lib/phlex_kit.rb +1 -0
- metadata +45 -8
- data/app/components/phlex_kit/stars/stars.css +0 -4
- data/app/components/phlex_kit/stars/stars.rb +0 -19
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* PhlexKit theme: Neutral — translated from a ui.shadcn.com/create export
|
|
2
|
+
(shadcn's stock neutral palette: pure grayscale, zero chroma throughout).
|
|
3
|
+
Pure token override: load AFTER the phlex_kit.css manifest and the
|
|
4
|
+
cascade re-themes every component.
|
|
5
|
+
|
|
6
|
+
Translation notes (the recipe for any shadcn/create export):
|
|
7
|
+
- background → bg; card/popover → surface; secondary/muted/accent →
|
|
8
|
+
surface-2/accent; foreground → text; secondary-foreground → text-2;
|
|
9
|
+
muted-foreground → muted; primary/primary-foreground → brand/brand-ink;
|
|
10
|
+
destructive → red; border/input/ring/chart-1..5/radius map 1:1.
|
|
11
|
+
- shadcn is light-default with a .dark class; PhlexKit themes are
|
|
12
|
+
dark-default with data-theme="light" opt-in — blocks swapped.
|
|
13
|
+
- sidebar-* maps onto the kit's optional --pk-sidebar-* overrides, set
|
|
14
|
+
only where the export differs from the shared tokens: here that's the
|
|
15
|
+
dark sidebar-primary (a distinct blue) + its ink. Like the shadcn create
|
|
16
|
+
page, the blue only shows on active items under Sidebar's menu: :solid —
|
|
17
|
+
the :default menu highlights with the accent role. shadcn/create exports
|
|
18
|
+
no green/amber — those fall through to _tokens.css defaults.
|
|
19
|
+
- dark text-2 has no shadcn source (every dark *-foreground is 0.985);
|
|
20
|
+
chart-1's 0.87 step (neutral-300) is used, matching the kit's own dark
|
|
21
|
+
text-2 weight. */
|
|
22
|
+
:root {
|
|
23
|
+
--pk-bg: oklch(0.145 0 0);
|
|
24
|
+
--pk-surface: oklch(0.205 0 0);
|
|
25
|
+
--pk-surface-2: oklch(0.269 0 0);
|
|
26
|
+
--pk-accent: oklch(0.269 0 0);
|
|
27
|
+
--pk-border: oklch(1 0 0 / 10%);
|
|
28
|
+
--pk-input: oklch(1 0 0 / 15%);
|
|
29
|
+
--pk-ring: oklch(0.556 0 0);
|
|
30
|
+
--pk-text: oklch(0.985 0 0);
|
|
31
|
+
--pk-text-2: oklch(0.87 0 0);
|
|
32
|
+
--pk-muted: oklch(0.708 0 0);
|
|
33
|
+
--pk-brand: oklch(0.922 0 0);
|
|
34
|
+
--pk-brand-ink: oklch(0.205 0 0);
|
|
35
|
+
--pk-red: oklch(0.704 0.191 22.216);
|
|
36
|
+
--pk-chart-1: oklch(0.87 0 0);
|
|
37
|
+
--pk-chart-2: oklch(0.556 0 0);
|
|
38
|
+
--pk-chart-3: oklch(0.439 0 0);
|
|
39
|
+
--pk-chart-4: oklch(0.371 0 0);
|
|
40
|
+
--pk-chart-5: oklch(0.269 0 0);
|
|
41
|
+
--pk-sidebar-primary: oklch(0.488 0.243 264.376);
|
|
42
|
+
--pk-sidebar-primary-ink: oklch(0.985 0 0);
|
|
43
|
+
--pk-radius: 0.625rem;
|
|
44
|
+
color-scheme: dark;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
:root[data-theme="light"] {
|
|
48
|
+
--pk-bg: oklch(1 0 0);
|
|
49
|
+
--pk-sidebar-primary: oklch(0.205 0 0);
|
|
50
|
+
--pk-sidebar-primary-ink: oklch(0.985 0 0);
|
|
51
|
+
--pk-surface: oklch(1 0 0);
|
|
52
|
+
--pk-surface-2: oklch(0.97 0 0);
|
|
53
|
+
--pk-accent: oklch(0.97 0 0);
|
|
54
|
+
--pk-border: oklch(0.922 0 0);
|
|
55
|
+
--pk-input: oklch(0.922 0 0);
|
|
56
|
+
--pk-ring: oklch(0.708 0 0);
|
|
57
|
+
--pk-text: oklch(0.145 0 0);
|
|
58
|
+
--pk-text-2: oklch(0.205 0 0);
|
|
59
|
+
--pk-muted: oklch(0.556 0 0);
|
|
60
|
+
--pk-brand: oklch(0.205 0 0);
|
|
61
|
+
--pk-brand-ink: oklch(0.985 0 0);
|
|
62
|
+
--pk-red: oklch(0.577 0.245 27.325);
|
|
63
|
+
--pk-chart-1: oklch(0.87 0 0);
|
|
64
|
+
--pk-chart-2: oklch(0.556 0 0);
|
|
65
|
+
--pk-chart-3: oklch(0.439 0 0);
|
|
66
|
+
--pk-chart-4: oklch(0.371 0 0);
|
|
67
|
+
--pk-chart-5: oklch(0.269 0 0);
|
|
68
|
+
color-scheme: light;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@media (prefers-color-scheme: light) {
|
|
72
|
+
:root[data-theme="system"] {
|
|
73
|
+
--pk-bg: oklch(1 0 0);
|
|
74
|
+
--pk-sidebar-primary: oklch(0.205 0 0);
|
|
75
|
+
--pk-sidebar-primary-ink: oklch(0.985 0 0);
|
|
76
|
+
--pk-surface: oklch(1 0 0);
|
|
77
|
+
--pk-surface-2: oklch(0.97 0 0);
|
|
78
|
+
--pk-accent: oklch(0.97 0 0);
|
|
79
|
+
--pk-border: oklch(0.922 0 0);
|
|
80
|
+
--pk-input: oklch(0.922 0 0);
|
|
81
|
+
--pk-ring: oklch(0.708 0 0);
|
|
82
|
+
--pk-text: oklch(0.145 0 0);
|
|
83
|
+
--pk-text-2: oklch(0.205 0 0);
|
|
84
|
+
--pk-muted: oklch(0.556 0 0);
|
|
85
|
+
--pk-brand: oklch(0.205 0 0);
|
|
86
|
+
--pk-brand-ink: oklch(0.985 0 0);
|
|
87
|
+
--pk-red: oklch(0.577 0.245 27.325);
|
|
88
|
+
--pk-chart-1: oklch(0.87 0 0);
|
|
89
|
+
--pk-chart-2: oklch(0.556 0 0);
|
|
90
|
+
--pk-chart-3: oklch(0.439 0 0);
|
|
91
|
+
--pk-chart-4: oklch(0.371 0 0);
|
|
92
|
+
--pk-chart-5: oklch(0.269 0 0);
|
|
93
|
+
color-scheme: light;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* PhlexKit theme: Zinc — translated from a ui.shadcn.com/create export
|
|
2
|
+
(preset b6mHLWLL2u, shadcn's stock zinc palette). Pure token override:
|
|
3
|
+
load AFTER the phlex_kit.css manifest and the cascade re-themes every
|
|
4
|
+
component.
|
|
5
|
+
|
|
6
|
+
Translation notes (the recipe for any shadcn/create export):
|
|
7
|
+
- background → bg; card/popover → surface; secondary/muted/accent →
|
|
8
|
+
surface-2/accent; foreground → text; secondary-foreground → text-2;
|
|
9
|
+
muted-foreground → muted; primary/primary-foreground → brand/brand-ink;
|
|
10
|
+
destructive → red; border/input/ring/chart-1..5/radius map 1:1.
|
|
11
|
+
- shadcn is light-default with a .dark class; PhlexKit themes are
|
|
12
|
+
dark-default with data-theme="light" opt-in — blocks swapped.
|
|
13
|
+
- sidebar-* has no --pk-* slots (the kit's sidebar reads the shared
|
|
14
|
+
surface tokens) and shadcn/create exports no green/amber — both fall
|
|
15
|
+
through to _tokens.css defaults.
|
|
16
|
+
- dark text-2 has no shadcn source (every dark *-foreground is 0.985);
|
|
17
|
+
zinc-300 is used, matching the kit's own dark text-2 weight. */
|
|
18
|
+
:root {
|
|
19
|
+
--pk-bg: oklch(0.141 0.005 285.823);
|
|
20
|
+
--pk-surface: oklch(0.21 0.006 285.885);
|
|
21
|
+
--pk-surface-2: oklch(0.274 0.006 286.033);
|
|
22
|
+
--pk-accent: oklch(0.274 0.006 286.033);
|
|
23
|
+
--pk-border: oklch(1 0 0 / 10%);
|
|
24
|
+
--pk-input: oklch(1 0 0 / 15%);
|
|
25
|
+
--pk-ring: oklch(0.552 0.016 285.938);
|
|
26
|
+
--pk-text: oklch(0.985 0 0);
|
|
27
|
+
--pk-text-2: oklch(0.871 0.006 286.286);
|
|
28
|
+
--pk-muted: oklch(0.705 0.015 286.067);
|
|
29
|
+
--pk-brand: oklch(0.92 0.004 286.32);
|
|
30
|
+
--pk-brand-ink: oklch(0.21 0.006 285.885);
|
|
31
|
+
--pk-red: oklch(0.704 0.191 22.216);
|
|
32
|
+
--pk-chart-1: oklch(0.871 0.006 286.286);
|
|
33
|
+
--pk-chart-2: oklch(0.552 0.016 285.938);
|
|
34
|
+
--pk-chart-3: oklch(0.442 0.017 285.786);
|
|
35
|
+
--pk-chart-4: oklch(0.37 0.013 285.805);
|
|
36
|
+
--pk-chart-5: oklch(0.274 0.006 286.033);
|
|
37
|
+
--pk-radius: 0.625rem;
|
|
38
|
+
color-scheme: dark;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:root[data-theme="light"] {
|
|
42
|
+
--pk-bg: oklch(1 0 0);
|
|
43
|
+
--pk-surface: oklch(1 0 0);
|
|
44
|
+
--pk-surface-2: oklch(0.967 0.001 286.375);
|
|
45
|
+
--pk-accent: oklch(0.967 0.001 286.375);
|
|
46
|
+
--pk-border: oklch(0.92 0.004 286.32);
|
|
47
|
+
--pk-input: oklch(0.92 0.004 286.32);
|
|
48
|
+
--pk-ring: oklch(0.705 0.015 286.067);
|
|
49
|
+
--pk-text: oklch(0.141 0.005 285.823);
|
|
50
|
+
--pk-text-2: oklch(0.21 0.006 285.885);
|
|
51
|
+
--pk-muted: oklch(0.552 0.016 285.938);
|
|
52
|
+
--pk-brand: oklch(0.21 0.006 285.885);
|
|
53
|
+
--pk-brand-ink: oklch(0.985 0 0);
|
|
54
|
+
--pk-red: oklch(0.577 0.245 27.325);
|
|
55
|
+
--pk-chart-1: oklch(0.871 0.006 286.286);
|
|
56
|
+
--pk-chart-2: oklch(0.552 0.016 285.938);
|
|
57
|
+
--pk-chart-3: oklch(0.442 0.017 285.786);
|
|
58
|
+
--pk-chart-4: oklch(0.37 0.013 285.805);
|
|
59
|
+
--pk-chart-5: oklch(0.274 0.006 286.033);
|
|
60
|
+
color-scheme: light;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (prefers-color-scheme: light) {
|
|
64
|
+
:root[data-theme="system"] {
|
|
65
|
+
--pk-bg: oklch(1 0 0);
|
|
66
|
+
--pk-surface: oklch(1 0 0);
|
|
67
|
+
--pk-surface-2: oklch(0.967 0.001 286.375);
|
|
68
|
+
--pk-accent: oklch(0.967 0.001 286.375);
|
|
69
|
+
--pk-border: oklch(0.92 0.004 286.32);
|
|
70
|
+
--pk-input: oklch(0.92 0.004 286.32);
|
|
71
|
+
--pk-ring: oklch(0.705 0.015 286.067);
|
|
72
|
+
--pk-text: oklch(0.141 0.005 285.823);
|
|
73
|
+
--pk-text-2: oklch(0.21 0.006 285.885);
|
|
74
|
+
--pk-muted: oklch(0.552 0.016 285.938);
|
|
75
|
+
--pk-brand: oklch(0.21 0.006 285.885);
|
|
76
|
+
--pk-brand-ink: oklch(0.985 0 0);
|
|
77
|
+
--pk-red: oklch(0.577 0.245 27.325);
|
|
78
|
+
--pk-chart-1: oklch(0.871 0.006 286.286);
|
|
79
|
+
--pk-chart-2: oklch(0.552 0.016 285.938);
|
|
80
|
+
--pk-chart-3: oklch(0.442 0.017 285.786);
|
|
81
|
+
--pk-chart-4: oklch(0.37 0.013 285.805);
|
|
82
|
+
--pk-chart-5: oklch(0.274 0.006 286.033);
|
|
83
|
+
color-scheme: light;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -12,3 +12,16 @@
|
|
|
12
12
|
.pk-accordion-icon svg { width: 1rem; height: 1rem; }
|
|
13
13
|
.pk-accordion-content { overflow-y: hidden; }
|
|
14
14
|
.pk-accordion-default-content { padding-bottom: 1rem; padding-top: 0; font-size: .875rem; }
|
|
15
|
+
|
|
16
|
+
/* Disabled items: inert trigger, dimmed. */
|
|
17
|
+
.pk-accordion-item[data-disabled] .pk-accordion-trigger {
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
opacity: .5;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Bordered flavour (shadcn's "Borders" example): outer border, inset rows. */
|
|
23
|
+
.pk-accordion.bordered {
|
|
24
|
+
border: 1px solid var(--pk-border);
|
|
25
|
+
border-radius: var(--pk-radius);
|
|
26
|
+
padding-inline: 1rem;
|
|
27
|
+
}
|
|
@@ -4,7 +4,16 @@ module PhlexKit
|
|
|
4
4
|
# AccordionContent). Animated open/close via the phlex-kit--accordion controller
|
|
5
5
|
# (native Web Animations API — no `motion` dependency).
|
|
6
6
|
class Accordion < BaseComponent
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
# single (default, shadcn's): opening an item closes the others.
|
|
8
|
+
TYPES = { single: "single", multiple: "multiple" }.freeze
|
|
9
|
+
|
|
10
|
+
def initialize(type: :single, **attrs)
|
|
11
|
+
@type = type.to_sym
|
|
12
|
+
@attrs = attrs
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def view_template(&)
|
|
16
|
+
div(**mix({ class: "pk-accordion", data: { type: TYPES.fetch(@type) } }, @attrs), &)
|
|
17
|
+
end
|
|
9
18
|
end
|
|
10
19
|
end
|
|
@@ -6,9 +6,7 @@ module PhlexKit
|
|
|
6
6
|
if block
|
|
7
7
|
yield
|
|
8
8
|
else
|
|
9
|
-
|
|
10
|
-
s.path(fill_rule: "evenodd", clip_rule: "evenodd", d: "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z")
|
|
11
|
-
end
|
|
9
|
+
render Icon.new(:chevron_down, size: nil)
|
|
12
10
|
end
|
|
13
11
|
end
|
|
14
12
|
end
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
2
|
class AccordionItem < BaseComponent
|
|
3
|
-
def initialize(open: false, rotate_icon: 180, **attrs)
|
|
3
|
+
def initialize(open: false, disabled: false, rotate_icon: 180, **attrs)
|
|
4
4
|
@open = open
|
|
5
|
+
@disabled = disabled
|
|
5
6
|
@rotate_icon = rotate_icon
|
|
6
7
|
@attrs = attrs
|
|
7
8
|
end
|
|
8
9
|
def view_template(&)
|
|
9
|
-
|
|
10
|
+
data = { controller: "phlex-kit--accordion", phlex_kit__accordion_open_value: @open, phlex_kit__accordion_rotate_icon_value: @rotate_icon }
|
|
11
|
+
data[:disabled] = "" if @disabled
|
|
12
|
+
div(**mix({ class: "pk-accordion-item", data: data }, @attrs), &)
|
|
10
13
|
end
|
|
11
14
|
end
|
|
12
15
|
end
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
position: fixed;
|
|
9
9
|
inset: 0;
|
|
10
10
|
z-index: 50;
|
|
11
|
-
background:
|
|
12
|
-
backdrop-filter: blur(
|
|
11
|
+
background: rgb(0 0 0 / .5);
|
|
12
|
+
backdrop-filter: blur(8px);
|
|
13
13
|
}
|
|
14
14
|
.pk-alert-dialog-panel {
|
|
15
15
|
position: fixed;
|
|
@@ -28,10 +28,51 @@
|
|
|
28
28
|
border-radius: var(--pk-radius);
|
|
29
29
|
background: var(--pk-surface);
|
|
30
30
|
color: var(--pk-text);
|
|
31
|
-
|
|
31
|
+
--pk-dialog-spacing: 1.5rem;
|
|
32
|
+
padding: var(--pk-dialog-spacing);
|
|
32
33
|
box-shadow: 0 10px 40px rgba(0, 0, 0, .5);
|
|
33
34
|
}
|
|
35
|
+
.pk-alert-dialog-panel.sm { max-width: 24rem; --pk-dialog-spacing: 1rem; gap: .75rem; }
|
|
36
|
+
.pk-alert-dialog-panel.sm .pk-alert-dialog-title { font-size: 1rem; }
|
|
37
|
+
|
|
34
38
|
.pk-alert-dialog-header { display: flex; flex-direction: column; gap: .5rem; }
|
|
39
|
+
|
|
40
|
+
/* Media tile: a rounded-square icon chip; a header containing one becomes a
|
|
41
|
+
centered stack (shadcn's media layout). */
|
|
42
|
+
.pk-alert-dialog-media {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
flex: none;
|
|
47
|
+
width: 2.75rem;
|
|
48
|
+
height: 2.75rem;
|
|
49
|
+
border-radius: var(--pk-radius);
|
|
50
|
+
background: var(--pk-surface-2);
|
|
51
|
+
color: var(--pk-text);
|
|
52
|
+
margin-bottom: .25rem;
|
|
53
|
+
font-size: 1.25rem;
|
|
54
|
+
}
|
|
55
|
+
.pk-alert-dialog-media svg { width: 1.25rem; height: 1.25rem; }
|
|
56
|
+
.pk-alert-dialog-media img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
|
|
57
|
+
.pk-alert-dialog-header:has(.pk-alert-dialog-media) {
|
|
58
|
+
align-items: center;
|
|
59
|
+
text-align: center;
|
|
60
|
+
}
|
|
61
|
+
/* :sm dialogs pair the centered stack with a full-width 50/50 footer. */
|
|
62
|
+
.pk-alert-dialog-panel.sm .pk-alert-dialog-footer {
|
|
63
|
+
display: grid;
|
|
64
|
+
grid-template-columns: 1fr 1fr;
|
|
65
|
+
}
|
|
35
66
|
.pk-alert-dialog-title { margin: 0; font-size: 1.125rem; font-weight: 600; }
|
|
36
67
|
.pk-alert-dialog-description { margin: 0; font-size: .875rem; color: var(--pk-muted); }
|
|
37
|
-
|
|
68
|
+
/* Footer: shadcn's full-bleed muted band (escapes the panel padding). */
|
|
69
|
+
.pk-alert-dialog-footer {
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: flex-end;
|
|
72
|
+
gap: .5rem;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
margin: 0 calc(-1 * var(--pk-dialog-spacing)) calc(-1 * var(--pk-dialog-spacing));
|
|
75
|
+
padding: var(--pk-dialog-spacing);
|
|
76
|
+
border-top: 1px solid var(--pk-border);
|
|
77
|
+
background: color-mix(in oklab, var(--pk-surface-2) 50%, transparent);
|
|
78
|
+
}
|
|
@@ -3,12 +3,13 @@ module PhlexKit
|
|
|
3
3
|
# non-GET destructive action, use a `button_to` in the footer instead, so it
|
|
4
4
|
# submits a real form.) See alert_dialog.rb.
|
|
5
5
|
class AlertDialogAction < BaseComponent
|
|
6
|
-
def initialize(**attrs)
|
|
6
|
+
def initialize(variant: :primary, **attrs)
|
|
7
|
+
@variant = variant
|
|
7
8
|
@attrs = attrs
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def view_template(&block)
|
|
11
|
-
render PhlexKit::Button.new(variant:
|
|
12
|
+
render PhlexKit::Button.new(variant: @variant, **@attrs, &block)
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
15
|
end
|
|
@@ -3,7 +3,11 @@ module PhlexKit
|
|
|
3
3
|
# cloned div carries its own `phlex-kit--alert-dialog` controller so Cancel
|
|
4
4
|
# (#dismiss) can remove it. Holds Header + Footer. See alert_dialog.rb.
|
|
5
5
|
class AlertDialogContent < BaseComponent
|
|
6
|
-
|
|
6
|
+
# size => panel modifier; :sm is shadcn's compact variant.
|
|
7
|
+
SIZES = { default: nil, sm: "sm" }.freeze
|
|
8
|
+
|
|
9
|
+
def initialize(size: :default, **attrs)
|
|
10
|
+
@size = size.to_sym
|
|
7
11
|
@attrs = attrs
|
|
8
12
|
end
|
|
9
13
|
|
|
@@ -11,7 +15,7 @@ module PhlexKit
|
|
|
11
15
|
template(**mix({ data: { phlex_kit__alert_dialog_target: "content" } }, @attrs)) do
|
|
12
16
|
div(data: { controller: "phlex-kit--alert-dialog" }) do
|
|
13
17
|
div(class: "pk-alert-dialog-overlay", "aria-hidden": "true")
|
|
14
|
-
div(role: "alertdialog", "aria-modal": "true", class: "pk-alert-dialog-panel", &block)
|
|
18
|
+
div(role: "alertdialog", "aria-modal": "true", class: [ "pk-alert-dialog-panel", SIZES.fetch(@size) ].compact.join(" "), &block)
|
|
15
19
|
end
|
|
16
20
|
end
|
|
17
21
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Icon/image tile in an AlertDialogHeader — a muted circle above the title
|
|
3
|
+
# (beside the text column in size :sm dialogs). Ported from shadcn/ui's
|
|
4
|
+
# AlertDialogMedia (newer than ruby_ui's alert dialog). See alert_dialog.rb.
|
|
5
|
+
class AlertDialogMedia < BaseComponent
|
|
6
|
+
def initialize(**attrs)
|
|
7
|
+
@attrs = attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template(&)
|
|
11
|
+
div(**mix({ class: "pk-alert-dialog-media", aria: { hidden: "true" } }, @attrs), &)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/* Co-located with attachment.rb — UI::Attachment, shadcn parity: bordered
|
|
2
|
-
file chip (icon/
|
|
2
|
+
file chip (icon/image media, name + meta, trailing actions), upload states
|
|
3
|
+
(uploading/processing shimmer the title; error goes destructive), three
|
|
4
|
+
sizes, vertical orientation, a full-card trigger behind the actions, and a
|
|
5
|
+
scrollable snapping group with an edge fade. */
|
|
3
6
|
.pk-attachment {
|
|
7
|
+
position: relative;
|
|
4
8
|
display: flex;
|
|
5
9
|
align-items: center;
|
|
6
10
|
gap: .5rem;
|
|
@@ -39,7 +43,14 @@
|
|
|
39
43
|
text-overflow: ellipsis;
|
|
40
44
|
}
|
|
41
45
|
.pk-attachment-description { color: var(--pk-muted); font-size: .75rem; }
|
|
42
|
-
.pk-attachment-actions {
|
|
46
|
+
.pk-attachment-actions {
|
|
47
|
+
position: relative; /* above the full-card trigger */
|
|
48
|
+
z-index: 2;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: .25rem;
|
|
52
|
+
flex: none;
|
|
53
|
+
}
|
|
43
54
|
.pk-attachment-action {
|
|
44
55
|
display: inline-flex;
|
|
45
56
|
align-items: center;
|
|
@@ -60,3 +71,92 @@
|
|
|
60
71
|
box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
61
72
|
}
|
|
62
73
|
.pk-attachment-action svg { width: .875rem; height: .875rem; }
|
|
74
|
+
|
|
75
|
+
/* Sizes. */
|
|
76
|
+
.pk-attachment.sm { padding: .375rem; gap: .375rem; }
|
|
77
|
+
.pk-attachment.sm .pk-attachment-media { width: 2rem; height: 2rem; }
|
|
78
|
+
.pk-attachment.xs { padding: .25rem .375rem; gap: .375rem; font-size: .8125rem; }
|
|
79
|
+
.pk-attachment.xs .pk-attachment-media { width: 1.5rem; height: 1.5rem; border-radius: calc(var(--pk-radius) - 4px); }
|
|
80
|
+
|
|
81
|
+
/* Vertical: media stacks above the content (image-preview cards). */
|
|
82
|
+
.pk-attachment.vertical {
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
align-items: stretch;
|
|
85
|
+
width: 10rem;
|
|
86
|
+
}
|
|
87
|
+
.pk-attachment.vertical .pk-attachment-media.image {
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: auto;
|
|
90
|
+
aspect-ratio: 4 / 3;
|
|
91
|
+
}
|
|
92
|
+
.pk-attachment.vertical .pk-attachment-actions {
|
|
93
|
+
position: absolute;
|
|
94
|
+
top: .75rem;
|
|
95
|
+
right: .75rem;
|
|
96
|
+
}
|
|
97
|
+
.pk-attachment.vertical .pk-attachment-action {
|
|
98
|
+
background: color-mix(in oklab, var(--pk-bg) 60%, transparent);
|
|
99
|
+
color: var(--pk-text);
|
|
100
|
+
backdrop-filter: blur(4px);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Upload states: uploading/processing shimmer the title (same recipe as
|
|
104
|
+
.pk-shimmer); error goes destructive. */
|
|
105
|
+
.pk-attachment[data-state="uploading"] .pk-attachment-title,
|
|
106
|
+
.pk-attachment[data-state="processing"] .pk-attachment-title {
|
|
107
|
+
--pk-shimmer-color: oklch(from currentColor calc(l + .25) c h);
|
|
108
|
+
background-image:
|
|
109
|
+
linear-gradient(110deg,
|
|
110
|
+
transparent calc(50% - (3ch + 40px) / 2),
|
|
111
|
+
var(--pk-shimmer-color) 50%,
|
|
112
|
+
transparent calc(50% + (3ch + 40px) / 2)),
|
|
113
|
+
linear-gradient(currentColor, currentColor);
|
|
114
|
+
background-size: 250% 100%, auto;
|
|
115
|
+
background-repeat: no-repeat;
|
|
116
|
+
-webkit-background-clip: text;
|
|
117
|
+
background-clip: text;
|
|
118
|
+
-webkit-text-fill-color: transparent;
|
|
119
|
+
animation: pk-shimmer 2s linear infinite;
|
|
120
|
+
}
|
|
121
|
+
.pk-attachment[data-state="error"] { border-color: color-mix(in oklab, var(--pk-red) 40%, transparent); }
|
|
122
|
+
.pk-attachment[data-state="error"] .pk-attachment-media { color: var(--pk-red); background: color-mix(in oklab, var(--pk-red) 12%, transparent); }
|
|
123
|
+
.pk-attachment[data-state="error"] .pk-attachment-description { color: var(--pk-red); }
|
|
124
|
+
|
|
125
|
+
/* Full-card trigger — behind the actions, focus ring on the card. */
|
|
126
|
+
.pk-attachment-trigger {
|
|
127
|
+
position: absolute;
|
|
128
|
+
inset: 0;
|
|
129
|
+
z-index: 1;
|
|
130
|
+
border: 0;
|
|
131
|
+
border-radius: inherit;
|
|
132
|
+
background: transparent;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
padding: 0;
|
|
135
|
+
}
|
|
136
|
+
.pk-attachment-trigger:focus-visible { outline: none; }
|
|
137
|
+
.pk-attachment:has(.pk-attachment-trigger:hover) { background: color-mix(in oklab, var(--pk-surface-2) 40%, var(--pk-surface)); }
|
|
138
|
+
.pk-attachment:has(.pk-attachment-trigger:focus-visible) {
|
|
139
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Scrollable, snapping row with an edge fade (horizontal scroll-fade). */
|
|
143
|
+
.pk-attachment-group {
|
|
144
|
+
--pk-scroll-fade-size: 2rem;
|
|
145
|
+
display: flex;
|
|
146
|
+
gap: .5rem;
|
|
147
|
+
overflow-x: auto;
|
|
148
|
+
scroll-snap-type: x mandatory;
|
|
149
|
+
padding: 2px; /* room for card rings */
|
|
150
|
+
mask-image: linear-gradient(to right,
|
|
151
|
+
transparent 0, black var(--pk-scroll-fade-size),
|
|
152
|
+
black calc(100% - var(--pk-scroll-fade-size)), transparent 100%);
|
|
153
|
+
}
|
|
154
|
+
.pk-attachment-group[data-at-start] {
|
|
155
|
+
mask-image: linear-gradient(to right,
|
|
156
|
+
black calc(100% - var(--pk-scroll-fade-size)), transparent 100%);
|
|
157
|
+
}
|
|
158
|
+
.pk-attachment-group[data-at-end] {
|
|
159
|
+
mask-image: linear-gradient(to right, transparent 0, black var(--pk-scroll-fade-size));
|
|
160
|
+
}
|
|
161
|
+
.pk-attachment-group[data-at-start][data-at-end] { mask-image: none; }
|
|
162
|
+
.pk-attachment-group > .pk-attachment { flex: none; scroll-snap-align: start; }
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
-
# File chip, ported from shadcn/ui's Attachment (
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
2
|
+
# File chip, ported from shadcn/ui's Attachment: media (icon or image),
|
|
3
|
+
# name + metadata, optional actions, upload state and a full-card trigger.
|
|
4
|
+
# `state:` drives styling — :uploading/:processing shimmer the title, :error
|
|
5
|
+
# goes destructive; `size:` (:default/:sm/:xs) and `orientation:`
|
|
6
|
+
# (:horizontal/:vertical) shape the card. Lay several out in an
|
|
7
|
+
# AttachmentGroup (scrollable, snapping, edge-faded row).
|
|
6
8
|
# `.pk-attachment*` (attachment.css).
|
|
7
9
|
class Attachment < BaseComponent
|
|
8
|
-
|
|
10
|
+
STATES = { idle: "idle", uploading: "uploading", processing: "processing", error: "error", done: "done" }.freeze
|
|
11
|
+
SIZES = { default: nil, sm: "sm", xs: "xs" }.freeze
|
|
12
|
+
ORIENTATIONS = { horizontal: nil, vertical: "vertical" }.freeze
|
|
13
|
+
|
|
14
|
+
def initialize(state: :done, size: :default, orientation: :horizontal, **attrs)
|
|
15
|
+
@state = state.to_sym
|
|
16
|
+
@size = size.to_sym
|
|
17
|
+
@orientation = orientation.to_sym
|
|
9
18
|
@attrs = attrs
|
|
10
19
|
end
|
|
11
20
|
|
|
12
21
|
def view_template(&)
|
|
13
|
-
div(**mix({
|
|
22
|
+
div(**mix({
|
|
23
|
+
class: [ "pk-attachment", SIZES.fetch(@size), ORIENTATIONS.fetch(@orientation) ].compact.join(" "),
|
|
24
|
+
data: { state: STATES.fetch(@state) }
|
|
25
|
+
}, @attrs), &)
|
|
14
26
|
end
|
|
15
27
|
end
|
|
16
28
|
end
|
|
@@ -11,12 +11,7 @@ module PhlexKit
|
|
|
11
11
|
if block
|
|
12
12
|
yield
|
|
13
13
|
else
|
|
14
|
-
|
|
15
|
-
stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round",
|
|
16
|
-
"stroke-linejoin": "round", "aria-hidden": "true") do |s|
|
|
17
|
-
s.path(d: "M18 6 6 18")
|
|
18
|
-
s.path(d: "m6 6 12 12")
|
|
19
|
-
end
|
|
14
|
+
render Icon.new(:x, size: nil)
|
|
20
15
|
end
|
|
21
16
|
end
|
|
22
17
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Horizontally scrollable, snapping row of Attachments with an edge fade
|
|
3
|
+
# (the phlex-kit--scroll-fade controller in horizontal mode).
|
|
4
|
+
# See attachment.rb.
|
|
5
|
+
class AttachmentGroup < BaseComponent
|
|
6
|
+
def initialize(**attrs)
|
|
7
|
+
@attrs = attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template(&)
|
|
11
|
+
div(**mix({
|
|
12
|
+
class: "pk-attachment-group",
|
|
13
|
+
role: "group",
|
|
14
|
+
data: {
|
|
15
|
+
controller: "phlex-kit--scroll-fade",
|
|
16
|
+
phlex_kit__scroll_fade_axis_value: "x"
|
|
17
|
+
}
|
|
18
|
+
}, @attrs), &)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
-
#
|
|
3
|
-
#
|
|
2
|
+
# Media slot of an Attachment: `variant: :icon` (default, a muted tile) or
|
|
3
|
+
# `:image` (render an <img> inside; fills the slot, full-width when the
|
|
4
|
+
# attachment is vertical). See attachment.rb.
|
|
4
5
|
class AttachmentMedia < BaseComponent
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
VARIANTS = { icon: nil, image: "image" }.freeze
|
|
7
|
+
|
|
8
|
+
def initialize(variant: :icon, **attrs)
|
|
9
|
+
@variant = variant.to_sym
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&)
|
|
14
|
+
div(**mix({ class: [ "pk-attachment-media", VARIANTS.fetch(@variant) ].compact.join(" ") }, @attrs), &)
|
|
15
|
+
end
|
|
7
16
|
end
|
|
8
17
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Full-card overlay that activates the attachment (a preview link or dialog
|
|
3
|
+
# trigger). Sits BEHIND AttachmentActions in the stacking order, so actions
|
|
4
|
+
# stay independently clickable. Give it an aria-label; pass `as: :a` +
|
|
5
|
+
# `href:` for a link. See attachment.rb.
|
|
6
|
+
class AttachmentTrigger < BaseComponent
|
|
7
|
+
def initialize(as: :button, href: nil, **attrs)
|
|
8
|
+
@as = as.to_sym
|
|
9
|
+
@href = href
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template
|
|
14
|
+
base = { class: "pk-attachment-trigger" }
|
|
15
|
+
if @as == :a
|
|
16
|
+
a(**mix(base.merge(href: @href), @attrs))
|
|
17
|
+
else
|
|
18
|
+
button(**mix(base.merge(type: :button), @attrs))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -3,11 +3,7 @@ module PhlexKit
|
|
|
3
3
|
def initialize(**attrs) = (@attrs = attrs)
|
|
4
4
|
def view_template
|
|
5
5
|
span(**mix({ class: "pk-breadcrumb-ellipsis", role: "presentation", aria: { hidden: true } }, @attrs)) do
|
|
6
|
-
|
|
7
|
-
s.circle(cx: "12", cy: "12", r: "1")
|
|
8
|
-
s.circle(cx: "19", cy: "12", r: "1")
|
|
9
|
-
s.circle(cx: "5", cy: "12", r: "1")
|
|
10
|
-
end
|
|
6
|
+
render Icon.new(:ellipsis, size: nil)
|
|
11
7
|
span(class: "pk-sr-only") { "More" }
|
|
12
8
|
end
|
|
13
9
|
end
|
|
@@ -3,7 +3,7 @@ module PhlexKit
|
|
|
3
3
|
def initialize(**attrs) = (@attrs = attrs)
|
|
4
4
|
def view_template(&block)
|
|
5
5
|
li(**mix({ class: "pk-breadcrumb-separator", role: "presentation", aria: { hidden: true } }, @attrs)) do
|
|
6
|
-
if block then yield else
|
|
6
|
+
if block then yield else render(Icon.new(:chevron_right, size: nil)) end
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -20,21 +20,7 @@ module PhlexKit
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
22
|
def icon
|
|
23
|
-
|
|
24
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
25
|
-
width: "15",
|
|
26
|
-
height: "15",
|
|
27
|
-
viewbox: "0 0 15 15",
|
|
28
|
-
fill: "none",
|
|
29
|
-
"aria-hidden": "true"
|
|
30
|
-
) do |s|
|
|
31
|
-
s.path(
|
|
32
|
-
d: "M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z",
|
|
33
|
-
fill: "currentColor",
|
|
34
|
-
fill_rule: "evenodd",
|
|
35
|
-
clip_rule: "evenodd"
|
|
36
|
-
)
|
|
37
|
-
end
|
|
23
|
+
render Icon.new(:chevron_right, size: 15)
|
|
38
24
|
end
|
|
39
25
|
end
|
|
40
26
|
end
|