okonomi_ui_kit 0.1.2 → 0.1.4

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/okonomi_ui_kit/application.tailwind.css +336 -53
  3. data/app/assets/stylesheets/okonomi_ui_kit/application.tailwind.css +75 -3
  4. data/app/helpers/okonomi_ui_kit/attribute_section_helper.rb +1 -1
  5. data/app/helpers/okonomi_ui_kit/form_builder.rb +167 -158
  6. data/app/helpers/okonomi_ui_kit/navigation_helper.rb +2 -2
  7. data/app/helpers/okonomi_ui_kit/page_builder_helper.rb +1 -1
  8. data/app/helpers/okonomi_ui_kit/table_helper.rb +1 -1
  9. data/app/helpers/okonomi_ui_kit/theme.rb +107 -0
  10. data/app/helpers/okonomi_ui_kit/theme_helper.rb +17 -0
  11. data/app/helpers/okonomi_ui_kit/ui_helper.rb +110 -0
  12. data/app/javascript/okonomi_ui_kit/controllers/file_input_controller.js +13 -0
  13. data/app/javascript/okonomi_ui_kit/controllers/flash_controller.js +31 -0
  14. data/app/javascript/okonomi_ui_kit/controllers/form_field_visibility_controller.js +27 -0
  15. data/app/javascript/okonomi_ui_kit/controllers/upload_controller.js +55 -0
  16. data/app/views/okonomi/components/_typography.html.erb +7 -0
  17. data/app/views/{okonomi_ui_kit → okonomi}/forms/tailwind/_checkbox_label.html.erb +2 -2
  18. data/app/views/{okonomi_ui_kit → okonomi}/forms/tailwind/_field.html.erb +2 -2
  19. data/config/importmap.rb +5 -0
  20. data/lib/okonomi_ui_kit/engine.rb +3 -2
  21. data/lib/okonomi_ui_kit/version.rb +1 -1
  22. metadata +49 -14
  23. data/app/helpers/okonomi_ui_kit/button_helper.rb +0 -39
  24. /data/app/views/{okonomi_ui_kit → okonomi}/attribute_sections/_section.html.erb +0 -0
  25. /data/app/views/{okonomi_ui_kit → okonomi}/forms/tailwind/_field_set.html.erb +0 -0
  26. /data/app/views/{okonomi_ui_kit → okonomi}/forms/tailwind/_multi_select.html.erb +0 -0
  27. /data/app/views/{okonomi_ui_kit → okonomi}/forms/tailwind/_radio_button.html.erb +0 -0
  28. /data/app/views/{okonomi_ui_kit → okonomi}/forms/tailwind/_upload_field.html.erb +0 -0
  29. /data/app/views/{okonomi_ui_kit → okonomi}/navigation/_link.html.erb +0 -0
  30. /data/app/views/{okonomi_ui_kit → okonomi}/navigation/_menu.html.erb +0 -0
  31. /data/app/views/{okonomi_ui_kit → okonomi}/navigation/_navbar.html.erb +0 -0
  32. /data/app/views/{okonomi_ui_kit → okonomi}/page_builder/_page.html.erb +0 -0
  33. /data/app/views/{okonomi_ui_kit → okonomi}/tables/_table.html.erb +0 -0
