kiso 0.5.2.pre → 0.6.0.pre
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/app/assets/tailwind/kiso/button.css +12 -3
- data/app/assets/tailwind/kiso/checkbox.css +13 -2
- data/app/assets/tailwind/kiso/color-mode.css +15 -3
- data/app/assets/tailwind/kiso/dashboard.css +97 -44
- data/app/assets/tailwind/kiso/dialog.css +39 -5
- data/app/assets/tailwind/kiso/engine.css +117 -34
- data/app/assets/tailwind/kiso/input-otp.css +24 -4
- data/app/assets/tailwind/kiso/palettes/blue.css +14 -5
- data/app/assets/tailwind/kiso/palettes/green.css +9 -5
- data/app/assets/tailwind/kiso/palettes/orange.css +9 -5
- data/app/assets/tailwind/kiso/palettes/violet.css +9 -5
- data/app/assets/tailwind/kiso/palettes/zinc.css +11 -7
- data/app/assets/tailwind/kiso/radio-group.css +11 -4
- data/app/assets/tailwind/kiso/slider.css +25 -6
- data/app/assets/tailwind/kiso/tooltip.css +37 -11
- data/app/helpers/kiso/app_component_helper.rb +83 -34
- data/app/helpers/kiso/component_helper.rb +227 -70
- data/app/helpers/kiso/icon_helper.rb +101 -39
- data/app/helpers/kiso/theme_helper.rb +50 -9
- data/app/helpers/kiso/ui_context_helper.rb +87 -35
- data/app/javascript/controllers/kiso/combobox_controller.js +10 -2
- data/app/javascript/controllers/kiso/command_controller.js +2 -0
- data/app/javascript/controllers/kiso/command_dialog_controller.js +4 -0
- data/app/javascript/controllers/kiso/dialog_controller.js +6 -1
- data/app/javascript/controllers/kiso/dialog_trigger_controller.js +1 -1
- data/app/javascript/controllers/kiso/dropdown_menu_controller.js +23 -5
- data/app/javascript/controllers/kiso/index.js +25 -0
- data/app/javascript/controllers/kiso/input_otp_controller.js +5 -3
- data/app/javascript/controllers/kiso/popover_controller.js +18 -4
- data/app/javascript/controllers/kiso/select_controller.js +10 -2
- data/app/javascript/controllers/kiso/sidebar_controller.js +26 -4
- data/app/javascript/controllers/kiso/slider_controller.js +3 -3
- data/app/javascript/controllers/kiso/theme_controller.js +2 -1
- data/app/javascript/controllers/kiso/toggle_controller.js +2 -0
- data/app/javascript/controllers/kiso/toggle_group_controller.js +3 -0
- data/app/javascript/controllers/kiso/tooltip_controller.js +3 -0
- data/app/javascript/kiso/utils/focusable.js +14 -0
- data/app/javascript/kiso/utils/highlight.js +15 -1
- data/app/views/kiso/components/_alert.html.erb +2 -0
- data/app/views/kiso/components/_alert_dialog.html.erb +5 -2
- data/app/views/kiso/components/_app.html.erb +2 -0
- data/app/views/kiso/components/_aspect_ratio.html.erb +1 -0
- data/app/views/kiso/components/_avatar.html.erb +6 -2
- data/app/views/kiso/components/_button.html.erb +3 -0
- data/app/views/kiso/components/_checkbox.html.erb +1 -0
- data/app/views/kiso/components/_color_mode_button.html.erb +2 -0
- data/app/views/kiso/components/_color_mode_select.html.erb +2 -0
- data/app/views/kiso/components/_combobox.html.erb +3 -0
- data/app/views/kiso/components/_command.html.erb +2 -0
- data/app/views/kiso/components/_dashboard_group.html.erb +4 -0
- data/app/views/kiso/components/_dashboard_navbar.html.erb +2 -0
- data/app/views/kiso/components/_dashboard_panel.html.erb +1 -0
- data/app/views/kiso/components/_dashboard_sidebar.html.erb +2 -0
- data/app/views/kiso/components/_dashboard_toolbar.html.erb +2 -0
- data/app/views/kiso/components/_dialog.html.erb +3 -0
- data/app/views/kiso/components/_dropdown_menu.html.erb +2 -0
- data/app/views/kiso/components/_empty.html.erb +2 -0
- data/app/views/kiso/components/_field.html.erb +2 -0
- data/app/views/kiso/components/_field_group.html.erb +1 -0
- data/app/views/kiso/components/_field_set.html.erb +1 -0
- data/app/views/kiso/components/_input_group.html.erb +1 -0
- data/app/views/kiso/components/_input_otp.html.erb +3 -0
- data/app/views/kiso/components/_nav.html.erb +2 -0
- data/app/views/kiso/components/_page_card.html.erb +3 -0
- data/app/views/kiso/components/_page_header.html.erb +3 -0
- data/app/views/kiso/components/_page_section.html.erb +2 -0
- data/app/views/kiso/components/_pagination.html.erb +2 -0
- data/app/views/kiso/components/_popover.html.erb +3 -0
- data/app/views/kiso/components/_select.html.erb +3 -0
- data/app/views/kiso/components/_select_native.html.erb +2 -0
- data/app/views/kiso/components/_separator.html.erb +2 -0
- data/app/views/kiso/components/_skeleton.html.erb +1 -0
- data/app/views/kiso/components/_slider.html.erb +4 -0
- data/app/views/kiso/components/_spinner.html.erb +2 -0
- data/app/views/kiso/components/_stats_card.html.erb +2 -0
- data/app/views/kiso/components/_stats_grid.html.erb +1 -0
- data/app/views/kiso/components/_switch.html.erb +2 -0
- data/app/views/kiso/components/_table.html.erb +2 -0
- data/app/views/kiso/components/_textarea.html.erb +3 -0
- data/app/views/kiso/components/_toggle.html.erb +2 -0
- data/app/views/kiso/components/_toggle_group.html.erb +2 -0
- data/app/views/kiso/components/_tooltip.html.erb +3 -0
- data/app/views/kiso/components/alert_dialog/_action.html.erb +1 -0
- data/app/views/kiso/components/alert_dialog/_cancel.html.erb +1 -0
- data/app/views/kiso/components/alert_dialog/_description.html.erb +1 -0
- data/app/views/kiso/components/alert_dialog/_title.html.erb +1 -0
- data/app/views/kiso/components/avatar/_image.html.erb +1 -0
- data/app/views/kiso/components/breadcrumb/_separator.html.erb +3 -0
- data/app/views/kiso/components/combobox/_chips.html.erb +3 -0
- data/app/views/kiso/components/command/_dialog.html.erb +2 -0
- data/app/views/kiso/components/dashboard_sidebar/_collapse.html.erb +2 -0
- data/app/views/kiso/components/dialog/_close.html.erb +1 -0
- data/app/views/kiso/components/field/_error.html.erb +4 -0
- data/app/views/kiso/components/field/_label.html.erb +2 -0
- data/app/views/kiso/components/field/_separator.html.erb +3 -0
- data/app/views/kiso/components/input_otp/_separator.html.erb +2 -0
- data/app/views/kiso/components/input_otp/_slot.html.erb +2 -0
- data/app/views/kiso/components/nav/_section.html.erb +4 -0
- data/app/views/kiso/components/tooltip/_content.html.erb +2 -0
- data/lib/generators/kiso/install/USAGE +23 -0
- data/lib/generators/kiso/install/install_generator.rb +91 -0
- data/lib/generators/kiso/install/templates/design_system.md.tt +190 -0
- data/lib/generators/kiso/install/templates/initializer.rb.tt +40 -0
- data/lib/kiso/cli/make.rb +6 -3
- data/lib/kiso/cli.rb +10 -0
- data/lib/kiso/color_utils.rb +31 -8
- data/lib/kiso/configuration.rb +11 -0
- data/lib/kiso/engine.rb +9 -2
- data/lib/kiso/propshaft_tailwind_stub_filter.rb +9 -2
- data/lib/kiso/themes/avatar.rb +40 -6
- data/lib/kiso/themes/badge.rb +5 -1
- data/lib/kiso/themes/color_mode_button.rb +11 -0
- data/lib/kiso/themes/color_mode_select.rb +7 -0
- data/lib/kiso/themes/dashboard.rb +28 -0
- data/lib/kiso/themes/dropdown_menu.rb +2 -2
- data/lib/kiso/themes/input_otp.rb +6 -3
- data/lib/kiso/themes/nav.rb +17 -0
- data/lib/kiso/themes/pagination.rb +9 -4
- data/lib/kiso/themes/shared.rb +27 -7
- data/lib/kiso/version.rb +5 -2
- metadata +5 -1
|
@@ -4,52 +4,91 @@ module Kiso
|
|
|
4
4
|
# View helpers for rendering host app components.
|
|
5
5
|
#
|
|
6
6
|
# Mirrors {ComponentHelper#kui} but resolves partials from
|
|
7
|
-
# +app/views/components/+ and themes from +AppThemes
|
|
8
|
-
#
|
|
7
|
+
# +app/views/components/+ and themes from the +AppThemes::+ namespace
|
|
8
|
+
# (loaded from +app/themes/<theme_name>/+).
|
|
9
9
|
#
|
|
10
|
-
#
|
|
10
|
+
# == Key difference from +kui()+
|
|
11
|
+
#
|
|
12
|
+
# +appui()+ does *not* merge global config overrides (Layer 2). Host apps
|
|
13
|
+
# own their component source directly, so there is no need for a
|
|
14
|
+
# boot-time override layer. The three layers for +appui()+ are:
|
|
15
|
+
#
|
|
16
|
+
# 1. **Theme default** -- the +ClassVariants+ definition in
|
|
17
|
+
# +app/themes/<theme_name>/+.
|
|
18
|
+
# 2. **Instance +ui:+** -- per-render slot overrides.
|
|
19
|
+
# 3. **Instance +css_classes:+** -- per-render root-element overrides.
|
|
20
|
+
#
|
|
21
|
+
# == Generating host app components
|
|
22
|
+
#
|
|
23
|
+
# Use the generator to scaffold theme and partial files:
|
|
24
|
+
#
|
|
25
|
+
# bin/rails generate kiso:component pricing_card --sub-parts header body footer
|
|
26
|
+
#
|
|
27
|
+
# This creates files in +app/themes/default/+ and +app/views/components/+.
|
|
28
|
+
#
|
|
29
|
+
# Included in all views automatically by {Kiso::Engine}.
|
|
30
|
+
#
|
|
31
|
+
# @see ComponentHelper#kui the equivalent helper for engine-shipped components
|
|
32
|
+
# @see ComponentHelper#kui_tag the themed +content_tag+ shorthand (aliased as {#appui_tag})
|
|
11
33
|
module AppComponentHelper
|
|
12
34
|
# Renders a host app component partial.
|
|
13
35
|
#
|
|
14
36
|
# Components live in +app/views/components/+. Sub-parts are nested
|
|
15
|
-
# in a directory matching the parent component name.
|
|
37
|
+
# in a directory matching the parent component name (e.g.
|
|
38
|
+
# +components/pricing_card/_header.html.erb+).
|
|
16
39
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# @param
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# @param
|
|
40
|
+
# Behaves identically to {ComponentHelper#kui} except:
|
|
41
|
+
# - Partials resolve from +app/views/components/+ instead of
|
|
42
|
+
# +app/views/kiso/components/+.
|
|
43
|
+
# - Global config +ui:+ overrides are *not* merged (host apps own the
|
|
44
|
+
# source and can edit themes directly).
|
|
45
|
+
#
|
|
46
|
+
# @param component [Symbol] the component name (e.g. +:pricing_card+).
|
|
47
|
+
# Must match a partial at +app/views/components/_<name>.html.erb+.
|
|
48
|
+
# @param part [Symbol, nil] optional sub-part name (e.g. +:header+,
|
|
49
|
+
# +:footer+). Resolves to
|
|
50
|
+
# +app/views/components/<component>/_<part>.html.erb+.
|
|
51
|
+
# @param collection [Array, nil] when present, renders the partial once
|
|
52
|
+
# per item using Rails collection rendering.
|
|
53
|
+
# @param ui [Hash{Symbol => String}, nil] per-slot class overrides keyed
|
|
54
|
+
# by sub-part name. Pushed onto the context stack for composed
|
|
55
|
+
# sub-parts, and also passed as a +ui:+ local to self-rendering
|
|
56
|
+
# partials.
|
|
57
|
+
# @param scope [Hash, nil] domain locals shared from parent to sub-parts
|
|
58
|
+
# via context stack. Sub-parts receive scope values as kwargs
|
|
59
|
+
# automatically. Explicit kwargs on sub-part calls override scope
|
|
60
|
+
# values. One level deep only -- no ancestor resolution.
|
|
61
|
+
# @param kwargs [Hash] locals forwarded to the partial (e.g.
|
|
62
|
+
# +css_classes:+, +plan:+, +featured:+). Must match the partial's
|
|
63
|
+
# strict locals declaration.
|
|
29
64
|
# @yield optional block for component content
|
|
30
|
-
# @return [ActiveSupport::SafeBuffer] rendered HTML
|
|
65
|
+
# @return [ActiveSupport::SafeBuffer] rendered HTML string
|
|
31
66
|
#
|
|
32
67
|
# @example Render a pricing card
|
|
33
|
-
# appui(:pricing_card) { "Content" }
|
|
68
|
+
# <%= appui(:pricing_card, plan: @plan) { "Content" } %>
|
|
34
69
|
#
|
|
35
|
-
# @example Render a pricing card with sub-parts
|
|
36
|
-
# appui(:pricing_card) do
|
|
37
|
-
# appui(:pricing_card, :header) { "
|
|
38
|
-
#
|
|
70
|
+
# @example Render a pricing card with composed sub-parts
|
|
71
|
+
# <%= appui(:pricing_card) do %>
|
|
72
|
+
# <%= appui(:pricing_card, :header) { "Pro Plan" } %>
|
|
73
|
+
# <%= appui(:pricing_card, :body) do %>
|
|
74
|
+
# <p>Everything you need to get started.</p>
|
|
75
|
+
# <% end %>
|
|
76
|
+
# <%= appui(:pricing_card, :footer) { "Sign up" } %>
|
|
77
|
+
# <% end %>
|
|
39
78
|
#
|
|
40
79
|
# @example Render with per-slot overrides
|
|
41
|
-
# appui(:pricing_card, ui: { header: "p-8" }) do
|
|
42
|
-
# appui(:pricing_card, :header) { "
|
|
43
|
-
# end
|
|
80
|
+
# <%= appui(:pricing_card, ui: { header: "p-8 bg-muted" }) do %>
|
|
81
|
+
# <%= appui(:pricing_card, :header) { "Enterprise" } %>
|
|
82
|
+
# <% end %>
|
|
44
83
|
#
|
|
45
|
-
# @example Share domain locals with sub-parts
|
|
46
|
-
# appui(:room_card, scope: { room: room }) do
|
|
47
|
-
# appui(:room_card, :status)
|
|
48
|
-
# appui(:room_card, :meta)
|
|
49
|
-
# end
|
|
84
|
+
# @example Share domain locals with sub-parts via scope
|
|
85
|
+
# <%= appui(:room_card, scope: { room: room }) do %>
|
|
86
|
+
# <%= appui(:room_card, :status) %>
|
|
87
|
+
# <%= appui(:room_card, :meta) %>
|
|
88
|
+
# <% end %>
|
|
50
89
|
#
|
|
51
90
|
# @example Render a collection
|
|
52
|
-
# appui(:pricing_card, collection: @plans)
|
|
91
|
+
# <%= appui(:pricing_card, collection: @plans) %>
|
|
53
92
|
def appui(component, part = nil, collection: nil, ui: nil, scope: nil, **kwargs, &block)
|
|
54
93
|
kiso_render_component(
|
|
55
94
|
component, part,
|
|
@@ -61,10 +100,20 @@ module Kiso
|
|
|
61
100
|
|
|
62
101
|
# Renders a themed HTML element for host app components.
|
|
63
102
|
#
|
|
64
|
-
#
|
|
65
|
-
# convenience so host app partials use +appui_tag+ alongside +appui()
|
|
103
|
+
# This is an alias for {ComponentHelper#kui_tag}, provided as a naming
|
|
104
|
+
# convenience so host app partials use +appui_tag+ alongside +appui()+
|
|
105
|
+
# for visual consistency.
|
|
106
|
+
#
|
|
107
|
+
# @see ComponentHelper#kui_tag for full parameter documentation
|
|
66
108
|
#
|
|
67
|
-
# @
|
|
109
|
+
# @example In a host app component partial
|
|
110
|
+
# <%# app/views/components/_pricing_card.html.erb %>
|
|
111
|
+
# <%= appui_tag :div, theme: AppThemes::Default::PricingCard,
|
|
112
|
+
# slot: "pricing-card", css_classes: css_classes,
|
|
113
|
+
# variants: { featured: featured },
|
|
114
|
+
# **component_options do %>
|
|
115
|
+
# <%= yield %>
|
|
116
|
+
# <% end %>
|
|
68
117
|
def appui_tag(...)
|
|
69
118
|
kui_tag(...)
|
|
70
119
|
end
|
|
@@ -1,48 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Kiso
|
|
2
|
-
# View helpers for rendering Kiso components.
|
|
4
|
+
# View helpers for rendering Kiso UI components in ERB templates.
|
|
5
|
+
#
|
|
6
|
+
# This module is the primary public API for the Kiso component library.
|
|
7
|
+
# It provides {#kui} for rendering engine-shipped components,
|
|
8
|
+
# {#kiso_prepare_options} for building +data-slot+ attributes in partials,
|
|
9
|
+
# and {#kui_tag} for collapsing the common +content_tag+ boilerplate.
|
|
10
|
+
#
|
|
11
|
+
# Included in all views automatically by {Kiso::Engine}.
|
|
12
|
+
#
|
|
13
|
+
# == Override layers
|
|
3
14
|
#
|
|
4
|
-
#
|
|
15
|
+
# Kiso resolves Tailwind classes through four layers (lowest to highest
|
|
16
|
+
# priority):
|
|
17
|
+
#
|
|
18
|
+
# 1. **Theme default** -- the +ClassVariants+ definition in
|
|
19
|
+
# +lib/kiso/themes/+.
|
|
20
|
+
# 2. **Global config** -- +Kiso.configure { |c| c.theme[:button] = ... }+,
|
|
21
|
+
# applied once at boot via +ClassVariants::Instance#merge+.
|
|
22
|
+
# 3. **Instance +ui:+** -- per-render slot overrides passed to +kui()+.
|
|
23
|
+
# 4. **Instance +css_classes:+** -- per-render root-element overrides,
|
|
24
|
+
# merged via +tailwind_merge+ at render time.
|
|
25
|
+
#
|
|
26
|
+
# @see AppComponentHelper#appui the equivalent helper for host app components
|
|
27
|
+
# @see UiContextHelper the request-scoped context stack that powers +ui:+
|
|
28
|
+
# @see Kiso::Configuration global theme and icon configuration
|
|
5
29
|
module ComponentHelper
|
|
6
30
|
# Renders a Kiso component partial.
|
|
7
31
|
#
|
|
32
|
+
# This is the main entry point for rendering any Kiso UI component.
|
|
8
33
|
# Components live in +app/views/kiso/components/+. Sub-parts are nested
|
|
9
|
-
# in a directory matching the parent component name.
|
|
10
|
-
#
|
|
11
|
-
# @param component [Symbol] the component name (e.g. +:badge+, +:card+)
|
|
12
|
-
# @param part [Symbol, nil] optional sub-part name (e.g. +:header+, +:footer+)
|
|
13
|
-
# @param collection [Array, nil] renders the partial once per item when present
|
|
14
|
-
# @param ui [Hash{Symbol => String}, nil] per-slot class overrides keyed by sub-part name.
|
|
15
|
-
# For parent components, the hash is pushed onto a context stack so sub-parts
|
|
16
|
-
# inherit overrides automatically. For self-rendering components, the hash is
|
|
17
|
-
# also passed as a local so the partial can apply overrides to internally
|
|
18
|
-
# rendered elements.
|
|
19
|
-
# @param scope [Hash, nil] domain locals shared from parent to sub-parts via context stack.
|
|
20
|
-
# Sub-parts receive scope values as kwargs automatically. Explicit kwargs on sub-part
|
|
21
|
-
# calls override scope values. One level deep only — no ancestor resolution.
|
|
22
|
-
# @param kwargs [Hash] locals passed to the partial (e.g. +color:+, +variant:+, +css_classes:+)
|
|
23
|
-
# @yield optional block for component content
|
|
24
|
-
# @return [ActiveSupport::SafeBuffer] rendered HTML
|
|
34
|
+
# in a directory matching the parent component name (e.g.
|
|
35
|
+
# +card/_header.html.erb+).
|
|
25
36
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
37
|
+
# Internally wraps Rails +render+ with Kiso-specific behavior:
|
|
38
|
+
# - Injects an empty +proc+ when no block is given, preventing +yield+
|
|
39
|
+
# from bubbling up the ERB rendering chain (see implementation notes)
|
|
40
|
+
# - Pushes +ui:+ and +scope:+ onto request-scoped context stacks so
|
|
41
|
+
# sub-parts inherit them automatically
|
|
42
|
+
# - Merges global config +ui:+ overrides (Layer 2) with instance +ui:+
|
|
43
|
+
# (Layer 3)
|
|
44
|
+
#
|
|
45
|
+
# @param component [Symbol] the component name (e.g. +:badge+, +:card+,
|
|
46
|
+
# +:button+). Must match a partial at
|
|
47
|
+
# +app/views/kiso/components/_<name>.html.erb+.
|
|
48
|
+
# @param part [Symbol, nil] optional sub-part name (e.g. +:header+,
|
|
49
|
+
# +:footer+, +:title+). Resolves to
|
|
50
|
+
# +app/views/kiso/components/<component>/_<part>.html.erb+.
|
|
51
|
+
# @param collection [Array, nil] when present, renders the partial once
|
|
52
|
+
# per item using Rails collection rendering.
|
|
53
|
+
# @param ui [Hash{Symbol => String}, nil] per-slot class overrides keyed
|
|
54
|
+
# by sub-part name. For parent components, the hash is pushed onto a
|
|
55
|
+
# context stack so composed sub-parts inherit overrides automatically.
|
|
56
|
+
# For self-rendering components (Alert, Dialog, Switch, etc.), the
|
|
57
|
+
# hash is also passed as a +ui:+ local so the partial can apply
|
|
58
|
+
# overrides to internally rendered elements via
|
|
59
|
+
# +Kiso::Themes::SubPart.render(class: ui[:slot_name])+.
|
|
60
|
+
# @param scope [Hash, nil] domain locals shared from parent to sub-parts
|
|
61
|
+
# via context stack. Sub-parts receive scope values as kwargs
|
|
62
|
+
# automatically. Explicit kwargs on sub-part calls override scope
|
|
63
|
+
# values. One level deep only -- no ancestor resolution.
|
|
64
|
+
# @param kwargs [Hash] locals forwarded to the partial (e.g. +color:+,
|
|
65
|
+
# +variant:+, +size:+, +css_classes:+, +icon:+). These must match the
|
|
66
|
+
# partial's +strict locals+ declaration.
|
|
67
|
+
# @yield optional block for component content. When omitted, an empty
|
|
68
|
+
# proc is injected so partials can safely use
|
|
69
|
+
# +capture { yield }.presence+ for optional block overrides.
|
|
70
|
+
# @return [ActiveSupport::SafeBuffer] rendered HTML string
|
|
71
|
+
#
|
|
72
|
+
# @example Render a simple badge
|
|
73
|
+
# <%= kui(:badge, color: :success, variant: :soft) { "Active" } %>
|
|
74
|
+
#
|
|
75
|
+
# @example Render a card with composed sub-parts
|
|
76
|
+
# <%= kui(:card) do %>
|
|
77
|
+
# <%= kui(:card, :header) do %>
|
|
78
|
+
# <%= kui(:card, :title) { "Dashboard" } %>
|
|
79
|
+
# <%= kui(:card, :description) { "Overview of your projects" } %>
|
|
80
|
+
# <% end %>
|
|
81
|
+
# <%= kui(:card, :content) do %>
|
|
82
|
+
# <p>Card body content here.</p>
|
|
83
|
+
# <% end %>
|
|
84
|
+
# <% end %>
|
|
28
85
|
#
|
|
29
86
|
# @example Render a card with per-slot overrides
|
|
30
|
-
# kui(:card, ui: { header: "p-8", title: "text-xl" }) do
|
|
31
|
-
# kui(:card, :header) do
|
|
32
|
-
# kui(:card, :title) { "Dashboard" }
|
|
33
|
-
# end
|
|
34
|
-
# end
|
|
87
|
+
# <%= kui(:card, ui: { header: "p-8", title: "text-xl" }) do %>
|
|
88
|
+
# <%= kui(:card, :header) do %>
|
|
89
|
+
# <%= kui(:card, :title) { "Dashboard" } %>
|
|
90
|
+
# <% end %>
|
|
91
|
+
# <% end %>
|
|
35
92
|
#
|
|
36
93
|
# @example Render an alert with inner element overrides
|
|
37
|
-
# kui(:alert, icon: "info", ui: { close: "opacity-50" })
|
|
94
|
+
# <%= kui(:alert, icon: "info", ui: { close: "opacity-50" }) %>
|
|
95
|
+
#
|
|
96
|
+
# @example Override root element classes
|
|
97
|
+
# <%= kui(:badge, css_classes: "uppercase tracking-wide") { "New" } %>
|
|
38
98
|
#
|
|
39
|
-
# @example
|
|
40
|
-
# kui(:
|
|
41
|
-
#
|
|
42
|
-
#
|
|
99
|
+
# @example Pass HTML attributes through to the root element
|
|
100
|
+
# <%= kui(:button, id: "submit-btn", data: { turbo: false }) { "Submit" } %>
|
|
101
|
+
#
|
|
102
|
+
# @example Share domain locals with sub-parts via scope
|
|
103
|
+
# <%= kui(:card, scope: { project: @project }) do %>
|
|
104
|
+
# <%= kui(:card, :header) %> <%# receives project: automatically %>
|
|
105
|
+
# <% end %>
|
|
43
106
|
#
|
|
44
107
|
# @example Render a collection
|
|
45
|
-
# kui(:badge, collection: @tags)
|
|
108
|
+
# <%= kui(:badge, collection: @tags) %>
|
|
46
109
|
def kui(component, part = nil, collection: nil, ui: nil, scope: nil, **kwargs, &block)
|
|
47
110
|
kiso_render_component(
|
|
48
111
|
component, part,
|
|
@@ -52,55 +115,125 @@ module Kiso
|
|
|
52
115
|
)
|
|
53
116
|
end
|
|
54
117
|
|
|
55
|
-
# Prepares +component_options+ for use with +content_tag
|
|
118
|
+
# Prepares +component_options+ for use with +content_tag+ in component
|
|
119
|
+
# partials.
|
|
56
120
|
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# (
|
|
121
|
+
# Extracts the caller's +data:+ hash from +component_options+, merges it
|
|
122
|
+
# with the mandatory +data-slot+ attribute (shadcn v4 convention) and any
|
|
123
|
+
# additional data attributes (typically Stimulus bindings). Returns the
|
|
124
|
+
# merged data hash ready for +content_tag+.
|
|
60
125
|
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# @param data_attrs [Hash] additional data attributes (e.g. +controller: "kiso--toggle"+)
|
|
65
|
-
# @return [Hash] merged data attributes hash for +content_tag+
|
|
66
|
-
# @raise [ArgumentError] if +component_options+ contains a +class:+ key
|
|
126
|
+
# This method mutates +component_options+ by deleting the +data:+ key so
|
|
127
|
+
# it is not double-passed when the partial splats +**component_options+
|
|
128
|
+
# onto +content_tag+.
|
|
67
129
|
#
|
|
68
|
-
# @
|
|
69
|
-
#
|
|
130
|
+
# @param component_options [Hash] the +**component_options+ splat from
|
|
131
|
+
# the partial's strict locals. Callers pass arbitrary HTML attributes
|
|
132
|
+
# here (e.g. +id:+, +aria:+, +data:+). The +data:+ key is extracted
|
|
133
|
+
# and merged; all other keys pass through to +content_tag+.
|
|
134
|
+
# @param slot [String] the +data-slot+ value in kebab-case
|
|
135
|
+
# (e.g. +"badge"+, +"card-header"+, +"alert-title"+). Every component
|
|
136
|
+
# and sub-part must have a unique slot name.
|
|
137
|
+
# @param data_attrs [Hash] additional data attributes merged into the
|
|
138
|
+
# result (e.g. +controller: "kiso--toggle"+, +action: "click->..."+).
|
|
139
|
+
# For +action:+ and +controller:+, user and component values are
|
|
140
|
+
# concatenated (space-separated) so both Stimulus bindings apply.
|
|
141
|
+
# All other keys use standard merge (component wins on conflict).
|
|
142
|
+
# @return [Hash] merged data attributes hash suitable for the +data:+
|
|
143
|
+
# kwarg of +content_tag+.
|
|
144
|
+
# @raise [ArgumentError] if +component_options+ contains a +class:+ key.
|
|
145
|
+
# Kiso uses +css_classes:+ to avoid conflicts with Ruby's +class+
|
|
146
|
+
# method and to make the override intent explicit.
|
|
147
|
+
#
|
|
148
|
+
# @example Basic usage in a component partial
|
|
149
|
+
# <%# _badge.html.erb %>
|
|
150
|
+
# <%= content_tag :span,
|
|
151
|
+
# class: Kiso::Themes::Badge.render(color: color, class: css_classes),
|
|
152
|
+
# data: kiso_prepare_options(component_options, slot: "badge"),
|
|
153
|
+
# **component_options do %>
|
|
154
|
+
# <%= yield %>
|
|
155
|
+
# <% end %>
|
|
70
156
|
#
|
|
71
157
|
# @example With a Stimulus controller
|
|
72
|
-
# data: kiso_prepare_options(component_options, slot: "toggle",
|
|
158
|
+
# data: kiso_prepare_options(component_options, slot: "toggle",
|
|
159
|
+
# controller: "kiso--toggle")
|
|
160
|
+
#
|
|
161
|
+
# @example Caller passes data attributes through
|
|
162
|
+
# <%# In the view: %>
|
|
163
|
+
# <%= kui(:badge, data: { turbo_frame: "results" }) { "Active" } %>
|
|
164
|
+
# <%# Inside the partial, kiso_prepare_options merges caller's data
|
|
165
|
+
# with { slot: "badge" }, producing:
|
|
166
|
+
# { slot: "badge", turbo_frame: "results" } %>
|
|
73
167
|
def kiso_prepare_options(component_options, slot:, **data_attrs)
|
|
74
168
|
if component_options.key?(:class)
|
|
75
169
|
raise ArgumentError, "Use css_classes: instead of class: for Kiso components"
|
|
76
170
|
end
|
|
77
171
|
|
|
78
|
-
|
|
172
|
+
user_data = component_options.delete(:data) || {}
|
|
173
|
+
component_data = {slot: slot, **data_attrs}
|
|
174
|
+
|
|
175
|
+
# Stimulus data-action and data-controller are space-separated and
|
|
176
|
+
# additive — concatenate rather than overwrite so both the component's
|
|
177
|
+
# and user's bindings apply.
|
|
178
|
+
CONCATENABLE_DATA_KEYS.each do |key|
|
|
179
|
+
if user_data.key?(key) && component_data.key?(key)
|
|
180
|
+
component_data[key] = "#{user_data.delete(key)} #{component_data[key]}"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
user_data.merge(component_data)
|
|
79
185
|
end
|
|
80
186
|
|
|
187
|
+
# Data attribute keys whose values are space-separated lists in Stimulus.
|
|
188
|
+
# When both the user and the component provide these, values are
|
|
189
|
+
# concatenated rather than overwritten.
|
|
190
|
+
CONCATENABLE_DATA_KEYS = %i[action controller].freeze
|
|
191
|
+
|
|
81
192
|
# Renders a themed HTML element with Kiso conventions.
|
|
82
193
|
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
194
|
+
# A convenience method that collapses the common three-step boilerplate
|
|
195
|
+
# in component partials:
|
|
196
|
+
#
|
|
197
|
+
# 1. +theme.render(**variants, class: css_classes)+ -- compute classes
|
|
198
|
+
# 2. +kiso_prepare_options(component_options, slot: ...)+ -- build data attrs
|
|
199
|
+
# 3. +content_tag(tag, class: ..., data: ..., **component_options)+ -- render
|
|
200
|
+
#
|
|
201
|
+
# Into a single call. Prefer this over manual +content_tag+ wiring in
|
|
202
|
+
# component partials for consistency.
|
|
86
203
|
#
|
|
87
204
|
# @param tag [Symbol] HTML element name (e.g. +:div+, +:span+, +:button+)
|
|
88
|
-
# @param theme [ClassVariants::Instance] the theme module to render
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# @param
|
|
92
|
-
# (e.g. +
|
|
93
|
-
# @param
|
|
94
|
-
#
|
|
205
|
+
# @param theme [ClassVariants::Instance] the theme module to render
|
|
206
|
+
# classes from (e.g. +Kiso::Themes::Badge+, +Kiso::Themes::Card+).
|
|
207
|
+
# Must respond to +#render(**variants, class:)+.
|
|
208
|
+
# @param slot [String] the +data-slot+ value in kebab-case
|
|
209
|
+
# (e.g. +"badge"+, +"card-header"+).
|
|
210
|
+
# @param css_classes [String] caller's class overrides, merged via
|
|
211
|
+
# +tailwind_merge+ inside the theme's +#render+ method. Defaults to
|
|
212
|
+
# an empty string.
|
|
213
|
+
# @param variants [Hash] variant key-value pairs forwarded to
|
|
214
|
+
# +theme.render+ (e.g. +{ size: :md, color: :primary, variant: :soft }+).
|
|
215
|
+
# @param component_options [Hash] HTML attributes forwarded to
|
|
216
|
+
# +content_tag+ (e.g. +id:+, +aria:+, +type:+). A +data:+ key is
|
|
217
|
+
# extracted and merged with the slot and any Stimulus bindings.
|
|
95
218
|
# @yield optional block for element content
|
|
96
219
|
# @return [ActiveSupport::SafeBuffer] rendered HTML
|
|
97
220
|
#
|
|
98
221
|
# @example In a component partial
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
222
|
+
# <%# _badge.html.erb %>
|
|
223
|
+
# <%= kui_tag :span, theme: Kiso::Themes::Badge, slot: "badge",
|
|
224
|
+
# css_classes: css_classes,
|
|
225
|
+
# variants: { color: color, variant: variant, size: size },
|
|
226
|
+
# **component_options do %>
|
|
227
|
+
# <%= yield %>
|
|
228
|
+
# <% end %>
|
|
229
|
+
#
|
|
230
|
+
# @example Button with type attribute
|
|
231
|
+
# <%= kui_tag :button, theme: Kiso::Themes::Button, slot: "button",
|
|
232
|
+
# css_classes: css_classes,
|
|
233
|
+
# variants: { color: color, variant: variant, size: size },
|
|
234
|
+
# type: "button", **component_options do %>
|
|
235
|
+
# <%= yield %>
|
|
236
|
+
# <% end %>
|
|
104
237
|
def kui_tag(tag, theme:, slot:, css_classes: "", variants: {}, **component_options, &block)
|
|
105
238
|
html_options = {
|
|
106
239
|
class: theme.render(**variants, class: css_classes),
|
|
@@ -117,15 +250,33 @@ module Kiso
|
|
|
117
250
|
|
|
118
251
|
private
|
|
119
252
|
|
|
120
|
-
# Shared rendering pipeline for both kui
|
|
253
|
+
# Shared rendering pipeline for both {#kui} and {AppComponentHelper#appui}.
|
|
254
|
+
#
|
|
255
|
+
# Handles two distinct code paths:
|
|
256
|
+
#
|
|
257
|
+
# - **Parent components** (+part+ is +nil+): merges global and instance
|
|
258
|
+
# +ui:+ layers, pushes +ui:+ and +scope:+ onto their respective
|
|
259
|
+
# context stacks, renders the partial, then pops the stacks in an
|
|
260
|
+
# +ensure+ block.
|
|
261
|
+
# - **Sub-parts** (+part+ is present): reads the parent's context stacks
|
|
262
|
+
# to inherit +ui:+ slot overrides and +scope:+ domain locals, then
|
|
263
|
+
# renders the partial with the merged kwargs.
|
|
264
|
+
#
|
|
265
|
+
# The empty +proc {}+ guard is critical: without it, partials that call
|
|
266
|
+
# +capture { yield }.presence+ for optional block overrides would have
|
|
267
|
+
# their +yield+ bubble through nested +content_tag+ blocks all the way
|
|
268
|
+
# to the layout's +<%= yield %>+, capturing the entire page template.
|
|
121
269
|
#
|
|
122
270
|
# @param component [Symbol] the component name
|
|
123
271
|
# @param part [Symbol, nil] optional sub-part name
|
|
124
|
-
# @param path_prefix [String] partial path prefix
|
|
125
|
-
#
|
|
126
|
-
# @param
|
|
272
|
+
# @param path_prefix [String] partial path prefix
|
|
273
|
+
# (+"kiso/components"+ for engine, +"components"+ for host app)
|
|
274
|
+
# @param collection [Array, nil] renders the partial once per item
|
|
275
|
+
# @param ui [Hash{Symbol => String}, nil] per-slot class overrides
|
|
127
276
|
# @param scope [Hash, nil] domain locals shared from parent to sub-parts
|
|
128
|
-
# @param merge_global_ui [Boolean] whether to merge global config ui
|
|
277
|
+
# @param merge_global_ui [Boolean] whether to merge global config ui
|
|
278
|
+
# layer. +true+ for {#kui} (engine components have global config),
|
|
279
|
+
# +false+ for {AppComponentHelper#appui} (host apps own the source).
|
|
129
280
|
# @param kwargs [Hash] locals passed to the partial
|
|
130
281
|
# @param block [Proc] optional block for component content
|
|
131
282
|
# @return [ActiveSupport::SafeBuffer] rendered HTML
|
|
@@ -198,12 +349,18 @@ module Kiso
|
|
|
198
349
|
end
|
|
199
350
|
end
|
|
200
351
|
|
|
201
|
-
#
|
|
202
|
-
#
|
|
352
|
+
# Merges global config +ui:+ overrides (Layer 2) with per-instance
|
|
353
|
+
# +ui:+ overrides (Layer 3).
|
|
203
354
|
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
#
|
|
355
|
+
# When both layers define the same slot, their class strings are
|
|
356
|
+
# concatenated (space-separated). Conflicts are resolved later by
|
|
357
|
+
# +tailwind_merge+ when the sub-part calls +theme.render(class:)+.
|
|
358
|
+
#
|
|
359
|
+
# @param component [Symbol] the component name (e.g. +:card+)
|
|
360
|
+
# @param instance_ui [Hash{Symbol => String}, nil] per-instance ui
|
|
361
|
+
# overrides passed to +kui(:card, ui: { ... })+
|
|
362
|
+
# @return [Hash{Symbol => String}] merged ui hash with all slot
|
|
363
|
+
# overrides from both layers
|
|
207
364
|
def kiso_merge_ui_layers(component, instance_ui)
|
|
208
365
|
global_ui = Kiso.config.theme.dig(component, :ui)
|
|
209
366
|
return instance_ui || {} if global_ui.nil? || global_ui.empty?
|