kiso 0.6.5.pre → 0.6.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6945cef88ebb1ed0f85502844448e5014a0fbeea1a4f723032d6d17c9fb61083
4
- data.tar.gz: a0b8bd38fdaed32409c35930930467af8658ebc05ff85e2375d21ac9f96d798d
3
+ metadata.gz: 37ad37d5141bb1b542e4e44cdd206772ad63c60b66d83cb24c02fb9e21fe451e
4
+ data.tar.gz: 67d62e3a1879e40cb52b99e18d720c07ed7b390067c38a306230966e5bd3855f
5
5
  SHA512:
6
- metadata.gz: c11fd1e71b2eb52fe9d0a71633c7890b2c0ea89ab29bcd2590299f736b38165f7568ec43ce33fe4b48ab4d2fb072130a043e8062b69642fd4b43d25726d818b9
7
- data.tar.gz: 1674670d2765c08e3a11cc2b0bc7ba2c0138dd83abc82677bdefe9f7b52ac219942d60ffe34eb3a47841e58beac896e3d8ba5ec345440ed26feaa9ad9d882edb
6
+ metadata.gz: 8212fb15a724a219925764fcb5d95ef4b3eb98e261e35ede25086575f5f6ef718aea5579449477bdca4a9d340093bf671762755204f88626adcb6bb11a5d98b9
7
+ data.tar.gz: e076abe507c3f6e880f63fda2fd0ece8af98457f908c2b6f92814d8e391feacde16f6761b3204ea4a21da974f1af4a9eb5fe1e79a6a80fcc3fd0986a3e1d0aa4
@@ -28,6 +28,7 @@
28
28
  @import "./input-otp.css";
29
29
  @import "./slider.css";
30
30
  @import "./tooltip.css";
31
+ @import "./progress.css";
31
32
 
32
33
  /* === Tailwind Source Scanning ===
33
34
  Tell Tailwind v4 to scan Kiso's own source files for class references.
@@ -0,0 +1,67 @@
1
+ /* ── Progress ─────────────────────────────────────────────────────────
2
+ * Indeterminate animation keyframes for the progress indicator.
3
+ * 8 keyframes: 4 animation styles × 2 orientations (horizontal/vertical).
4
+ *
5
+ * Why CSS: @keyframes definitions and prefers-reduced-motion media
6
+ * queries cannot be expressed in ERB/Tailwind utilities.
7
+ * ──────────────────────────────────────────────────────────────────── */
8
+
9
+ /* === Carousel === */
10
+
11
+ @keyframes carousel {
12
+ 0%, 100% { width: 50%; }
13
+ 0% { transform: translateX(-100%); }
14
+ 100% { transform: translateX(200%); }
15
+ }
16
+
17
+ @keyframes carousel-vertical {
18
+ 0%, 100% { height: 50%; }
19
+ 0% { transform: translateY(-100%); }
20
+ 100% { transform: translateY(200%); }
21
+ }
22
+
23
+ /* === Carousel Inverse === */
24
+
25
+ @keyframes carousel-inverse {
26
+ 0%, 100% { width: 50%; }
27
+ 0% { transform: translateX(200%); }
28
+ 100% { transform: translateX(-100%); }
29
+ }
30
+
31
+ @keyframes carousel-inverse-vertical {
32
+ 0%, 100% { height: 50%; }
33
+ 0% { transform: translateY(200%); }
34
+ 100% { transform: translateY(-100%); }
35
+ }
36
+
37
+ /* === Swing === */
38
+
39
+ @keyframes swing {
40
+ 0%, 100% { width: 50%; transform: translateX(-25%); }
41
+ 50% { transform: translateX(125%); }
42
+ }
43
+
44
+ @keyframes swing-vertical {
45
+ 0%, 100% { height: 50%; transform: translateY(-25%); }
46
+ 50% { transform: translateY(125%); }
47
+ }
48
+
49
+ /* === Elastic === */
50
+
51
+ @keyframes elastic {
52
+ 0%, 100% { width: 50%; margin-left: 25%; }
53
+ 50% { width: 90%; margin-left: 5%; }
54
+ }
55
+
56
+ @keyframes elastic-vertical {
57
+ 0%, 100% { height: 50%; margin-top: 25%; }
58
+ 50% { height: 90%; margin-top: 5%; }
59
+ }
60
+
61
+ /* === Reduced motion === */
62
+
63
+ @media (prefers-reduced-motion: reduce) {
64
+ [data-slot="progress-indicator"][data-state="indeterminate"] {
65
+ animation-duration: 0s !important;
66
+ }
67
+ }
@@ -54,11 +54,23 @@ export default class extends Controller {
54
54
  this._tooltipId = this.contentTarget.id || `tooltip-${crypto.randomUUID().slice(0, 8)}`
55
55
  this.contentTarget.id = this._tooltipId
56
56
  this._triggerEl.setAttribute("aria-describedby", this._tooltipId)
57
+
58
+ // Strip native title tooltip to prevent double tooltip
59
+ if (this._triggerEl.hasAttribute("title")) {
60
+ this._originalTitle = this._triggerEl.getAttribute("title")
61
+ this._triggerEl.removeAttribute("title")
62
+ }
57
63
  }
