keystone_ui 0.40.0 → 0.42.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/keystone_ui/line_chart_controller.js +98 -13
- data/app/components/keystone/ui/caption_component.html.erb +1 -0
- data/app/components/keystone/ui/caption_component.rb +13 -0
- data/app/components/keystone/ui/heading_component.html.erb +8 -0
- data/app/components/keystone/ui/heading_component.rb +21 -0
- data/app/components/keystone/ui/navigation_component.html.erb +21 -9
- data/app/components/keystone/ui/navigation_component.rb +1 -0
- data/app/components/keystone/ui/paragraph_component.html.erb +1 -0
- data/app/components/keystone/ui/paragraph_component.rb +13 -0
- data/app/helpers/keystone_ui_helper.rb +12 -0
- data/lib/keystone_ui/configuration.rb +6 -0
- data/lib/keystone_ui/navigation_group.rb +6 -1
- data/lib/keystone_ui/safelist.rb +3 -0
- data/lib/keystone_ui/version.rb +1 -1
- data/lib/keystone_ui/visible_navigation.rb +2 -2
- data/the_local/agents/keystone_ui-develop.md +11 -1
- data/the_local/agents/keystone_ui-install.md +7 -0
- data/the_local/interface.yml +3 -0
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28436277e0b304a48d5093a2220bb1df0e04f94315f085781e93cb34207ea654
|
|
4
|
+
data.tar.gz: 82bb8f8de9b4132f47da04e7ab3423ba3c73e216b2ae79659f8709b52888bfde
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e63c3495c2b1709109cb25584e9a9e5c5b7fcaef4cfb1cc1e948641b5ae06a22acc3703f52d038d69a7371b57d39a554d75a8ac671e90662f525734979ab8fd
|
|
7
|
+
data.tar.gz: 8941b3263e4944bbfc4e6d6dbc8eb82f74908ee51b922f9c6f61b6cb9c4af869171c22b8e5cbdc4bef6e7c238e740d2f531b143cd5abaddba2a5d73b36d34b1a
|
|
@@ -11,18 +11,92 @@ const MILLISECONDS_IN_A_DAY = 86_400_000
|
|
|
11
11
|
export const dayLabel = (day) =>
|
|
12
12
|
new Date(day * MILLISECONDS_IN_A_DAY).toLocaleDateString("en-US", { timeZone: "UTC", month: "short", day: "numeric", year: "numeric" })
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const DATED_AXIS = { type: "linear", bounds: "data" }
|
|
15
|
+
const DATED_TICKS = { precision: 0, callback: dayLabel }
|
|
16
|
+
const DATED_TOOLTIP = { callbacks: { title: ([point]) => dayLabel(point.parsed.x) } }
|
|
17
|
+
|
|
18
|
+
const tooltip = (theme) => ({
|
|
19
|
+
backgroundColor: theme.surface,
|
|
20
|
+
borderColor: theme.border,
|
|
21
|
+
borderWidth: 1,
|
|
22
|
+
titleColor: theme.text,
|
|
23
|
+
bodyColor: theme.text
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const DRAW_IN_MILLISECONDS = 600
|
|
27
|
+
|
|
28
|
+
const pointStep = (context) => DRAW_IN_MILLISECONDS / context.chart.data.datasets[context.datasetIndex].data.length
|
|
29
|
+
|
|
30
|
+
const drawsIn = (context) => context.type === "data" && context.mode === "default"
|
|
31
|
+
|
|
32
|
+
const drawIn = {
|
|
33
|
+
x: {
|
|
34
|
+
type: "number",
|
|
35
|
+
easing: "linear",
|
|
36
|
+
from: NaN,
|
|
37
|
+
duration: (context) => (drawsIn(context) ? pointStep(context) : 0),
|
|
38
|
+
delay: (context) => (drawsIn(context) ? context.index * pointStep(context) : 0)
|
|
39
|
+
},
|
|
40
|
+
y: { duration: 0 }
|
|
17
41
|
}
|
|
18
42
|
|
|
19
|
-
|
|
43
|
+
const axisText = (theme) => ({ color: theme.mutedText, font: { size: 11, family: theme.font } })
|
|
44
|
+
|
|
45
|
+
export const chartOptions = (dated, theme = {}, animated = false) => ({
|
|
20
46
|
responsive: true,
|
|
21
47
|
maintainAspectRatio: false,
|
|
22
48
|
interaction: { mode: "index", intersect: false },
|
|
23
|
-
...(
|
|
49
|
+
...(animated ? { animations: drawIn } : { animation: false }),
|
|
50
|
+
elements: { line: { borderWidth: 1.5 }, point: { radius: 0, hoverRadius: 4 } },
|
|
51
|
+
scales: {
|
|
52
|
+
x: { ...(dated ? DATED_AXIS : {}), grid: { display: false }, ticks: { ...(dated ? DATED_TICKS : {}), ...axisText(theme) } },
|
|
53
|
+
y: { border: { display: false }, grid: { color: theme.border }, ticks: axisText(theme) }
|
|
54
|
+
},
|
|
55
|
+
plugins: { tooltip: { ...(dated ? DATED_TOOLTIP : {}), ...tooltip(theme) } }
|
|
24
56
|
})
|
|
25
57
|
|
|
58
|
+
const LIGHT_THEMES = ["light", "custom"]
|
|
59
|
+
|
|
60
|
+
export const isDarkMode = (theme, prefersDark) => theme === "dark" || (prefersDark && !LIGHT_THEMES.includes(theme))
|
|
61
|
+
|
|
62
|
+
const CHART_COLOR_COUNT = 5
|
|
63
|
+
|
|
64
|
+
const INHERITED_FONTS = ["", "inherit"]
|
|
65
|
+
|
|
66
|
+
export const themeColors = (read, dark, inheritedFont) => {
|
|
67
|
+
const variable = (name) => read(dark ? `${name}-dark` : name)
|
|
68
|
+
const bodyFont = read("--ks-font-body")
|
|
69
|
+
return {
|
|
70
|
+
series: Array.from({ length: CHART_COLOR_COUNT }, (_, index) => variable(`--ks-color-chart-${index + 1}`)),
|
|
71
|
+
border: variable("--ks-color-border"),
|
|
72
|
+
surface: variable("--ks-color-surface"),
|
|
73
|
+
text: variable("--ks-color-text"),
|
|
74
|
+
mutedText: variable("--ks-color-text-muted"),
|
|
75
|
+
font: INHERITED_FONTS.includes(bodyFont) ? inheritedFont : bodyFont
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const styleDatasets = (datasets, theme, resolve) => {
|
|
80
|
+
let uncolored = 0
|
|
81
|
+
return datasets.map((dataset) => {
|
|
82
|
+
const color = dataset.borderColor ? resolve(dataset.borderColor) : theme.series[uncolored++ % theme.series.length]
|
|
83
|
+
return { ...dataset, borderColor: color, pointHoverBackgroundColor: color }
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const watchTheme = ({ document, matchMedia, MutationObserver }, onChange) => {
|
|
88
|
+
const observer = new MutationObserver(onChange)
|
|
89
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme", "data-look"] })
|
|
90
|
+
observer.observe(document.head, { childList: true, subtree: true, characterData: true })
|
|
91
|
+
const darkPreference = matchMedia("(prefers-color-scheme: dark)")
|
|
92
|
+
darkPreference.addEventListener("change", onChange)
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
observer.disconnect()
|
|
96
|
+
darkPreference.removeEventListener("change", onChange)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
26
100
|
export default class extends Controller {
|
|
27
101
|
static targets = ["canvas"]
|
|
28
102
|
static values = { data: Object, dated: Boolean }
|
|
@@ -31,27 +105,38 @@ export default class extends Controller {
|
|
|
31
105
|
const Chart = await loadChart()
|
|
32
106
|
if (!this.element.isConnected) return
|
|
33
107
|
|
|
108
|
+
const { theme, resolve } = this.readTheme()
|
|
109
|
+
this.animated = !matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
34
110
|
this.chart = new Chart(this.canvasTarget, {
|
|
35
111
|
type: "line",
|
|
36
|
-
data: this.
|
|
37
|
-
options: chartOptions(this.datedValue)
|
|
112
|
+
data: { ...this.dataValue, datasets: styleDatasets(this.dataValue.datasets, theme, resolve) },
|
|
113
|
+
options: chartOptions(this.datedValue, theme, this.animated)
|
|
38
114
|
})
|
|
115
|
+
this.stopWatchingTheme = watchTheme({ document, matchMedia: (query) => matchMedia(query), MutationObserver }, () => this.redraw())
|
|
39
116
|
}
|
|
40
117
|
|
|
41
118
|
disconnect() {
|
|
119
|
+
this.stopWatchingTheme?.()
|
|
42
120
|
this.chart?.destroy()
|
|
43
121
|
}
|
|
44
122
|
|
|
45
|
-
|
|
123
|
+
redraw() {
|
|
124
|
+
const { theme, resolve } = this.readTheme()
|
|
125
|
+
const styled = styleDatasets(this.dataValue.datasets, theme, resolve)
|
|
126
|
+
this.chart.data.datasets.forEach((dataset, index) => Object.assign(dataset, styled[index]))
|
|
127
|
+
this.chart.options = chartOptions(this.datedValue, theme, this.animated)
|
|
128
|
+
this.chart.update("none")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
readTheme() {
|
|
46
132
|
const styles = getComputedStyle(this.element)
|
|
133
|
+
const read = (name) => styles.getPropertyValue(name).trim()
|
|
47
134
|
const resolve = (color) => {
|
|
48
135
|
const match = typeof color === "string" && color.match(/^var\((--[^)]+)\)$/)
|
|
49
|
-
return match ?
|
|
136
|
+
return match ? read(match[1]) || color : color
|
|
50
137
|
}
|
|
51
138
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
return data
|
|
139
|
+
const dark = isDarkMode(document.documentElement.dataset.theme, matchMedia("(prefers-color-scheme: dark)").matches)
|
|
140
|
+
return { theme: themeColors(read, dark, styles.fontFamily), resolve }
|
|
56
141
|
}
|
|
57
142
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<p class="ks-caption"><%= text %></p>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Keystone
|
|
4
|
+
module Ui
|
|
5
|
+
class HeadingComponent < ViewComponent::Base
|
|
6
|
+
SIZE_CLASSES = { sm: "ks-heading-sm", md: "ks-heading-md", lg: "ks-heading-lg" }.freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :text, :note
|
|
9
|
+
|
|
10
|
+
def initialize(text:, size: :md, note: nil)
|
|
11
|
+
@text = text
|
|
12
|
+
@note = note
|
|
13
|
+
@size = size
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def classes
|
|
17
|
+
[ "ks-heading", SIZE_CLASSES.fetch(@size) ].join(" ")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
<div class="<%= SIDEBAR_LOGO_CLASSES %>"><%= logo %></div>
|
|
8
8
|
<% end %>
|
|
9
9
|
<% groups.each do |group| %>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
<% if group.standalone %>
|
|
11
|
+
<% tab = group.tabs.first %>
|
|
12
|
+
<a class="<%= sidebar_tab_classes(tab) %>" href="<%= tab.href %>"><%= tab.label %></a>
|
|
13
|
+
<% else %>
|
|
14
|
+
<details class="<%= SIDEBAR_GROUP_CLASSES %>"<%= " open" if group.active %>>
|
|
15
|
+
<summary class="<%= sidebar_group_label_classes(group) %>"><%= group.label %></summary>
|
|
16
|
+
<div class="<%= SIDEBAR_GROUP_TABS_CLASSES %>">
|
|
17
|
+
<% group.tabs.each do |tab| %>
|
|
18
|
+
<a class="<%= sidebar_tab_classes(tab) %>" href="<%= tab.href %>"><%= tab.label %></a>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
</details>
|
|
22
|
+
<% end %>
|
|
16
23
|
<% end %>
|
|
17
24
|
<% if menus? %>
|
|
18
25
|
<div class="<%= SIDEBAR_MENUS_CLASSES %>"><%= menus %></div>
|
|
@@ -33,9 +40,14 @@
|
|
|
33
40
|
<% end %>
|
|
34
41
|
<% navbar.with_desktop_links do %>
|
|
35
42
|
<% groups.each do |group| %>
|
|
36
|
-
|
|
37
|
-
<% group.tabs.
|
|
38
|
-
|
|
43
|
+
<% if group.standalone %>
|
|
44
|
+
<% tab = group.tabs.first %>
|
|
45
|
+
<%= render Keystone::Ui::NavItemComponent.new(label: tab.label, href: tab.href, active: tab.active) %>
|
|
46
|
+
<% else %>
|
|
47
|
+
<%= render Keystone::Ui::NavDropdownComponent.new(title: group.label, area: group.label, active: group.active) do %>
|
|
48
|
+
<% group.tabs.each do |tab| %>
|
|
49
|
+
<%= render Keystone::Ui::NavItemComponent.new(label: tab.label, href: tab.href, active: tab.active) %>
|
|
50
|
+
<% end %>
|
|
39
51
|
<% end %>
|
|
40
52
|
<% end %>
|
|
41
53
|
<% end %>
|
|
@@ -11,6 +11,7 @@ module Keystone
|
|
|
11
11
|
SIDEBAR_LOGO_CLASSES = "ks-sidebar-logo"
|
|
12
12
|
SIDEBAR_GROUP_CLASSES = "flex flex-col"
|
|
13
13
|
SIDEBAR_GROUP_LABEL_CLASSES = "ks-sidebar-group-label"
|
|
14
|
+
SIDEBAR_GROUP_TABS_CLASSES = "ks-sidebar-group-tabs"
|
|
14
15
|
SIDEBAR_TAB_CLASSES = "ks-sidebar-tab"
|
|
15
16
|
ACTIVE_CLASS = "active"
|
|
16
17
|
SIDEBAR_MENUS_CLASSES = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<p class="ks-paragraph"><%= text %></p>
|
|
@@ -237,6 +237,18 @@ module KeystoneUiHelper
|
|
|
237
237
|
render Keystone::Ui::FigureComponent.new(**args)
|
|
238
238
|
end
|
|
239
239
|
|
|
240
|
+
def ui_heading(**args)
|
|
241
|
+
render Keystone::Ui::HeadingComponent.new(**args)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def ui_caption(**args)
|
|
245
|
+
render Keystone::Ui::CaptionComponent.new(**args)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def ui_paragraph(**args)
|
|
249
|
+
render Keystone::Ui::ParagraphComponent.new(**args)
|
|
250
|
+
end
|
|
251
|
+
|
|
240
252
|
def ui_info(**args, &block)
|
|
241
253
|
render(Keystone::Ui::InfoComponent.new(**args), &block)
|
|
242
254
|
end
|
|
@@ -31,6 +31,12 @@ module KeystoneUi
|
|
|
31
31
|
@navigation_groups << group
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
def navigation_tab(key, label:, href:, permitted:)
|
|
35
|
+
group = NavigationGroup.new(label, standalone: true)
|
|
36
|
+
group.tab(key, label: label, href: href, permitted: permitted)
|
|
37
|
+
@navigation_groups << group
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
def supplied_look(view)
|
|
35
41
|
look_supplier&.call(view)&.to_s
|
|
36
42
|
end
|
|
@@ -10,11 +10,16 @@ module KeystoneUi
|
|
|
10
10
|
|
|
11
11
|
attr_reader :label, :tabs
|
|
12
12
|
|
|
13
|
-
def initialize(label)
|
|
13
|
+
def initialize(label, standalone: false)
|
|
14
14
|
@label = label
|
|
15
|
+
@standalone = standalone
|
|
15
16
|
@tabs = []
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
def standalone?
|
|
20
|
+
@standalone
|
|
21
|
+
end
|
|
22
|
+
|
|
18
23
|
def tabs_permitted_for(view)
|
|
19
24
|
tabs.select { |tab| tab.permitted.call(view) }
|
|
20
25
|
end
|
data/lib/keystone_ui/safelist.rb
CHANGED
|
@@ -14,7 +14,9 @@ module Keystone
|
|
|
14
14
|
Keystone::Ui::LineChartComponent,
|
|
15
15
|
Keystone::Ui::CtaBannerComponent,
|
|
16
16
|
Keystone::Ui::FeatureGridComponent,
|
|
17
|
+
Keystone::Ui::CaptionComponent,
|
|
17
18
|
Keystone::Ui::FigureComponent,
|
|
19
|
+
Keystone::Ui::HeadingComponent,
|
|
18
20
|
Keystone::Ui::HeroComponent,
|
|
19
21
|
Keystone::Ui::ModalComponent,
|
|
20
22
|
Keystone::Ui::SelectComponent,
|
|
@@ -32,6 +34,7 @@ module Keystone
|
|
|
32
34
|
Keystone::Ui::TextareaComponent,
|
|
33
35
|
Keystone::Ui::FormFieldComponent,
|
|
34
36
|
Keystone::Ui::PageHeaderComponent,
|
|
37
|
+
Keystone::Ui::ParagraphComponent,
|
|
35
38
|
Keystone::Ui::AlertComponent,
|
|
36
39
|
Keystone::Ui::NavDropdownComponent,
|
|
37
40
|
Keystone::Ui::NavItemComponent,
|
data/lib/keystone_ui/version.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "keystone_ui/navigation_order"
|
|
|
4
4
|
|
|
5
5
|
module KeystoneUi
|
|
6
6
|
class VisibleNavigation
|
|
7
|
-
Group = Struct.new(:label, :tabs, :active, keyword_init: true)
|
|
7
|
+
Group = Struct.new(:label, :tabs, :active, :standalone, keyword_init: true)
|
|
8
8
|
Tab = Struct.new(:label, :href, :active, keyword_init: true)
|
|
9
9
|
|
|
10
10
|
def initialize(declared_groups, view:, saved_order:, current_tab:)
|
|
@@ -28,7 +28,7 @@ module KeystoneUi
|
|
|
28
28
|
|
|
29
29
|
def visible_group(group, tabs)
|
|
30
30
|
visible_tabs = tabs.map { |tab| visible_tab(tab) }
|
|
31
|
-
Group.new(label: group.label, tabs: visible_tabs, active: visible_tabs.any?(&:active))
|
|
31
|
+
Group.new(label: group.label, tabs: visible_tabs, active: visible_tabs.any?(&:active), standalone: group.standalone?)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def visible_tab(tab)
|
|
@@ -63,6 +63,9 @@ not available in the app yet, that is `keystone_ui-install`'s job.
|
|
|
63
63
|
- `ui_button` — a link or a button styled as a button.
|
|
64
64
|
- `ui_badge` — a coloured pill holding a short label.
|
|
65
65
|
- `ui_figure` — one amount as inline text, coloured as a gain or a loss.
|
|
66
|
+
- `ui_heading` — a heading at one of three sizes, with an optional short note on its right.
|
|
67
|
+
- `ui_caption` — small uppercase muted text placed above a title or a figure.
|
|
68
|
+
- `ui_paragraph` — a paragraph of running text.
|
|
66
69
|
- `ui_alert` — a banner message, optionally dismissible.
|
|
67
70
|
- `ui_progress` — a labelled progress bar.
|
|
68
71
|
- `ui_stat_card` — a single metric tile with optional change, link and info button.
|
|
@@ -197,7 +200,10 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
|
|
|
197
200
|
it goes around `yield`. It draws the tabs the app declares with
|
|
198
201
|
`config.navigation_group` as a top bar of menus from `lg:` up, then the
|
|
199
202
|
content, and below `lg:` it draws only the content. It leaves out tabs
|
|
200
|
-
whose permission check fails and groups with no tab left.
|
|
203
|
+
whose permission check fails and groups with no tab left. A tab declared
|
|
204
|
+
outside any group with `config.navigation_tab` is drawn as a plain link
|
|
205
|
+
in the top bar and as a plain tab with no group label in the sidebar,
|
|
206
|
+
which does not open or close.
|
|
201
207
|
- Inside its block, `navigation.with_logo do ... end` puts the app's logo at
|
|
202
208
|
the left of the top bar, and `navigation.with_menus do ... end` puts menus
|
|
203
209
|
such as the account menu at its right, so the layout draws no second bar.
|
|
@@ -394,6 +400,10 @@ two exceptions: `ui_page`'s `padding:` treats any value other than `:none` as
|
|
|
394
400
|
- `ui_disclosure(open: false)` takes a block yielding the component. Fill
|
|
395
401
|
its `summary` slot with the header, and the rest of the block is the body.
|
|
396
402
|
- `ui_accordion(items: [])` — `items:` is `[{ question:, answer: }, ...]`.
|
|
403
|
+
- `ui_heading(text:, size: :md, note: nil)` — `size:` is `:sm` `:md` `:lg`,
|
|
404
|
+
and `note:` puts a short muted note at the right end of the heading's row.
|
|
405
|
+
- `ui_caption(text:)` shows small uppercase muted text above a title or a figure.
|
|
406
|
+
- `ui_paragraph(text:)` shows running text.
|
|
397
407
|
|
|
398
408
|
10. Explain figures.
|
|
399
409
|
|
|
@@ -31,6 +31,8 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
31
31
|
saves to, and the navigation's placement under the key `:navigation`),
|
|
32
32
|
`navigation_group` (a labelled group of desktop tabs that the navigation
|
|
33
33
|
helper draws, each tab declared with `tab key, label:, href:, permitted:`),
|
|
34
|
+
`navigation_tab` (a single desktop tab outside any group, declared with
|
|
35
|
+
`navigation_tab key, label:, href:, permitted:`),
|
|
34
36
|
`current_tab_supplier` (a callable that supplies the key of the declared tab
|
|
35
37
|
the current page belongs to, so the navigation marks it and its group
|
|
36
38
|
active),
|
|
@@ -324,6 +326,11 @@ built on ViewComponent; hook it in before building any screen with those helpers
|
|
|
324
326
|
`href:`, when two tabs share a key, or when a group has no tabs. Drawing
|
|
325
327
|
the declared groups in the layout is done with the navigation helper,
|
|
326
328
|
which belongs to `keystone_ui-develop`.
|
|
329
|
+
- `navigation_tab key, label:, href:, permitted:` — declares one tab
|
|
330
|
+
outside any group, such as a Console or Clients tab, in its place among
|
|
331
|
+
the groups. The navigation helper draws it as a plain link in the top bar
|
|
332
|
+
and as a plain tab with no group label in the sidebar. A saved `"order"`
|
|
333
|
+
names it under `"group"` by its label.
|
|
327
334
|
- `current_tab_supplier` — a callable that receives the view and returns the
|
|
328
335
|
key of the declared tab the current page belongs to, or `nil`.
|
|
329
336
|
The navigation shows that tab and its group as active in the top bar and
|
data/the_local/interface.yml
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: keystone_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.42.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tyler Schneider
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.19.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.19.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: view_component
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -98,6 +98,8 @@ files:
|
|
|
98
98
|
- app/components/keystone/ui/button_component.rb
|
|
99
99
|
- app/components/keystone/ui/calculation_component.html.erb
|
|
100
100
|
- app/components/keystone/ui/calculation_component.rb
|
|
101
|
+
- app/components/keystone/ui/caption_component.html.erb
|
|
102
|
+
- app/components/keystone/ui/caption_component.rb
|
|
101
103
|
- app/components/keystone/ui/card_component.html.erb
|
|
102
104
|
- app/components/keystone/ui/card_component.rb
|
|
103
105
|
- app/components/keystone/ui/card_link_component.html.erb
|
|
@@ -139,6 +141,8 @@ files:
|
|
|
139
141
|
- app/components/keystone/ui/funnel_component.rb
|
|
140
142
|
- app/components/keystone/ui/grid_component.html.erb
|
|
141
143
|
- app/components/keystone/ui/grid_component.rb
|
|
144
|
+
- app/components/keystone/ui/heading_component.html.erb
|
|
145
|
+
- app/components/keystone/ui/heading_component.rb
|
|
142
146
|
- app/components/keystone/ui/hero_component.html.erb
|
|
143
147
|
- app/components/keystone/ui/hero_component.rb
|
|
144
148
|
- app/components/keystone/ui/info_component.html.erb
|
|
@@ -171,6 +175,8 @@ files:
|
|
|
171
175
|
- app/components/keystone/ui/page_header_component.rb
|
|
172
176
|
- app/components/keystone/ui/panel_component.html.erb
|
|
173
177
|
- app/components/keystone/ui/panel_component.rb
|
|
178
|
+
- app/components/keystone/ui/paragraph_component.html.erb
|
|
179
|
+
- app/components/keystone/ui/paragraph_component.rb
|
|
174
180
|
- app/components/keystone/ui/pipeline_component.html.erb
|
|
175
181
|
- app/components/keystone/ui/pipeline_component.rb
|
|
176
182
|
- app/components/keystone/ui/progress_component.html.erb
|