okonomi_ui_kit 0.1.6 → 0.1.8
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 +163 -7
- data/app/assets/builds/okonomi_ui_kit/application.tailwind.css +292 -4
- data/app/helpers/okonomi_ui_kit/component.rb +81 -0
- data/app/helpers/okonomi_ui_kit/components/badge.rb +21 -16
- data/app/helpers/okonomi_ui_kit/components/breadcrumbs.rb +69 -0
- data/app/helpers/okonomi_ui_kit/components/button_base.rb +56 -0
- data/app/helpers/okonomi_ui_kit/components/button_tag.rb +23 -0
- data/app/helpers/okonomi_ui_kit/components/button_to.rb +23 -0
- data/app/helpers/okonomi_ui_kit/components/code.rb +73 -0
- data/app/helpers/okonomi_ui_kit/components/icon.rb +36 -0
- data/app/helpers/okonomi_ui_kit/components/link_to.rb +23 -0
- data/app/helpers/okonomi_ui_kit/components/page.rb +247 -0
- data/app/helpers/okonomi_ui_kit/components/table.rb +207 -0
- data/app/helpers/okonomi_ui_kit/components/typography.rb +29 -3
- data/app/helpers/okonomi_ui_kit/config.rb +20 -0
- data/app/helpers/okonomi_ui_kit/form_builder.rb +2 -2
- data/app/helpers/okonomi_ui_kit/t_w_merge.rb +108 -0
- data/app/helpers/okonomi_ui_kit/theme.rb +3 -26
- data/app/helpers/okonomi_ui_kit/ui_helper.rb +0 -40
- data/app/views/okonomi/components/breadcrumbs/_breadcrumbs.html.erb +46 -0
- data/app/views/okonomi/components/code/_code.html.erb +1 -0
- data/app/views/okonomi/components/icon/_icon.html.erb +38 -0
- data/app/views/okonomi/components/page/_page.html.erb +5 -0
- data/app/views/okonomi/components/table/_table.html.erb +3 -0
- data/app/views/okonomi/forms/tailwind/_upload_field.html.erb +2 -2
- data/app/views/okonomi/modals/_confirmation_modal.html.erb +2 -2
- data/app/views/okonomi/navigation/_link.html.erb +1 -1
- data/lib/okonomi_ui_kit/engine.rb +0 -5
- data/lib/okonomi_ui_kit/version.rb +1 -1
- metadata +18 -7
- data/app/helpers/okonomi_ui_kit/badge_helper.rb +0 -23
- data/app/helpers/okonomi_ui_kit/breadcrumbs_helper.rb +0 -60
- data/app/helpers/okonomi_ui_kit/icon_helper.rb +0 -39
- data/app/helpers/okonomi_ui_kit/page_builder_helper.rb +0 -217
- data/app/helpers/okonomi_ui_kit/table_helper.rb +0 -158
@@ -0,0 +1,207 @@
|
|
1
|
+
module OkonomiUiKit
|
2
|
+
module Components
|
3
|
+
class Table < OkonomiUiKit::Component
|
4
|
+
def render(options = {}, &block)
|
5
|
+
options = options.with_indifferent_access
|
6
|
+
variant = (options.delete(:variant) || :default).to_sym
|
7
|
+
|
8
|
+
builder = TableBuilder.new(view, theme, self, variant)
|
9
|
+
view.render(template_path, builder: builder, options: options, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
register_styles :default do
|
13
|
+
{
|
14
|
+
default: {
|
15
|
+
body: {
|
16
|
+
base: "divide-y divide-gray-200 bg-white"
|
17
|
+
},
|
18
|
+
th: {
|
19
|
+
base: "text-sm font-semibold text-gray-900",
|
20
|
+
first: "py-3.5 pr-3",
|
21
|
+
last: "relative py-3.5",
|
22
|
+
middle: "pl-3 pr-3 py-3.5"
|
23
|
+
},
|
24
|
+
td: {
|
25
|
+
base: "text-sm whitespace-nowrap",
|
26
|
+
first: "py-4 pr-3 font-medium text-gray-900",
|
27
|
+
last: "relative py-4 font-medium",
|
28
|
+
middle: "pl-3 pr-3 py-4 text-gray-500"
|
29
|
+
},
|
30
|
+
alignment: {
|
31
|
+
left: "text-left",
|
32
|
+
center: "text-center",
|
33
|
+
right: "text-right"
|
34
|
+
},
|
35
|
+
empty_state: {
|
36
|
+
wrapper: "text-center py-8",
|
37
|
+
icon: "mx-auto h-12 w-12 text-gray-400",
|
38
|
+
title: "mt-2 text-sm font-medium text-gray-900",
|
39
|
+
subtitle: "mt-1 text-sm text-gray-500",
|
40
|
+
cell: "text-center py-8 text-gray-500"
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class TableBuilder
|
48
|
+
include ActionView::Helpers::TagHelper
|
49
|
+
include ActionView::Helpers::CaptureHelper
|
50
|
+
|
51
|
+
def initialize(template, theme, style_provider, variant = :default)
|
52
|
+
@template = template
|
53
|
+
@theme = theme
|
54
|
+
@style_provider = style_provider
|
55
|
+
@variant = variant
|
56
|
+
@current_row_cells = []
|
57
|
+
@in_header = false
|
58
|
+
@in_body = false
|
59
|
+
end
|
60
|
+
|
61
|
+
def head(&block)
|
62
|
+
@in_header = true
|
63
|
+
@in_body = false
|
64
|
+
result = tag.thead(&block)
|
65
|
+
@in_header = false
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def body(&block)
|
70
|
+
@in_header = false
|
71
|
+
@in_body = true
|
72
|
+
result = tag.tbody(class: style(:body, :base), &block)
|
73
|
+
@in_body = false
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
def tr(&block)
|
78
|
+
@current_row_cells = []
|
79
|
+
|
80
|
+
# Collect all cells first
|
81
|
+
yield if block_given?
|
82
|
+
|
83
|
+
# Now render each cell with proper first/last detection
|
84
|
+
rendered_cells = @current_row_cells.map.with_index do |cell, index|
|
85
|
+
is_first = index == 0
|
86
|
+
is_last = index == @current_row_cells.length - 1
|
87
|
+
|
88
|
+
if cell[:type] == :th
|
89
|
+
render_th(cell, is_first, is_last)
|
90
|
+
else
|
91
|
+
render_td(cell, is_first, is_last)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
result = tag.tr do
|
96
|
+
@template.safe_join(rendered_cells)
|
97
|
+
end
|
98
|
+
|
99
|
+
@current_row_cells = []
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
def th(scope: "col", align: :left, **options, &block)
|
104
|
+
content = capture(&block) if block_given?
|
105
|
+
|
106
|
+
# Store cell data for later processing in tr
|
107
|
+
cell = { type: :th, scope: scope, align: align, options: options, content: content }
|
108
|
+
@current_row_cells << cell
|
109
|
+
|
110
|
+
# Return empty string for now, actual rendering happens in tr
|
111
|
+
""
|
112
|
+
end
|
113
|
+
|
114
|
+
def td(align: :left, **options, &block)
|
115
|
+
content = capture(&block) if block_given?
|
116
|
+
|
117
|
+
# Store cell data for later processing in tr
|
118
|
+
cell = { type: :td, align: align, options: options, content: content }
|
119
|
+
@current_row_cells << cell
|
120
|
+
|
121
|
+
# Return empty string for now, actual rendering happens in tr
|
122
|
+
""
|
123
|
+
end
|
124
|
+
|
125
|
+
def empty_state(title: "No records found", icon: "heroicons/outline/document", colspan: nil, &block)
|
126
|
+
content = if block_given?
|
127
|
+
capture(&block)
|
128
|
+
else
|
129
|
+
tag.div(class: style(:empty_state, :wrapper)) do
|
130
|
+
icon_content = if @template.respond_to?(:ui)
|
131
|
+
@template.ui.icon(icon, class: style(:empty_state, :icon))
|
132
|
+
else
|
133
|
+
tag.div(class: style(:empty_state, :icon))
|
134
|
+
end
|
135
|
+
|
136
|
+
icon_content + tag.p(title, class: style(:empty_state, :title)) +
|
137
|
+
tag.p("Get started by creating a new record.", class: style(:empty_state, :subtitle))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
tr do
|
142
|
+
td(colspan: colspan, class: style(:empty_state, :cell)) do
|
143
|
+
content
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def tag
|
151
|
+
@template.tag
|
152
|
+
end
|
153
|
+
|
154
|
+
def capture(*args, &block)
|
155
|
+
@template.capture(*args, &block)
|
156
|
+
end
|
157
|
+
|
158
|
+
def render_th(cell, is_first, is_last)
|
159
|
+
align_class = style(:alignment, cell[:align]) || style(:alignment, :left)
|
160
|
+
|
161
|
+
position_class = if is_first
|
162
|
+
style(:th, :first)
|
163
|
+
elsif is_last
|
164
|
+
style(:th, :last)
|
165
|
+
else
|
166
|
+
style(:th, :middle)
|
167
|
+
end
|
168
|
+
|
169
|
+
classes = [
|
170
|
+
style(:th, :base),
|
171
|
+
position_class,
|
172
|
+
align_class,
|
173
|
+
cell[:options][:class]
|
174
|
+
].compact.join(' ')
|
175
|
+
|
176
|
+
options = cell[:options].except(:class)
|
177
|
+
tag.th(cell[:content], scope: cell[:scope], class: classes, **options)
|
178
|
+
end
|
179
|
+
|
180
|
+
def render_td(cell, is_first, is_last)
|
181
|
+
align_class = style(:alignment, cell[:align]) || style(:alignment, :left)
|
182
|
+
|
183
|
+
position_class = if is_first
|
184
|
+
style(:td, :first)
|
185
|
+
elsif is_last
|
186
|
+
style(:td, :last)
|
187
|
+
else
|
188
|
+
style(:td, :middle)
|
189
|
+
end
|
190
|
+
|
191
|
+
classes = [
|
192
|
+
style(:td, :base),
|
193
|
+
position_class,
|
194
|
+
align_class,
|
195
|
+
cell[:options][:class]
|
196
|
+
].compact.join(' ')
|
197
|
+
|
198
|
+
options = cell[:options].except(:class)
|
199
|
+
tag.td(cell[:content], class: classes, **options)
|
200
|
+
end
|
201
|
+
|
202
|
+
def style(*keys)
|
203
|
+
@style_provider.style(@variant, *keys)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -20,10 +20,10 @@ module OkonomiUiKit
|
|
20
20
|
variant = (options.delete(:variant) || 'body1').to_sym
|
21
21
|
component = (TYPOGRAPHY_COMPONENTS[variant] || 'span').to_s
|
22
22
|
color = (options.delete(:color) || 'default').to_sym
|
23
|
-
|
23
|
+
|
24
24
|
classes = [
|
25
|
-
|
26
|
-
|
25
|
+
style(:variants, variant) || '',
|
26
|
+
style(:colors, color) || '',
|
27
27
|
options.delete(:class) || ''
|
28
28
|
].reject(&:blank?).join(' ')
|
29
29
|
|
@@ -37,6 +37,32 @@ module OkonomiUiKit
|
|
37
37
|
&block
|
38
38
|
)
|
39
39
|
end
|
40
|
+
|
41
|
+
register_styles :default do
|
42
|
+
{
|
43
|
+
variants: {
|
44
|
+
body1: "text-base font-normal",
|
45
|
+
body2: "text-sm font-normal",
|
46
|
+
h1: "text-3xl font-bold",
|
47
|
+
h2: "text-2xl font-bold",
|
48
|
+
h3: "text-xl font-semibold",
|
49
|
+
h4: "text-lg font-semibold",
|
50
|
+
h5: "text-base font-semibold",
|
51
|
+
h6: "text-sm font-semibold"
|
52
|
+
},
|
53
|
+
colors: {
|
54
|
+
default: "text-default-700",
|
55
|
+
dark: "text-default-900",
|
56
|
+
muted: "text-default-500",
|
57
|
+
primary: "text-primary-600",
|
58
|
+
secondary: "text-secondary-600",
|
59
|
+
success: "text-success-600",
|
60
|
+
danger: "text-danger-600",
|
61
|
+
warning: "text-warning-600",
|
62
|
+
info: "text-info-600"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
40
66
|
end
|
41
67
|
end
|
42
68
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module OkonomiUiKit
|
2
|
+
class Config
|
3
|
+
def self.register_styles(theme = :default, &block)
|
4
|
+
styles = block.call if block_given?
|
5
|
+
|
6
|
+
raise ArgumentError, "Styles must be a Hash" unless styles.is_a?(Hash)
|
7
|
+
|
8
|
+
styles_registry[theme] ||= {}
|
9
|
+
styles_registry[theme] = deep_merge({}, styles_registry[theme], styles)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.styles_registry
|
13
|
+
@styles_registry ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.deep_merge(*hashes)
|
17
|
+
OkonomiUiKit::TWMerge.deep_merge_all(*hashes)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -85,7 +85,7 @@ module OkonomiUiKit
|
|
85
85
|
].compact.join(' ').split(' ').uniq
|
86
86
|
|
87
87
|
select_html = super(method, choices, options, html_options.merge(class: css), &block)
|
88
|
-
icon_html = @template.
|
88
|
+
icon_html = @template.ui.icon(
|
89
89
|
ui.get_theme.dig(:components, :select, :icon, :file),
|
90
90
|
class: ui.get_theme.dig(:components, :select, :icon, :class)
|
91
91
|
)
|
@@ -108,7 +108,7 @@ module OkonomiUiKit
|
|
108
108
|
].compact.join(' ').split(' ').uniq
|
109
109
|
|
110
110
|
select_html = super(method, collection, value_method, text_method, options, html_options.merge(class: css))
|
111
|
-
icon_html = @template.
|
111
|
+
icon_html = @template.ui.icon(
|
112
112
|
ui.get_theme.dig(:components, :select, :icon, :file),
|
113
113
|
class: ui.get_theme.dig(:components, :select, :icon, :class)
|
114
114
|
)
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module OkonomiUiKit
|
2
|
+
class TWMerge
|
3
|
+
# ---- Public API -----------------------------------------------------------
|
4
|
+
|
5
|
+
# Merge two Tailwind class strings with conflict resolution.
|
6
|
+
def self.merge(a, b)
|
7
|
+
tokens = "#{a} #{b}".split(/\s+/).reject(&:empty?)
|
8
|
+
result = []
|
9
|
+
index_by_key = {}
|
10
|
+
|
11
|
+
tokens.each do |tok|
|
12
|
+
variants, base = split_variants(tok)
|
13
|
+
group = conflict_group_for(base)
|
14
|
+
key = [variants, group || "literal:#{base}"]
|
15
|
+
|
16
|
+
if index_by_key.key?(key)
|
17
|
+
pos = index_by_key[key]
|
18
|
+
result[pos] = tok
|
19
|
+
else
|
20
|
+
index_by_key[key] = result.length
|
21
|
+
result << tok
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
result.join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Deep-merge two hashes; when both values are strings, merge as Tailwind classes.
|
29
|
+
# For other types, the right-hand value wins unless it is nil.
|
30
|
+
def self.deep_merge(a, b)
|
31
|
+
if a.is_a?(Hash) && b.is_a?(Hash)
|
32
|
+
(a.keys | b.keys).each_with_object({}) do |k, h|
|
33
|
+
h[k] = deep_merge(a[k], b[k])
|
34
|
+
end
|
35
|
+
elsif a.is_a?(String) && b.is_a?(String)
|
36
|
+
merge(a, b)
|
37
|
+
else
|
38
|
+
b.nil? ? a : b
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.deep_merge_all(*hashes)
|
43
|
+
hashes.reduce({}) do |result, hash|
|
44
|
+
if hash.is_a?(Hash)
|
45
|
+
deep_merge(result, hash)
|
46
|
+
else
|
47
|
+
raise ArgumentError, "All arguments must be Hashes"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# ---- Implementation details ----------------------------------------------
|
53
|
+
|
54
|
+
# Conflict groups (minimal, extensible). More specific patterns first.
|
55
|
+
CONFLICT_RULES = [
|
56
|
+
# Typography
|
57
|
+
[/^text-(?:xs|sm|base|lg|xl|\d+xl|\[\S+\])$/, :text_size],
|
58
|
+
[/^text-(?:inherit|current|transparent|black|white|[a-z]+-\d{2,3}|[a-z]+-950|\[.+\])$/, :text_color],
|
59
|
+
[/^font-(?:thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/, :font_weight],
|
60
|
+
[/^leading-(?:none|tight|snug|normal|relaxed|loose|\d+|\[.+\])$/, :line_height],
|
61
|
+
[/^tracking-(?:tighter|tight|normal|wide|widest|\[.+\])$/, :letter_spacing],
|
62
|
+
|
63
|
+
# Display & position
|
64
|
+
[/^(?:hidden|block|inline|inline-block|flex|inline-flex|grid|inline-grid|table|inline-table|flow-root)$/, :display],
|
65
|
+
[/^(?:static|fixed|absolute|relative|sticky)$/, :position],
|
66
|
+
|
67
|
+
# Flexbox
|
68
|
+
[/^flex-(?:row|col|row-reverse|col-reverse)$/, :flex_direction],
|
69
|
+
[/^flex-(?:wrap|nowrap|wrap-reverse)$/, :flex_wrap],
|
70
|
+
[/^items-(?:start|end|center|baseline|stretch)$/, :align_items],
|
71
|
+
[/^justify-(?:start|end|center|between|around|evenly)$/, :justify_content],
|
72
|
+
|
73
|
+
# Borders
|
74
|
+
[/^(?:border|border-(?:\d+|\[\S+\]))$/, :border_width_overall],
|
75
|
+
[/^border-[trblxy](?:-\d+|\[\S+\])?$/, :border_width_side],
|
76
|
+
[/^border-(?:solid|dashed|dotted|double|none)$/, :border_style],
|
77
|
+
[/^border-(?:inherit|current|transparent|black|white|[a-z]+-\d{2,3}|[a-z]+-950|\[.+\])$/, :border_color],
|
78
|
+
|
79
|
+
# Radius
|
80
|
+
[/^rounded(?:-(?:none|sm|md|lg|xl|2xl|3xl|full|\[.+\]))?$/, :rounded_overall],
|
81
|
+
[/^rounded-(?:t|r|b|l|tl|tr|br|bl)-(?:none|sm|md|lg|xl|2xl|3xl|full|\[.+\])$/, :rounded_corner],
|
82
|
+
|
83
|
+
# Background
|
84
|
+
[/^bg-(?:inherit|current|transparent|black|white|[a-z]+-\d{2,3}|[a-z]+-950|\[.+\])$/, :bg_color],
|
85
|
+
|
86
|
+
# Overflow & opacity
|
87
|
+
[/^overflow-(?:auto|hidden|visible|scroll|clip)$/, :overflow],
|
88
|
+
[/^overflow-[xy]-(?:auto|hidden|visible|scroll|clip)$/, :overflow_axis],
|
89
|
+
[/^opacity-(?:\d{1,3}|\[.+\])$/, :opacity],
|
90
|
+
].freeze
|
91
|
+
|
92
|
+
class << self
|
93
|
+
private
|
94
|
+
|
95
|
+
# "sm:hover:text-lg" -> ["sm:hover", "text-lg"]
|
96
|
+
def split_variants(token)
|
97
|
+
parts = token.split(':')
|
98
|
+
return ["", token] if parts.size == 1
|
99
|
+
[parts[0..-2].join(':'), parts[-1]]
|
100
|
+
end
|
101
|
+
|
102
|
+
def conflict_group_for(base)
|
103
|
+
rule = CONFLICT_RULES.find { |(rx, _)| base.match?(rx) }
|
104
|
+
rule ? rule[1] : nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -2,33 +2,10 @@ module OkonomiUiKit
|
|
2
2
|
module Theme
|
3
3
|
LIGHT_THEME = {
|
4
4
|
components: {
|
5
|
-
typography: {
|
6
|
-
variants: {
|
7
|
-
body1: "text-base font-normal",
|
8
|
-
body2: "text-sm font-normal",
|
9
|
-
h1: "text-3xl font-bold",
|
10
|
-
h2: "text-2xl font-bold",
|
11
|
-
h3: "text-xl font-semibold",
|
12
|
-
h4: "text-lg font-semibold",
|
13
|
-
h5: "text-base font-semibold",
|
14
|
-
h6: "text-sm font-semibold"
|
15
|
-
},
|
16
|
-
colors: {
|
17
|
-
default: "text-default-700",
|
18
|
-
dark: "text-default-900",
|
19
|
-
muted: "text-default-500",
|
20
|
-
primary: "text-primary-600",
|
21
|
-
secondary: "text-secondary-600",
|
22
|
-
success: "text-success-600",
|
23
|
-
danger: "text-danger-600",
|
24
|
-
warning: "text-warning-600",
|
25
|
-
info: "text-info-600"
|
26
|
-
}
|
27
|
-
},
|
28
5
|
link: {
|
29
|
-
root: "hover:cursor-pointer",
|
6
|
+
root: "hover:cursor-pointer text-sm",
|
30
7
|
outlined: {
|
31
|
-
root: "inline-flex border items-center justify-center px-
|
8
|
+
root: "inline-flex border items-center justify-center px-2 py-1 rounded-md font-medium focus:outline-none focus:ring-2 focus:ring-offset-2",
|
32
9
|
colors: {
|
33
10
|
default: "bg-white text-default-700 border-default-700 hover:bg-default-50",
|
34
11
|
primary: "bg-white text-primary-600 border-primary-600 hover:bg-primary-50",
|
@@ -40,7 +17,7 @@ module OkonomiUiKit
|
|
40
17
|
}
|
41
18
|
},
|
42
19
|
contained: {
|
43
|
-
root: "inline-flex border items-center justify-center px-
|
20
|
+
root: "inline-flex border items-center justify-center px-2 py-1 rounded-md font-medium focus:outline-none focus:ring-2 focus:ring-offset-2",
|
44
21
|
colors: {
|
45
22
|
default: "border-default-700 bg-default-600 text-white hover:bg-default-700",
|
46
23
|
primary: "border-primary-700 bg-primary-600 text-white hover:bg-primary-700",
|
@@ -26,42 +26,6 @@ module OkonomiUiKit
|
|
26
26
|
@_okonomi_ui_kit_theme ||= OkonomiUiKit::Theme::DEFAULT_THEME
|
27
27
|
end
|
28
28
|
|
29
|
-
def link_to(name = nil, options = nil, html_options = nil, &block)
|
30
|
-
html_options, options, name = options, name, block if block_given?
|
31
|
-
|
32
|
-
html_options ||= {}
|
33
|
-
html_options[:class] ||= ''
|
34
|
-
|
35
|
-
variant = (html_options.delete(:variant) || 'text').to_sym
|
36
|
-
color = (html_options.delete(:color) || 'default').to_sym
|
37
|
-
|
38
|
-
html_options[:class] = button_class(variant:, color:, classes: html_options[:class])
|
39
|
-
|
40
|
-
if block_given?
|
41
|
-
@template.link_to(options, html_options, &block)
|
42
|
-
else
|
43
|
-
@template.link_to(name, options, html_options)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def button_to(name = nil, options = nil, html_options = nil, &block)
|
48
|
-
html_options, options, name = options, name, block if block_given?
|
49
|
-
|
50
|
-
html_options ||= {}
|
51
|
-
html_options[:class] ||= ''
|
52
|
-
|
53
|
-
variant = (html_options.delete(:variant) || 'contained').to_sym
|
54
|
-
color = (html_options.delete(:color) || 'default').to_sym
|
55
|
-
|
56
|
-
html_options[:class] = button_class(variant:, color:, classes: html_options[:class])
|
57
|
-
|
58
|
-
if block_given?
|
59
|
-
@template.button_to(options, html_options, &block)
|
60
|
-
else
|
61
|
-
@template.button_to(name, options, html_options)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
29
|
def button_class(variant: 'contained', color: 'default', classes: '')
|
66
30
|
[
|
67
31
|
get_theme.dig(:components, :link, :root) || '',
|
@@ -71,10 +35,6 @@ module OkonomiUiKit
|
|
71
35
|
].join(' ')
|
72
36
|
end
|
73
37
|
|
74
|
-
def page(&block)
|
75
|
-
@template.page(&block)
|
76
|
-
end
|
77
|
-
|
78
38
|
def confirmation_modal(title:, message:, confirm_text: "Confirm", cancel_text: "Cancel", variant: :warning, size: :md, **options, &block)
|
79
39
|
modal_options = {
|
80
40
|
title: title,
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<%= tag.nav(**options.merge(class: component.style(:nav), 'aria-label': 'Breadcrumb')) do %>
|
2
|
+
<ol role="list" class="<%= component.style(:list) %>">
|
3
|
+
<% items.each_with_index do |item, index| %>
|
4
|
+
<li>
|
5
|
+
<div class="flex items-center">
|
6
|
+
<% if index == 0 %>
|
7
|
+
<% if item[:icon] %>
|
8
|
+
<% if item[:path] %>
|
9
|
+
<%= link_to item[:path], class: "text-gray-400 hover:text-gray-500", 'aria-current': (item[:current] ? 'page' : nil) do %>
|
10
|
+
<%= item[:icon] %>
|
11
|
+
<span class="sr-only"><%= item[:text] %></span>
|
12
|
+
<% end %>
|
13
|
+
<% else %>
|
14
|
+
<span class="text-gray-400" aria-current="<%= item[:current] ? 'page' : nil %>">
|
15
|
+
<%= item[:icon] %>
|
16
|
+
<span class="sr-only"><%= item[:text] %></span>
|
17
|
+
</span>
|
18
|
+
<% end %>
|
19
|
+
<% else %>
|
20
|
+
<div class="flex items-center">
|
21
|
+
<% if item[:path] && !item[:current] %>
|
22
|
+
<%= link_to item[:text], item[:path], class: component.style(:link, :first), 'aria-current': nil %>
|
23
|
+
<% else %>
|
24
|
+
<span class="<%= component.style(:link, :first) %>" aria-current="<%= item[:current] ? 'page' : nil %>">
|
25
|
+
<%= item[:text] %>
|
26
|
+
</span>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
<% else %>
|
31
|
+
<svg class="<%= component.style(:separator, :base) %>" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
32
|
+
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
33
|
+
</svg>
|
34
|
+
<% if item[:path] && !item[:current] %>
|
35
|
+
<%= link_to item[:text], item[:path], class: component.style(:link, :base), 'aria-current': nil %>
|
36
|
+
<% else %>
|
37
|
+
<span class="<%= component.style(:link, :current) %>" aria-current="<%= item[:current] ? 'page' : nil %>">
|
38
|
+
<%= item[:text] %>
|
39
|
+
</span>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
42
|
+
</div>
|
43
|
+
</li>
|
44
|
+
<% end %>
|
45
|
+
</ol>
|
46
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<pre<%= language ? " data-language=\"#{language}\"".html_safe : "" %> class="<%= classes %>"<%= tag.attributes(options) %>><code><%= content %></code></pre>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<%
|
2
|
+
# Load the SVG content
|
3
|
+
svg_content = if OkonomiUiKit::SvgIcons.exist?(name)
|
4
|
+
OkonomiUiKit::SvgIcons.read(name)
|
5
|
+
else
|
6
|
+
"<!-- SVG #{name} not found -->"
|
7
|
+
end
|
8
|
+
|
9
|
+
# Parse and modify the SVG
|
10
|
+
if svg_content.start_with?("<!--")
|
11
|
+
%><%= raw svg_content %><%
|
12
|
+
else
|
13
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(svg_content)
|
14
|
+
svg = doc.at_css('svg')
|
15
|
+
|
16
|
+
if svg
|
17
|
+
# Apply classes
|
18
|
+
svg['class'] = classes if classes.present?
|
19
|
+
|
20
|
+
# Apply dimensions
|
21
|
+
svg['width'] = width if width.present?
|
22
|
+
svg['height'] = height if height.present?
|
23
|
+
|
24
|
+
# Apply any additional options as attributes
|
25
|
+
options.each do |key, value|
|
26
|
+
if key.to_s == 'data' && value.is_a?(Hash)
|
27
|
+
# Handle data attributes specially
|
28
|
+
value.each do |data_key, data_value|
|
29
|
+
svg["data-#{data_key.to_s.dasherize}"] = data_value
|
30
|
+
end
|
31
|
+
else
|
32
|
+
svg[key.to_s.dasherize] = value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
%><%= raw doc.to_html %><%
|
37
|
+
end
|
38
|
+
%>
|
@@ -11,10 +11,10 @@
|
|
11
11
|
<% if blob.image? %>
|
12
12
|
<%= image_tag url_for(blob), class: "max-h-32" %>
|
13
13
|
<% else %>
|
14
|
-
<%=
|
14
|
+
<%= ui.icon("heroicons/solid/document", class: "size-12 text-gray-400") %>
|
15
15
|
<% end %>
|
16
16
|
<% else %>
|
17
|
-
<%=
|
17
|
+
<%= ui.icon("heroicons/solid/plus", class: "size-12 w-12 text-gray-400") %>
|
18
18
|
<% end %>
|
19
19
|
</div>
|
20
20
|
|
@@ -28,7 +28,7 @@
|
|
28
28
|
class="<%= ui.get_theme.dig(:components, :modal, :close_button, :button) %>"
|
29
29
|
data-action="click->modal#close">
|
30
30
|
<span class="sr-only">Close</span>
|
31
|
-
<%=
|
31
|
+
<%= ui.icon(ui.get_theme.dig(:components, :modal, :close_button, :icon, :file),
|
32
32
|
class: ui.get_theme.dig(:components, :modal, :close_button, :icon, :class)) %>
|
33
33
|
</button>
|
34
34
|
</div>
|
@@ -37,7 +37,7 @@
|
|
37
37
|
<div class="<%= ui.get_theme.dig(:components, :modal, :content, :wrapper) %>">
|
38
38
|
<!-- Icon -->
|
39
39
|
<div class="<%= ui.modal_icon_wrapper_class(options[:variant]) %>">
|
40
|
-
<%=
|
40
|
+
<%= ui.icon(ui.get_theme.dig(:components, :modal, :icon, :variants, options[:variant], :file),
|
41
41
|
class: [ui.get_theme.dig(:components, :modal, :icon, :class),
|
42
42
|
ui.get_theme.dig(:components, :modal, :icon, :variants, options[:variant], :icon)].join(' ')) %>
|
43
43
|
</div>
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<% if defined?(initials) && initials.present? %>
|
10
10
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-white text-[0.625rem] font-medium text-gray-400 group-hover:border-primary-600 group-hover:text-primary-600"><%= initials %></span>
|
11
11
|
<% elsif defined?(icon) && icon.present? %>
|
12
|
-
<%=
|
12
|
+
<%= ui.icon icon, class: "size-6 text-gray-400 group-hover:text-primary-600" %>
|
13
13
|
<% end %>
|
14
14
|
<%= title %>
|
15
15
|
<% end %>
|
@@ -22,12 +22,7 @@ module OkonomiUiKit
|
|
22
22
|
ActiveSupport.on_load(:action_view) do
|
23
23
|
include OkonomiUiKit::ApplicationHelper
|
24
24
|
include OkonomiUiKit::AttributeSectionHelper
|
25
|
-
include OkonomiUiKit::BadgeHelper
|
26
|
-
include OkonomiUiKit::BreadcrumbsHelper
|
27
|
-
include OkonomiUiKit::IconHelper
|
28
25
|
include OkonomiUiKit::NavigationHelper
|
29
|
-
include OkonomiUiKit::PageBuilderHelper
|
30
|
-
include OkonomiUiKit::TableHelper
|
31
26
|
include OkonomiUiKit::UiHelper
|
32
27
|
|
33
28
|
ActionView::Base.field_error_proc = ->(html_tag, _instance) { html_tag.html_safe }
|