@@ -0,0 +1,110 @@
1
+ module OkonomiUiKit
2
+ module UiHelper
3
+ def ui
4
+ @ui ||= UiBuilder.new(self)
5
+ end
6
+
7
+ class UiBuilder
8
+ include ActionView::Helpers::TagHelper
9
+ include ActionView::Helpers::CaptureHelper
10
+
11
+ def initialize(template)
12
+ @template = template
13
+ end
14
+
15
+ def theme(t = {}, &block)
16
+ old_theme = get_theme
17
+
18
+ @_okonomi_ui_kit_theme = {}.merge(old_theme).merge(t || {})
19
+
20
+ yield(@_okonomi_ui_kit_theme)
21
+
22
+ @_okonomi_ui_kit_theme = old_theme
23
+ end
24
+
25
+ def get_theme
26
+ @_okonomi_ui_kit_theme ||= OkonomiUiKit::Theme::DEFAULT_THEME
27
+ end
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
+ def button_class(variant: 'contained', color: 'default', classes: '')
66
+ [
67
+ get_theme.dig(:components, :link, :root) || '',
68
+ get_theme.dig(:components, :link, variant.to_sym, :root) || '',
69
+ get_theme.dig(:components, :link, variant.to_sym, :colors, color.to_sym) || '',
70
+ classes,
71
+ ].join(' ')
72
+ end
73
+
74
+ def page(&block)
75
+ @template.page(&block)
76
+ end
77
+
78
+ TYPOGRAPHY_COMPONENTS = {
79
+ body1: 'p',
80
+ body2: 'p',
81
+ h1: 'h1',
82
+ h2: 'h2',
83
+ h3: 'h3',
84
+ h4: 'h4',
85
+ h5: 'h5',
86
+ h6: 'h6',
87
+ }.freeze
88
+
89
+ def typography(text = nil, options = nil, &block)
90
+ options, text = text, nil if block_given?
91
+ options ||= {}
92
+
93
+ variant = (options.delete(:variant) || 'body1').to_sym
94
+ component = (TYPOGRAPHY_COMPONENTS[variant] || 'span').to_s
95
+ color = (options.delete(:color) || 'default').to_sym
96
+ classes = [
97
+ get_theme.dig(:components, :typography, :variants, variant) || '',
98
+ get_theme.dig(:components, :typography, :colors, color) || '',
99
+ options.delete(:class) || ''
100
+ ]
101
+
102
+ if block_given?
103
+ @template.render("okonomi/components/typography", options:, variant:, component:, classes:, &block)
104
+ else
105
+ @template.render("okonomi/components/typography", text:, options:, variant:, component:, classes:)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,13 @@
1
+ // app/javascript/controllers/file_input_controller.js
2
+ import { Controller } from "@hotwired/stimulus"
3
+
4
+ export default class extends Controller {
5
+ connect() {
6
+ this.input = this.element
7
+ this.label = document.getElementById("file-name")
8
+
9
+ this.input.addEventListener("change", () => {
10
+ this.label.textContent = this.input.files[0]?.name || "No file chosen"
11
+ })
12
+ }
13
+ }
@@ -0,0 +1,31 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["message"]
5
+ static values = { autoDismiss: Number }
6
+
7
+ connect() {
8
+ if (this.hasAutoDismissValue && this.autoDismissValue > 0) {
9
+ setTimeout(() => {
10
+ this.dismiss()
11
+ }, this.autoDismissValue)
12
+ }
13
+ }
14
+
15
+ dismiss() {
16
+ this.element.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'
17
+ this.element.style.opacity = '0'
18
+ this.element.style.transform = 'translateX(100%)'
19
+
20
+ setTimeout(() => {
21
+ if (this.element.parentNode) {
22
+ this.element.remove()
23
+ }
24
+ }, 300)
25
+ }
26
+
27
+ close(event) {
28
+ event.preventDefault()
29
+ this.dismiss()
30
+ }
31
+ }
@@ -0,0 +1,27 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static values = { fieldId: String, equals: String }
5
+
6
+ connect() {
7
+ console.log("FormFieldVisibilityController connected")
8
+ this.field = document.getElementById(this.fieldIdValue)
9
+ if (this.field) {
10
+ this.toggle()
11
+ this.field.addEventListener("change", this.toggle.bind(this))
12
+ }
13
+ }
14
+
15
+ toggle() {
16
+ let currentValue
17
+
18
+ if (this.field.type === "checkbox") {
19
+ currentValue = this.field.checked ? this.field.value : null
20
+ } else {
21
+ currentValue = this.field.value
22
+ }
23
+
24
+ const shouldShow = currentValue === this.equalsValue
25
+ this.element.classList.toggle("hidden", !shouldShow)
26
+ }
27
+ }
@@ -0,0 +1,55 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["input", "filename", "dropzone", "preview", "destroyInput"]
5
+
6
+ connect() {
7
+ this.dropzoneTarget.addEventListener("dragover", (e) => {
8
+ e.preventDefault()
9
+ this.dropzoneTarget.classList.add("bg-gray-100")
10
+ })
11
+
12
+ this.dropzoneTarget.addEventListener("dragleave", () => {
13
+ this.dropzoneTarget.classList.remove("bg-gray-100")
14
+ })
15
+
16
+ this.dropzoneTarget.addEventListener("drop", (e) => {
17
+ e.preventDefault()
18
+ this.dropzoneTarget.classList.remove("bg-gray-100")
19
+ const files = e.dataTransfer.files
20
+ if (files.length > 0) {
21
+ this.inputTarget.files = files
22
+ this.updatePreview()
23
+ }
24
+ })
25
+ }
26
+
27
+ updatePreview() {
28
+ const file = this.inputTarget.files[0]
29
+ this.filenameTarget.textContent = file ? file.name : "No file selected"
30
+ this.destroyInputTarget.value = "0"
31
+
32
+ if (file && file.type.startsWith("image/")) {
33
+ const reader = new FileReader()
34
+ reader.onload = (e) => {
35
+ this.previewTarget.innerHTML = `<img src="${e.target.result}" class="max-h-32 rounded" />`
36
+ }
37
+ reader.readAsDataURL(file)
38
+ } else if (file) {
39
+ this.previewTarget.innerHTML = `
40
+ <svg class="size-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
41
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16v-4m0 0V7m0 5H3m4 0h4" />
42
+ </svg>`
43
+ }
44
+ }
45
+
46
+ clear() {
47
+ this.inputTarget.value = ""
48
+ this.filenameTarget.textContent = "No file selected"
49
+ this.previewTarget.innerHTML = `
50
+ <svg class="size-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
51
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16v-4m0 0V7m0 5H3m4 0h4" />
52
+ </svg>`
53
+ this.destroyInputTarget.value = "1"
54
+ }
55
+ }
@@ -0,0 +1,7 @@
1
+ <%= content_tag component, class: classes do %>
2
+ <% if defined?(text)%>
3
+ <%= text %>
4
+ <% else %>
5
+ <%= yield %>
6
+ <% end %>
7
+ <% end %>
@@ -3,7 +3,7 @@
3
3
  label_options[:for] = options[:id] if options[:id]