58
64
 
59
65
  disconnect() {
60
66
  this._cleanupPosition?.()
61
67
  this._clearTimers()
68
+
69
+ // Restore native title attribute
70
+ if (this._originalTitle != null) {
71
+ this._triggerEl.setAttribute("title", this._originalTitle)
72
+ this._originalTitle = null
73
+ }
62
74
  }
63
75
 
64
76
  /**
@@ -37,7 +37,8 @@
37
37
  <% end %>
38
38
  <% else %>
39
39
  <% component_options[:type] = type
40
- component_options[:disabled] = true if is_disabled %>
40
+ component_options[:disabled] = true if is_disabled
41
+ component_options[:form] = form if form.is_a?(String) %>
41
42
  <%= content_tag :button, class: css, data: data, **component_options do %>
42
43
  <%= kiso_component_icon(:spinner, class: "animate-spin") if loading %>
43
44
  <%= yield %>
@@ -0,0 +1,74 @@
1
+ <%# locals: (value: nil, max: 100, status: false, color: :primary, size: :md, animation: :carousel, orientation: :horizontal, inverted: false, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Div-based progress bar with optional status text and step labels.
3
+ Indeterminate state when value: is nil (animated indicator via CSS).
4
+ Steps mode when max: is an array of labels. %>
5
+ <% is_indeterminate = value.nil? %>
6
+ <% has_steps = max.is_a?(Array) %>
7
+ <% real_max = has_steps ? [max.length - 1, 1].max : [max.to_i, 1].max %>
8
+ <% percent = is_indeterminate ? 0 : ((value.to_f / real_max) * 100).clamp(0, 100) %>
9
+ <%
10
+ vertical = orientation == :vertical
11
+
12
+ # Indicator transform
13
+ if is_indeterminate
14
+ indicator_style = nil
15
+ elsif vertical
16
+ indicator_style = inverted ? "transform: translateY(#{100 - percent}%)" : "transform: translateY(-#{100 - percent}%)"
17
+ else
18
+ indicator_style = inverted ? "transform: translateX(#{100 - percent}%)" : "transform: translateX(-#{100 - percent}%)"
19
+ end
20
+ %>
21
+ <%= content_tag :div,
22
+ class: Kiso::Themes::Progress.render(orientation: orientation, class: css_classes),
23
+ data: kiso_prepare_options(component_options, slot: "progress", orientation: orientation),
24
+ **component_options do %>
25
+ <% if !is_indeterminate && status %>
26
+ <%= tag.div(
27
+ class: Kiso::Themes::ProgressStatus.render(orientation: orientation, size: size, class: ui[:status]),
28
+ style: vertical ? "height: #{percent}%" : "width: #{percent}%",
29
+ data: {slot: "progress-status"}) do %>
30
+ <%= "#{percent.round}%" %>
31
+ <% end %>
32
+ <% end %>
33
+ <%= tag.div(
34
+ class: Kiso::Themes::ProgressTrack.render(orientation: orientation, size: size, class: ui[:track]),
35
+ role: "progressbar",
36
+ aria: {
37
+ valuenow: (is_indeterminate ? nil : value),
38
+ valuemin: 0,
39
+ valuemax: real_max,
40
+ label: component_options.dig(:aria, :label) || t("kiso.progress.label")
41
+ },
42
+ style: "transform: translateZ(0)",
43
+ data: {slot: "progress-track"}) do %>
44
+ <%= tag.div(
45
+ class: Kiso::Themes::ProgressIndicator.render(color: color, animation: animation, orientation: orientation, class: ui[:indicator]),
46
+ style: indicator_style,
47
+ data: {
48
+ slot: "progress-indicator",
49
+ state: is_indeterminate ? "indeterminate" : "loading"
50
+ }) %>
51
+ <% end %>
52
+ <% if has_steps %>
53
+ <%= tag.div(
54
+ class: Kiso::Themes::ProgressSteps.render(color: color, size: size, class: ui[:steps]),
55
+ data: {slot: "progress-steps"}) do %>
56
+ <% max.each_with_index do |step_label, index| %>
57
+ <%
58
+ step_variant = if index == value && index == 0
59
+ :first
60
+ elsif index == value && index == real_max
61
+ :last
62
+ elsif index == value
63
+ :active
64
+ else
65
+ :other
66
+ end
67
+ %>
68
+ <%= tag.div(step_label,
69
+ class: Kiso::Themes::ProgressStep.render(step: step_variant, class: ui[:step]),
70
+ data: {slot: "progress-step"}) %>
71
+ <% end %>
72
+ <% end %>
73
+ <% end %>
74
+ <% end %>
@@ -1,6 +1,7 @@
1
- <%# locals: (css_classes: "", **component_options) %>
1
+ <%# locals: (open_icon: nil, closed_icon: nil, css_classes: "", **component_options) %>
2
2
  <%# Sidebar collapse toggle for desktop. Shows open/closed panel icons that swap
3
- visibility based on sidebar state via CSS. Placed inside the sidebar itself. %>
3
+ visibility based on sidebar state via CSS. Placed inside the sidebar itself.
4
+ Pass open_icon: / closed_icon: to override the default panel icons per-instance. %>
4
5
  <%= content_tag :button,
5
6
  class: Kiso::Themes::DashboardSidebarCollapse.render(class: css_classes),
6
7
  data: kiso_prepare_options(component_options, slot: "dashboard-sidebar-collapse",
@@ -9,6 +10,6 @@
9
10
  aria: { label: t("kiso.dashboard_sidebar.collapse"), expanded: "false", controls: "dashboard-sidebar" },
10
11
  type: "button",
11
12
  **component_options do %>
12
- <span data-slot="collapse-icon-open"><%= kiso_component_icon(:panel_left_close) %></span>
13
- <span data-slot="collapse-icon-closed"><%= kiso_component_icon(:panel_left_open) %></span>
13
+ <span data-slot="collapse-icon-open"><%= open_icon || kiso_component_icon(:panel_left_close) %></span>
14
+ <span data-slot="collapse-icon-closed"><%= closed_icon || kiso_component_icon(:panel_left_open) %></span>
14
15
  <% end %>
@@ -28,6 +28,8 @@ en:
28
28
  loading: "Loading"
29
29
  tooltip:
30
30
  label: "Tooltip"
31
+ progress:
32
+ label: "Progress"
31
33
  pagination:
32
34
  label: "pagination"
33
35
  more_pages: "More pages"
@@ -136,6 +136,10 @@ module Kiso
136
136
  slider_track: {base: "rounded-none"},
137
137
  slider_thumb: {base: "rounded-none"},
138
138
 
139
+ # Progress: rounded-full → rounded-none
140
+ progress_track: {base: "rounded-none"},
141
+ progress_indicator: {base: "rounded-none"},
142
+
139
143
  # RadioGroup indicator: rounded-full → rounded-none
140
144
  radio_group_item: {base: "rounded-none"},
141
145
 
@@ -0,0 +1,161 @@
1
+ module Kiso
2
+ module Themes
3
+ # Visual progress bar with color, size, and orientation axes.
4
+ #
5
+ # Div-based structure (not native <progress>) for full Tailwind control.
6
+ # Optional status percentage text and step labels (when +max:+ is an array).
7
+ # Indeterminate animation when +value:+ is nil.
8
+ #
9
+ # @example
10
+ # Progress.render(orientation: :horizontal)
11
+ #
12
+ # Variants:
13
+ # - +orientation+ — :horizontal (default), :vertical
14
+ #
15
+ # Sub-parts: {ProgressTrack}, {ProgressIndicator}, {ProgressStatus},
16
+ # {ProgressSteps}, {ProgressStep}
17
+ #
18
+ # shadcn base: (no direct equivalent — shadcn wraps in Radix ProgressRoot)
19
+ Progress = ClassVariants.build(
20
+ base: "text-foreground gap-2",
21
+ variants: {
22
+ orientation: {
23
+ horizontal: "w-full flex flex-col",
24
+ vertical: "h-full flex flex-row-reverse"
25
+ }
26
+ },
27
+ defaults: {orientation: :horizontal}
28
+ )
29
+
30
+ # Track (bar background) with orientation × size compound variants.
31
+ #
32
+ # shadcn base: bg-primary/20 relative h-2 w-full overflow-hidden rounded-full
33
+ ProgressTrack = ClassVariants.build(
34
+ base: "relative overflow-hidden rounded-full bg-accented",
35
+ variants: {
36
+ orientation: {
37
+ horizontal: "w-full",
38
+ vertical: "h-full"
39
+ },
40
+ size: {
41
+ xs: "", sm: "", md: "", lg: "", xl: ""
42
+ }
43
+ },
44
+ compound_variants: [
45
+ # == horizontal sizes (height) ==
46
+ {orientation: :horizontal, size: :xs, class: "h-0.5"},
47
+ {orientation: :horizontal, size: :sm, class: "h-1"},
48
+ {orientation: :horizontal, size: :md, class: "h-2"},
49
+ {orientation: :horizontal, size: :lg, class: "h-3"},
50
+ {orientation: :horizontal, size: :xl, class: "h-4"},
51
+ # == vertical sizes (width) ==
52
+ {orientation: :vertical, size: :xs, class: "w-0.5"},
53
+ {orientation: :vertical, size: :sm, class: "w-1"},
54
+ {orientation: :vertical, size: :md, class: "w-2"},
55
+ {orientation: :vertical, size: :lg, class: "w-3"},
56
+ {orientation: :vertical, size: :xl, class: "w-4"}
57
+ ],
58
+ defaults: {orientation: :horizontal, size: :md}
59
+ )
60
+
61
+ # Indicator (fill bar) — color axis maps directly to bg-{color}.
62
+ # No variant axis (solid/outline/soft/subtle) — just direct color.
63
+ # Indeterminate animations gated by data-[state=indeterminate]: selector
64
+ # so the class is always present but only activates when indeterminate.
65
+ #
66
+ # shadcn base: bg-primary h-full w-full flex-1 transition-all
67
+ ProgressIndicator = ClassVariants.build(
68
+ base: "rounded-full size-full transition-transform duration-200 ease-out",
69
+ variants: {
70
+ color: {
71
+ primary: "bg-primary",
72
+ secondary: "bg-secondary",
73
+ success: "bg-success",
74
+ info: "bg-info",
75
+ warning: "bg-warning",
76
+ error: "bg-error",
77
+ neutral: "bg-inverted"
78
+ },
79
+ animation: {
80
+ carousel: "", carousel_inverse: "", swing: "", elastic: ""
81
+ },
82
+ orientation: {
83
+ horizontal: "", vertical: ""
84
+ }
85
+ },
86
+ compound_variants: [
87
+ # == indeterminate animation: horizontal ==
88
+ {animation: :carousel, orientation: :horizontal,
89
+ class: "data-[state=indeterminate]:animate-[carousel_2s_ease-in-out_infinite]"},
90
+ {animation: :carousel_inverse, orientation: :horizontal,
91
+ class: "data-[state=indeterminate]:animate-[carousel-inverse_2s_ease-in-out_infinite]"},
92
+ {animation: :swing, orientation: :horizontal,
93
+ class: "data-[state=indeterminate]:animate-[swing_2s_ease-in-out_infinite]"},
94
+ {animation: :elastic, orientation: :horizontal,
95
+ class: "data-[state=indeterminate]:animate-[elastic_2s_ease-in-out_infinite]"},
96
+ # == indeterminate animation: vertical ==
97
+ {animation: :carousel, orientation: :vertical,
98
+ class: "data-[state=indeterminate]:animate-[carousel-vertical_2s_ease-in-out_infinite]"},
99
+ {animation: :carousel_inverse, orientation: :vertical,
100
+ class: "data-[state=indeterminate]:animate-[carousel-inverse-vertical_2s_ease-in-out_infinite]"},
101
+ {animation: :swing, orientation: :vertical,
102
+ class: "data-[state=indeterminate]:animate-[swing-vertical_2s_ease-in-out_infinite]"},
103
+ {animation: :elastic, orientation: :vertical,
104
+ class: "data-[state=indeterminate]:animate-[elastic-vertical_2s_ease-in-out_infinite]"}
105
+ ],
106
+ defaults: {color: :primary, animation: :carousel, orientation: :horizontal}
107
+ )
108
+
109
+ # Status text showing percentage above/beside the bar.
110
+ ProgressStatus = ClassVariants.build(
111
+ base: "flex text-muted-foreground transition-[width] duration-200",
112
+ variants: {
113
+ orientation: {
114
+ horizontal: "flex-row items-center justify-end min-w-fit",
115
+ vertical: "flex-col justify-end min-h-fit"
116
+ },
117
+ size: {
118
+ xs: "text-xs", sm: "text-xs", md: "text-sm", lg: "text-sm", xl: "text-base"
119
+ }
120
+ },
121
+ defaults: {orientation: :horizontal, size: :md}
122
+ )
123
+
124
+ # Container for step labels — grid overlay technique.
125
+ ProgressSteps = ClassVariants.build(
126
+ base: "grid items-end",
127
+ variants: {
128
+ color: {
129
+ primary: "text-primary",
130
+ secondary: "text-secondary",
131
+ success: "text-success",
132
+ info: "text-info",
133
+ warning: "text-warning",
134
+ error: "text-error",
135
+ # text-inverted (not text-inverted-foreground) — step labels sit on
136
+ # the page background, so we want dark-on-light / light-on-dark text.
137
+ neutral: "text-inverted"
138
+ },
139
+ size: {
140
+ xs: "text-xs", sm: "text-xs", md: "text-sm", lg: "text-sm", xl: "text-base"
141
+ }
142
+ },
143
+ defaults: {color: :primary, size: :md}
144
+ )
145
+
146
+ # Individual step label — stacked in same grid cell via row/col-start-1.
147
+ # Only the active step is visible (opacity-100), others hidden (opacity-0).
148
+ ProgressStep = ClassVariants.build(
149
+ base: "truncate text-end row-start-1 col-start-1 transition-opacity",
150
+ variants: {
151
+ step: {
152
+ active: "opacity-100",
153
+ first: "opacity-50",
154
+ other: "opacity-0",
155
+ last: ""
156
+ }
157
+ },
158
+ defaults: {step: :other}
159
+ )
160
+ end
161
+ end
@@ -12,7 +12,7 @@ module Kiso
12
12
  # Kiso: bg-inverted text-inverted-foreground (semantic equivalents)
13
13
  TooltipContent = ClassVariants.build(
14
14
  base: "bg-inverted text-inverted-foreground px-3 py-1.5 text-xs rounded-md " \
15
- "flex items-center gap-1.5 select-none w-max max-w-xs"
15
+ "flex items-center gap-1.5 select-none w-max max-w-xs overflow-hidden"
16
16
  )
17
17
 
18
18
  # Arrow element pointing from tooltip content to the trigger.
data/lib/kiso/version.rb CHANGED
@@ -5,5 +5,5 @@ module Kiso
5
5
  # Updated by +bin/release+.
6
6
  #
7
7
  # @return [String]
8
- VERSION = "0.6.5.pre"
8
+ VERSION = "0.6.6.pre"
9
9
  end
data/lib/kiso.rb CHANGED
@@ -53,6 +53,7 @@ require "kiso/themes/skeleton"
53
53
  require "kiso/themes/spinner"
54
54
  require "kiso/themes/tooltip"
55
55
  require "kiso/themes/slider"
56
+ require "kiso/themes/progress"
56
57
  require "kiso/themes/layout"
57
58
  require "kiso/icons"
58
59
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kiso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5.pre
4
+ version: 0.6.6.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Clarke
@@ -91,6 +91,7 @@ files:
91
91
  - app/assets/tailwind/kiso/palettes/orange.css
92
92
  - app/assets/tailwind/kiso/palettes/violet.css
93
93
  - app/assets/tailwind/kiso/palettes/zinc.css
94
+ - app/assets/tailwind/kiso/progress.css
94
95
  - app/assets/tailwind/kiso/radio-group.css
95
96
  - app/assets/tailwind/kiso/slider.css
96
97
  - app/assets/tailwind/kiso/tooltip.css
@@ -166,6 +167,7 @@ files:
166
167
  - app/views/kiso/components/_page_section.html.erb
167
168
  - app/views/kiso/components/_pagination.html.erb
168
169
  - app/views/kiso/components/_popover.html.erb
170
+ - app/views/kiso/components/_progress.html.erb
169
171
  - app/views/kiso/components/_radio_group.html.erb
170
172
  - app/views/kiso/components/_select.html.erb
171
173
  - app/views/kiso/components/_select_native.html.erb
@@ -386,6 +388,7 @@ files:
386
388
  - lib/kiso/themes/page.rb
387
389
  - lib/kiso/themes/pagination.rb
388
390
  - lib/kiso/themes/popover.rb
391
+ - lib/kiso/themes/progress.rb
389
392
  - lib/kiso/themes/radio_group.rb
390
393
  - lib/kiso/themes/select.rb
391
394
  - lib/kiso/themes/select_native.rb