okonomi_ui_kit 0.1.6 → 0.1.7

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: 2c9f6e01883b276ac9f9698f3d62b837b2b015e43c58928c2dce2a8f7d989ec9
4
- data.tar.gz: 70f433e695cec861ff14035220709e8d56115b4747d18e4397b342d8f5693e0d
3
+ metadata.gz: c94c4c54d4385932abd25596610ea5e50f6d49477577a95731a0e0a1e22fca31
4
+ data.tar.gz: bb5f224ebe4b40a221cc382dea0256d75589557c553a3abf51f647cf3018044f
5
5
  SHA512:
6
- metadata.gz: b1477d7d86ad3def41842194405981bfcaed050bde961961f1e75f74ccc7057b9c0dbd4c06686f1c6639042e17bfef01fe5947a27fc895f516f257c878ac3f68
7
- data.tar.gz: 3ac01a8f7d26c04b62b3af528e32003f98b59f4caa06da35fd1f974e36017b7fe4c21589a32bf3ca50d14eecee641a719871ee7bb1b2ef3f3bef15652de4a503
6
+ metadata.gz: 1668ef825c3f06032802b9614baba74626f6e74cabd741880143627d1f6c2ee32d1d2b0a21676a550d2773c3abaa1d4ec3162da05b7e4bcb574d5e8429bab8fc
7
+ data.tar.gz: 1f457860e311e5efa5fa07d8199c0ef0758dac7122b9a07b3780c820f472546aee2083bbaf2b0374343fc41882e7830780e788070111c04404f9f2788b7b25d3
@@ -619,6 +619,9 @@
619
619
  .flex-shrink {
620
620
  flex-shrink: 1;
621
621
  }
622
+ .flex-shrink-0 {
623
+ flex-shrink: 0;
624
+ }
622
625
  .shrink-0 {
623
626
  flex-shrink: 0;
624
627
  }
@@ -663,9 +666,15 @@
663
666
  .transform {
664
667
  transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
665
668
  }
669
+ .cursor-not-allowed {
670
+ cursor: not-allowed;
671
+ }
666
672
  .cursor-pointer {
667
673
  cursor: pointer;
668
674
  }
675
+ .cursor-wait {
676
+ cursor: wait;
677
+ }
669
678
  .resize {
670
679
  resize: both;
671
680
  }
@@ -1087,6 +1096,9 @@
1087
1096
  .py-1 {
1088
1097
  padding-block: calc(var(--spacing) * 1);
1089
1098
  }
1099
+ .py-1\.5 {
1100
+ padding-block: calc(var(--spacing) * 1.5);
1101
+ }
1090
1102
  .py-2 {
1091
1103
  padding-block: calc(var(--spacing) * 2);
1092
1104
  }
@@ -1159,6 +1171,9 @@
1159
1171
  .align-middle {
1160
1172
  vertical-align: middle;
1161
1173
  }
1174
+ .font-mono {
1175
+ font-family: var(--font-mono);
1176
+ }
1162
1177
  .text-2xl {
1163
1178
  font-size: var(--text-2xl);
1164
1179
  line-height: var(--tw-leading, var(--text-2xl--line-height));
@@ -1237,6 +1252,9 @@
1237
1252
  .whitespace-nowrap {
1238
1253
  white-space: nowrap;
1239
1254
  }
1255
+ .text-amber-600 {
1256
+ color: var(--color-amber-600);
1257
+ }
1240
1258
  .text-blue-500 {
1241
1259
  color: var(--color-blue-500);
1242
1260
  }
@@ -1357,6 +1375,9 @@
1357
1375
  .opacity-0 {
1358
1376
  opacity: 0%;
1359
1377
  }
1378
+ .opacity-50 {
1379
+ opacity: 50%;
1380
+ }
1360
1381
  .opacity-100 {
1361
1382
  opacity: 100%;
1362
1383
  }
@@ -1368,6 +1389,10 @@
1368
1389
  --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1369
1390
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1370
1391
  }