4
4
  %>
5
5
  <%= form.label(method, label_options) do %>
6
- <div class="cursor-pointer font-medium text-gray-700">
6
+ <div class="<%= form.ui.get_theme.dig(:components, :checkbox, :label, :root) %>">
7
7
  <% if options[:label] %>
8
8
  <%= options[:label] %>
9
9
  <% else %>
@@ -11,7 +11,7 @@
11
11
  <% end %>
12
12
  </div>
13
13
  <% if options[:hint] %>
14
- <div class="cursor-pointer text-sm text-gray-400">
14
+ <div class="<%= form.ui.get_theme.dig(:components, :checkbox, :hint, :root) %>">
15
15
  <%= t("activerecord.hints.#{form.object.class.name.underscore}.#{method}") %>
16
16
  </div>
17
17
  <% end %>
@@ -1,7 +1,7 @@
1
1
  <div class="w-full flex flex-col gap-2">
2
2
  <div class="flex justify-between items-center" x-data="{ open: false }">
3
3
  <% if field_id %>
4
- <%= form.label field_id, t("activerecord.attributes.#{form.object_name}.#{field_id}"), class: form.label_class(options.dig(:label, :class)) %>
4
+ <%= form.label field_id, t("activerecord.attributes.#{form.object_name}.#{field_id}") %>
5
5
  <% end %>