1392
+ .shadow-none {
1393
+ --tw-shadow: 0 0 #0000;
1394
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1395
+ }
1371
1396
  .shadow-sm {
1372
1397
  --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1373
1398
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1380,6 +1405,10 @@
1380
1405
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1381
1406
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1382
1407
  }
1408
+ .ring-2 {
1409
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1410
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1411
+ }
1383
1412
  .ring-black {
1384
1413
  --tw-ring-color: var(--color-black);
1385
1414
  }
@@ -1395,6 +1424,10 @@
1395
1424
  .ring-gray-300 {
1396
1425
  --tw-ring-color: var(--color-gray-300);
1397
1426
  }
1427
+ .ring-offset-2 {
1428
+ --tw-ring-offset-width: 2px;
1429
+ --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1430
+ }
1398
1431
  .outline-hidden {
1399
1432
  --tw-outline-style: none;
1400
1433
  outline-style: none;
@@ -14,5 +14,67 @@ module OkonomiUiKit
14
14
  def name
15
15
  self.class.name.demodulize.underscore
16
16
  end
17
+
18
+ def style(*args)
19
+ styles.dig(*args)
20
+ end
21
+
22
+ def styles
23
+ @combined_styles ||= combined_styles
24
+ end
25
+
26
+ def combined_styles
27
+ internal_name = internal_styles_registry.has_key?(theme_name) ? theme_name : :default
28
+ config_name = config_styles_registry.has_key?(theme_name) ? theme_name : :default
29
+
30
+ internal_styles = internal_styles_registry[internal_name] || {}
31
+ config_styles = config_styles_registry[config_name] || {}
32
+
33
+ {}.deep_merge(internal_styles).deep_merge(config_styles)
34
+ end
35
+
36
+ def internal_styles_registry
37
+ self.class.internal_styles_registry
38
+ end
39
+
40
+ def config_styles_registry
41
+ self.class.config_styles_registry
42
+ end
43
+
44
+ def theme_name
45
+ :default
46
+ end
47
+
48
+ def self.config_styles_registry
49
+ return Hash.new({}) unless config_class?
50
+
51
+ config_class.styles_registry
52
+ end
53
+
54
+ def self.config_class
55
+ return nil unless config_class?
56
+
57
+ Object.const_get(config_class_name)
58
+ end
59
+
60
+ def self.config_class_name
61
+ "OkonomiUiKit::Configs::#{name.demodulize}"
62
+ end
63
+
64
+ def self.config_class?
65
+ Object.const_defined?(config_class_name)
66
+ end
67
+
68
+ def self.register_styles(theme = :default, &block)
69
+ styles = block.call if block_given?
70
+
71
+ raise ArgumentError, "Styles must be a Hash" unless styles.is_a?(Hash)
72
+
73
+ internal_styles_registry[theme] = styles if styles.is_a?(Hash)
74
+ end
75
+
76
+ def self.internal_styles_registry
77
+ @internal_styles_registry ||= {}
78
+ end
17
79
  end
18
80
  end
@@ -2,24 +2,29 @@ module OkonomiUiKit
2
2
  module Components
3
3
  class Badge < OkonomiUiKit::Component
4
4
  def render(text, options = {})
5
- severity = options.delete(:severity) || :default
6
- color_classes = case severity.to_sym
7
- when :success
8
- "bg-green-100 text-green-800"
9
- when :danger
10
- "bg-red-100 text-red-800"
11
- when :info
12
- "bg-blue-100 text-blue-800"
13
- when :warning
14
- "bg-yellow-100 text-yellow-800"
15
- else
16
- "bg-gray-100 text-gray-800"
17
- end
5
+ options = options.with_indifferent_access
6
+ severity = (options.delete(:severity) || :default).to_sym
7
+
8
+ classes = [
9
+ style(:base),
10
+ style(:severities, severity) || '',
11
+ options.delete(:class) || ''
12
+ ].reject(&:blank?).join(' ')
18
13
 
19
- base_classes = "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
20
- full_classes = "#{base_classes} #{color_classes} #{options[:class] || ''}".strip
14
+ view.tag.span(text, class: classes, **options)
15
+ end
21
16
 
22
- view.tag.span(text, class: full_classes, **options.except(:class))
17
+ register_styles :default do
18
+ {
19
+ base: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
20
+ severities: {
21
+ default: "bg-gray-100 text-gray-800",
22
+ success: "bg-green-100 text-green-800",
23
+ danger: "bg-red-100 text-red-800",
24
+ info: "bg-blue-100 text-blue-800",
25
+ warning: "bg-yellow-100 text-yellow-800"
26
+ }
27
+ }
23
28
  end
24
29
  end
25
30
  end
@@ -0,0 +1,34 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ class ButtonTo < OkonomiUiKit::Component
4
+ def render(name = nil, options = nil, html_options = nil, &block)
5
+ html_options, options, name = options, name, block if block_given?
6
+
7
+ html_options ||= {}
8
+ html_options = html_options.with_indifferent_access
9
+
10
+ variant = (html_options.delete(:variant) || 'contained').to_sym
11
+ color = (html_options.delete(:color) || 'default').to_sym
12
+
13
+ html_options[:class] = build_button_class(variant: variant, color: color, classes: html_options[:class])
14
+
15
+ if block_given?
16
+ view.button_to(options, html_options, &block)
17
+ else
18
+ view.button_to(name, options, html_options)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def build_button_class(variant:, color:, classes: '')
25
+ [
26
+ theme.dig(:components, :link, :root) || '',
27
+ theme.dig(:components, :link, variant.to_sym, :root) || '',
28
+ theme.dig(:components, :link, variant.to_sym, :colors, color.to_sym) || '',
29
+ classes,
30
+ ].reject(&:blank?).join(' ')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ class Code < OkonomiUiKit::Component
4
+ def render(content = nil, options = {}, &block)
5
+ options, content = content, nil if block_given?
6
+ options ||= {}
7
+ options = options.with_indifferent_access
8
+
9
+ # Extract component-specific options
10
+ language = options.delete(:language) || options.delete(:lang)
11
+ variant = (options.delete(:variant) || 'default').to_sym
12
+ size = (options.delete(:size) || 'default').to_sym
13
+ wrap = options.delete(:wrap) != false # Default to true
14
+
15
+ # Build classes
16
+ classes = build_classes(variant: variant, size: size, wrap: wrap, custom_class: options.delete(:class))
17
+
18
+ # Escape HTML entities in content
19
+ escaped_content = if block_given?
20
+ view.capture(&block)
21
+ elsif content
22
+ content
23
+ else
24
+ ""
25
+ end
26
+
27
+ view.render(
28
+ template_path,
29
+ content: escaped_content.strip.html_safe,
30
+ options: options,
31
+ classes: classes,
32
+ language: language
33
+ )
34
+ end
35
+
36
+ private
37
+
38
+ def build_classes(variant:, size:, wrap:, custom_class: nil)
39
+ base_classes = theme.dig(:components, :code, :base) || "bg-gray-900 text-gray-100 rounded-lg"
40
+
41
+ variant_classes = case variant
42
+ when :inline
43
+ "bg-gray-100 text-gray-900 px-1 py-0.5 rounded text-sm font-mono"
44
+ when :minimal
45
+ "bg-gray-900 text-gray-100 p-3 rounded text-xs"
46
+ else
47
+ # :default
48
+ "bg-gray-900 text-gray-100 p-4 rounded-lg"
49
+ end
50
+
51
+ size_classes = case size
52
+ when :xs
53
+ "text-xs"
54
+ when :sm
55
+ "text-sm"
56
+ when :lg
57
+ "text-base"
58
+ else
59
+ # :default
60
+ "text-sm"
61
+ end
62
+
63
+ wrap_classes = wrap ? "overflow-x-auto" : "overflow-hidden"
64
+
65
+ [base_classes, variant_classes, size_classes, wrap_classes, custom_class].compact.join(' ')
66
+ end
67
+
68
+ def html_escape(content)
69
+ ERB::Util.html_escape(content.to_s.strip)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,34 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ class LinkTo < OkonomiUiKit::Component
4
+ def render(name = nil, options = nil, html_options = nil, &block)
5
+ html_options, options, name = options, name, block if block_given?
6
+
7
+ html_options ||= {}
8
+ html_options = html_options.with_indifferent_access
9
+
10
+ variant = (html_options.delete(:variant) || 'text').to_sym
11
+ color = (html_options.delete(:color) || 'default').to_sym
12
+
13
+ html_options[:class] = build_button_class(variant: variant, color: color, classes: html_options[:class])
14
+
15
+ if block_given?
16
+ view.link_to(options, html_options, &block)
17
+ else
18
+ view.link_to(name, options, html_options)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def build_button_class(variant:, color:, classes: '')
25
+ [
26
+ theme.dig(:components, :link, :root) || '',
27
+ theme.dig(:components, :link, variant.to_sym, :root) || '',
28
+ theme.dig(:components, :link, variant.to_sym, :colors, color.to_sym) || '',
29
+ classes,
30
+ ].reject(&:blank?).join(' ')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,247 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ class Page < OkonomiUiKit::Component
4
+ def render(options = {}, &block)
5
+ builder = PageBuilder.new(view)
6
+
7
+ view.render(template_path, builder: builder, options: options, &block)
8
+ end
9
+ end
10
+
11
+ class PageBuilder
12
+ include ActionView::Helpers::TagHelper
13
+ include ActionView::Helpers::CaptureHelper
14
+
15
+ def initialize(template)
16
+ @template = template
17
+ @content_parts = []
18
+ end
19
+
20
+ def page_header(**options, &block)
21
+ header_builder = PageHeaderBuilder.new(@template)
22
+ yield(header_builder) if block_given?
23
+ @content_parts << header_builder.render
24
+ nil
25
+ end
26
+
27
+ def section(**options, &block)
28
+ section_builder = SectionBuilder.new(@template)
29
+ section_builder.title(options[:title]) if options[:title]
30
+ yield(section_builder) if block_given?
31
+ @content_parts << section_builder.render
32
+ nil
33
+ end
34
+
35
+ def render_content
36
+ @template.safe_join(@content_parts)
37
+ end
38
+
39
+ def to_s
40
+ render_content
41
+ end
42
+
43
+ private
44
+
45
+ def tag
46
+ @template.tag
47
+ end
48
+
49
+ def capture(*args, &block)
50
+ @template.capture(*args, &block)
51
+ end
52
+ end
53
+
54
+ class PageHeaderBuilder
55
+ include ActionView::Helpers::TagHelper
56
+ include ActionView::Helpers::CaptureHelper
57
+
58
+ def initialize(template)
59
+ @template = template
60
+ @breadcrumbs_content = nil
61
+ @row_content = nil
62
+ end
63
+
64
+ def breadcrumbs(&block)
65
+ @breadcrumbs_content = @template.breadcrumbs(&block)
66
+ end
67
+
68
+ def row(&block)
69
+ row_builder = PageHeaderRowBuilder.new(@template)
70
+ yield(row_builder) if block_given?
71
+ @row_content = row_builder.render
72
+ end
73
+
74
+ def render
75
+ content = []
76
+ content << @breadcrumbs_content if @breadcrumbs_content
77
+ content << @row_content if @row_content
78
+
79
+ tag.div(class: "flex flex-col gap-2") do
80
+ @template.safe_join(content.compact)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def tag
87
+ @template.tag
88
+ end
89
+
90
+ def capture(*args, &block)
91
+ @template.capture(*args, &block)
92
+ end
93
+ end
94
+
95
+ class PageHeaderRowBuilder
96
+ include ActionView::Helpers::TagHelper
97
+ include ActionView::Helpers::CaptureHelper
98
+
99
+ def initialize(template)
100
+ @template = template
101
+ @title_content = nil
102
+ @actions_content = nil
103
+ end
104
+
105
+ def title(text, **options)
106
+ @title_content = tag.h1(text, class: "text-2xl font-bold leading-7 text-gray-900 truncate sm:text-3xl sm:tracking-tight")
107
+ end
108
+
109
+ def actions(&block)
110
+ @actions_content = tag.div(class: "mt-4 flex md:ml-4 md:mt-0 gap-2") do
111
+ capture(&block) if block_given?
112
+ end
113
+ end
114
+
115
+ def render
116
+ tag.div(class: "flex w-full justify-between items-center") do
117
+ content = []
118
+ content << @title_content if @title_content
119
+ content << @actions_content if @actions_content
120
+ @template.safe_join(content.compact)
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def tag
127
+ @template.tag
128
+ end
129
+
130
+ def capture(*args, &block)
131
+ @template.capture(*args, &block)
132
+ end
133
+ end
134
+
135
+ class SectionBuilder
136
+ include ActionView::Helpers::TagHelper
137
+ include ActionView::Helpers::CaptureHelper
138
+
139
+ def initialize(template)
140
+ @template = template
141
+ @title_content = nil
142
+ @subtitle_content = nil
143
+ @actions_content = nil
144
+ @body_content = nil
145
+ @attributes = []
146
+ end
147
+
148
+ def title(text, **options)
149
+ @title_content = tag.h3(text, class: "text-base/7 font-semibold text-gray-900")
150
+ end
151
+
152
+ def subtitle(text, **options)
153
+ @subtitle_content = tag.p(text, class: "mt-1 max-w-2xl text-sm/6 text-gray-500")
154
+ end
155
+
156
+ def actions(&block)
157
+ @actions_content = tag.div(class: "mt-4 flex md:ml-4 md:mt-0") do
158
+ capture(&block) if block_given?
159
+ end
160
+ end
161
+
162
+ def body(&block)
163
+ if block_given?
164
+ # Capture the content first to see if attributes were used
165
+ content = capture { yield(self) }
166
+
167
+ @body_content = if @attributes.any?
168
+ # If attributes were added, wrap them in dl
169
+ tag.div do
170
+ tag.dl(class: "divide-y divide-gray-100") do
171
+ @template.safe_join(@attributes)
172
+ end
173
+ end
174
+ else
175
+ # Otherwise, just return the captured content
176
+ tag.div do
177
+ content
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def attribute(label, value = nil, **options, &block)
184
+ content = if block_given?
185
+ capture(&block)
186
+ elsif value.respond_to?(:call)
187
+ value.call
188
+ else
189
+ value
190
+ end
191
+
192
+ attribute_html = tag.div(class: "py-6 sm:grid sm:grid-cols-3 sm:gap-4") do
193
+ dt_content = tag.dt(label, class: "text-sm font-medium text-gray-900")
194
+ dd_content = tag.dd(content, class: "mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0")
195
+
196
+ dt_content + dd_content
197
+ end
198
+
199
+ @attributes << attribute_html
200
+ end
201
+
202
+ def render
203
+ tag.div(class: "overflow-hidden bg-white") do
204
+ header_content = build_header
205
+ content_parts = []
206
+ content_parts << header_content if header_content.present?
207
+ content_parts << @body_content if @body_content
208
+ @template.safe_join(content_parts.compact)
209
+ end
210
+ end
211
+
212
+ private
213
+
214
+ def build_header
215
+ return nil unless @title_content || @subtitle_content || @actions_content
216
+
217
+ tag.div(class: "py-6") do
218
+ if @actions_content
219
+ tag.div(class: "flex w-full justify-between items-start") do
220
+ title_section = tag.div do
221
+ content_parts = []
222
+ content_parts << @title_content if @title_content
223
+ content_parts << @subtitle_content if @subtitle_content
224
+ @template.safe_join(content_parts.compact)
225
+ end
226
+
227
+ title_section + @actions_content
228
+ end
229
+ else
230
+ content_parts = []
231
+ content_parts << @title_content if @title_content
232
+ content_parts << @subtitle_content if @subtitle_content
233
+ @template.safe_join(content_parts.compact)
234
+ end
235
+ end
236
+ end
237
+
238
+ def tag
239
+ @template.tag
240
+ end
241
+
242
+ def capture(*args, &block)
243
+ @template.capture(*args, &block)
244
+ end
245
+ end
246
+ end
247
+ end