6
6
  <% if options[:hint] %>
7
7
  <div class="relative">
@@ -26,7 +26,7 @@
26
26
  <%= yield %>
27
27
  </div>
28
28
  <% if field_id && form.object.errors.include?(field_id) %>
29
- <div class="mt-1 text-red-500 text-sm">
29
+ <div class="mt-1 text-danger-600 text-sm">
30
30
  <%= form.object.errors[field_id].join(", ") %>
31
31
  </div>
32
32
  <% end %>
data/config/importmap.rb CHANGED
@@ -1,2 +1,7 @@
1
+ pin "application", to: "okonomi_ui_kit/application.js", preload: true
2
+ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
3
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
4
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
5
+
1
6
  pin "okonomi_ui_kit", to: "okonomi_ui_kit/application.js", preload: true
2
7
  pin_all_from OkonomiUiKit::Engine.root.join("app/javascript/okonomi_ui_kit/controllers"), under: "controllers", to: "okonomi_ui_kit/controllers"
@@ -24,12 +24,13 @@ module OkonomiUiKit
24
24
  include OkonomiUiKit::AttributeSectionHelper
25
25
  include OkonomiUiKit::BadgeHelper
26
26
  include OkonomiUiKit::BreadcrumbsHelper
27
- include OkonomiUiKit::ButtonHelper
28
27
  include OkonomiUiKit::IconHelper
29
28
  include OkonomiUiKit::NavigationHelper
30
29
  include OkonomiUiKit::PageBuilderHelper
31
30
  include OkonomiUiKit::TableHelper
32
- # include OkonomiUiKit::FormBuilder
31
+ include OkonomiUiKit::UiHelper
32
+
33
+ ActionView::Base.field_error_proc = ->(html_tag, _instance) { html_tag.html_safe }
33
34
  end
34
35
  end
35
36
  end
@@ -1,3 +1,3 @@
1
1
  module OkonomiUiKit
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: okonomi_ui_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Okonomi GmbH
@@ -23,6 +23,34 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: 8.0.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: tailwindcss-ruby
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: tailwindcss-rails
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
26
54
  description: UI Kit for Okonomi applications
27
55
  email:
28
56
  - andre.lahs@okonomi.gmbh
@@ -1330,32 +1358,39 @@ files:
1330
1358
  - app/helpers/okonomi_ui_kit/attribute_section_helper.rb
1331
1359
  - app/helpers/okonomi_ui_kit/badge_helper.rb
1332
1360
  - app/helpers/okonomi_ui_kit/breadcrumbs_helper.rb
1333
- - app/helpers/okonomi_ui_kit/button_helper.rb
1334
1361
  - app/helpers/okonomi_ui_kit/form_builder.rb
1335
1362
  - app/helpers/okonomi_ui_kit/icon_helper.rb
1336
1363
  - app/helpers/okonomi_ui_kit/navigation_helper.rb
1337
1364
  - app/helpers/okonomi_ui_kit/page_builder_helper.rb
1338
1365
  - app/helpers/okonomi_ui_kit/svg_icons.rb
1339
1366
  - app/helpers/okonomi_ui_kit/table_helper.rb
1367
+ - app/helpers/okonomi_ui_kit/theme.rb
1368
+ - app/helpers/okonomi_ui_kit/theme_helper.rb
1369
+ - app/helpers/okonomi_ui_kit/ui_helper.rb
1340
1370
  - app/javascript/okonomi_ui_kit/application.js
1341
1371
  - app/javascript/okonomi_ui_kit/controllers/dropdown_controller.js
1372
+ - app/javascript/okonomi_ui_kit/controllers/file_input_controller.js
1373
+ - app/javascript/okonomi_ui_kit/controllers/flash_controller.js
1374
+ - app/javascript/okonomi_ui_kit/controllers/form_field_visibility_controller.js
1375
+ - app/javascript/okonomi_ui_kit/controllers/upload_controller.js
1342
1376
  - app/javascript/okonomi_ui_kit_manifest.js
1343
1377
  - app/jobs/okonomi_ui_kit/application_job.rb
1344
1378
  - app/mailers/okonomi_ui_kit/application_mailer.rb
1345
1379
  - app/models/okonomi_ui_kit/application_record.rb
1346
1380
  - app/views/layouts/okonomi_ui_kit/application.html.erb
1347
- - app/views/okonomi_ui_kit/attribute_sections/_section.html.erb
1348
- - app/views/okonomi_ui_kit/forms/tailwind/_checkbox_label.html.erb
1349
- - app/views/okonomi_ui_kit/forms/tailwind/_field.html.erb
1350
- - app/views/okonomi_ui_kit/forms/tailwind/_field_set.html.erb
1351
- - app/views/okonomi_ui_kit/forms/tailwind/_multi_select.html.erb
1352
- - app/views/okonomi_ui_kit/forms/tailwind/_radio_button.html.erb
1353
- - app/views/okonomi_ui_kit/forms/tailwind/_upload_field.html.erb
1354
- - app/views/okonomi_ui_kit/navigation/_link.html.erb
1355
- - app/views/okonomi_ui_kit/navigation/_menu.html.erb
1356
- - app/views/okonomi_ui_kit/navigation/_navbar.html.erb
1357
- - app/views/okonomi_ui_kit/page_builder/_page.html.erb
1358
- - app/views/okonomi_ui_kit/tables/_table.html.erb
1381
+ - app/views/okonomi/attribute_sections/_section.html.erb
1382
+ - app/views/okonomi/components/_typography.html.erb
1383
+ - app/views/okonomi/forms/tailwind/_checkbox_label.html.erb
1384
+ - app/views/okonomi/forms/tailwind/_field.html.erb
1385
+ - app/views/okonomi/forms/tailwind/_field_set.html.erb
1386
+ - app/views/okonomi/forms/tailwind/_multi_select.html.erb
1387
+ - app/views/okonomi/forms/tailwind/_radio_button.html.erb
1388
+ - app/views/okonomi/forms/tailwind/_upload_field.html.erb
1389
+ - app/views/okonomi/navigation/_link.html.erb
1390
+ - app/views/okonomi/navigation/_menu.html.erb
1391
+ - app/views/okonomi/navigation/_navbar.html.erb
1392
+ - app/views/okonomi/page_builder/_page.html.erb
1393
+ - app/views/okonomi/tables/_table.html.erb
1359
1394
  - config/importmap.rb
1360
1395
  - config/routes.rb
1361
1396
  - config/tailwind.config.js
@@ -1,39 +0,0 @@
1
- module OkonomiUiKit
2
- module ButtonHelper
3
- def okonomi_button(text, url = nil, variant: :primary, size: :medium, **options)
4
- base_classes = "inline-flex items-center justify-center font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors"
5
-
6
- variant_classes = case variant
7
- when :primary
8
- "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500"
9
- when :secondary
10
- "bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500"
11
- when :danger
12
- "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500"
13
- when :ghost
14
- "bg-transparent text-gray-700 hover:bg-gray-100 focus:ring-gray-500"
15
- else
16
- "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500"
17
- end
18
-
19
- size_classes = case size
20
- when :small
21
- "px-3 py-1.5 text-sm"
22
- when :medium
23
- "px-4 py-2 text-sm"
24
- when :large
25
- "px-6 py-3 text-base"
26
- else
27
- "px-4 py-2 text-sm"
28
- end
29
-
30
- options[:class] = [base_classes, variant_classes, size_classes, options[:class]].compact.join(" ")
31
-
32
- if url
33
- link_to text, url, options
34
- else
35
- content_tag :button, text, options
36
- end
37
- end
38
- end
39
- end