lightning_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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/lightning_ui_kit.css +1813 -65
  3. data/app/assets/builds/lightning_ui_kit.js +2 -2
  4. data/app/assets/builds/lightning_ui_kit.js.map +4 -4
  5. data/app/assets/vendor/lightning_ui_kit.css +2883 -0
  6. data/app/assets/vendor/lightning_ui_kit.js +5 -0
  7. data/app/components/lightning_ui_kit/badge_component.rb +30 -5
  8. data/app/components/lightning_ui_kit/banner_component.rb +1 -1
  9. data/app/components/lightning_ui_kit/checkbox_component.html.erb +1 -1
  10. data/app/components/lightning_ui_kit/dropzone_component.html.erb +82 -0
  11. data/app/components/lightning_ui_kit/dropzone_component.rb +61 -0
  12. data/app/components/lightning_ui_kit/errors.rb +34 -0
  13. data/app/components/lightning_ui_kit/file_input_component.html.erb +50 -0
  14. data/app/components/lightning_ui_kit/file_input_component.rb +62 -0
  15. data/app/components/lightning_ui_kit/input_component.html.erb +40 -23
  16. data/app/components/lightning_ui_kit/input_component.rb +17 -2
  17. data/app/components/lightning_ui_kit/select_component.html.erb +23 -3
  18. data/app/components/lightning_ui_kit/select_component.rb +31 -1
  19. data/app/components/lightning_ui_kit/switch_component.html.erb +9 -2
  20. data/app/components/lightning_ui_kit/switch_component.rb +12 -1
  21. data/app/components/lightning_ui_kit/textarea_component.html.erb +17 -10
  22. data/app/components/lightning_ui_kit/textarea_component.rb +17 -2
  23. data/app/javascript/lightning_ui_kit/controllers/accordion_controller.js +11 -6
  24. data/app/javascript/lightning_ui_kit/controllers/dropzone_controller.js +79 -0
  25. data/app/javascript/lightning_ui_kit/index.js +5 -0
  26. data/config/locales/en.yml +3 -0
  27. data/lib/lightning_ui_kit/engine.rb +0 -1
  28. data/lib/lightning_ui_kit/version.rb +1 -1
  29. metadata +11 -2
@@ -2,12 +2,12 @@
2
2
  class: classes,
3
3
  data:
4
4
  ) do %>
5
- <% if @label %>
6
- <%= tag.label(
7
- @label,
8
- class: "text-base/6 text-zinc-950 select-none data-disabled:opacity-50 sm:text-sm/6",
9
- data: label_data
10
- ) %>
5
+ <% if @label %>
6
+ <%= tag.label(
7
+ @label,
8
+ class: "text-base/6 text-zinc-950 select-none data-disabled:opacity-50 sm:text-sm/6",
9
+ data: label_data
10
+ ) %>
11
11
  <% end %>
12
12
  <% if @description %>
13
13
  <%= tag.p(
@@ -17,13 +17,13 @@
17
17
  ) %>
18
18
  <% end %>
19
19
  <% if @form %>
20
- <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
20
+ <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
21
21
  <% case @type %>
22
22
  <% when :text %>
23
23
  <%= @form.text_field(
24
24
  @name,
25
25
  data: input_data,
26
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
26
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
27
27
  disabled: @disabled,
28
28
  autofocus: @autofocus,
29
29
  placeholder: @placeholder
@@ -32,68 +32,85 @@
32
32
  <%= @form.email_field(
33
33
  @name,
34
34
  data: input_data,
35
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
35
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
36
36
  disabled: @disabled,
37
- autofocus: @autofocus
37
+ autofocus: @autofocus,
38
+ placeholder: @placeholder
38
39
  ) %>
39
40
  <% when :password %>
40
41
  <%= @form.password_field(
41
42
  @name,
42
43
  data: input_data,
43
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
44
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
44
45
  disabled: @disabled,
45
- autofocus: @autofocus
46
+ autofocus: @autofocus,
47
+ placeholder: @placeholder
46
48
  ) %>
47
49
  <% when :number %>
48
50
  <%= @form.number_field(
49
51
  @name,
50
52
  data: input_data,
51
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
53
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
52
54
  disabled: @disabled,
53
- autofocus: @autofocus
55
+ autofocus: @autofocus,
56
+ placeholder: @placeholder,
57
+ min: @options[:min],
58
+ max: @options[:max],
59
+ step: @options[:step]
54
60
  ) %>
55
61
  <% end %>
56
62
  </span>
57
63
  <% else %>
58
- <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
64
+ <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
59
65
  <% case @type %>
60
66
  <% when :text %>
61
67
  <%= text_field_tag(
62
68
  @name,
63
69
  @value,
64
70
  data: input_data,
65
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
71
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
66
72
  disabled: @disabled,
67
- autofocus: @autofocus
73
+ autofocus: @autofocus,
74
+ placeholder: @placeholder
68
75
  ) %>
69
76
  <% when :email %>
70
77
  <%= email_field_tag(
71
78
  @name,
72
79
  @value,
73
80
  data: input_data,
74
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
81
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
75
82
  disabled: @disabled,
76
- autofocus: @autofocus
83
+ autofocus: @autofocus,
84
+ placeholder: @placeholder
77
85
  ) %>
78
86
  <% when :password %>
79
87
  <%= password_field_tag(
80
88
  @name,
81
89
  @value,
82
90
  data: input_data,
83
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
91
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
84
92
  disabled: @disabled,
85
- autofocus: @autofocus
93
+ autofocus: @autofocus,
94
+ placeholder: @placeholder
86
95
  ) %>
87
96
  <% when :number %>
88
97
  <%= number_field_tag(
89
98
  @name,
90
99
  @value,
91
100
  data: input_data,
92
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
101
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500/60 data-disabled:border-zinc-950/20",
93
102
  disabled: @disabled,
94
- autofocus: @autofocus
103
+ autofocus: @autofocus,
104
+ placeholder: @placeholder,
95
105
  ) %>
96
106
  <% end %>
97
107
  </span>
98
108
  <% end %>
109
+ <% if has_errors? %>
110
+ <%= tag.p(
111
+ error_messages,
112
+ class: "text-base/6 text-red-600 data-disabled:opacity-50 sm:text-sm/6",
113
+ data: error_data
114
+ ) %>
115
+ <% end %>
99
116
  <% end %>
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class LightningUiKit::InputComponent < LightningUiKit::BaseComponent
4
- def initialize(name:, value: nil, autofocus: false, label: nil, form: nil, type: :text, description: nil, disabled: false, placeholder: nil, **options)
4
+ include LightningUiKit::Errors
5
+
6
+ def initialize(name:, value: nil, autofocus: false, label: nil, form: nil, type: :text, description: nil, disabled: false, placeholder: nil, error: nil, **options)
5
7
  @name = name
6
8
  @value = value
7
9
  @disabled = disabled
8
10
  @autofocus = autofocus
11
+ @error = error
9
12
  @label = label
10
13
  @form = form
11
14
  @type = type
@@ -23,7 +26,11 @@ class LightningUiKit::InputComponent < LightningUiKit::BaseComponent
23
26
  end
24
27
 
25
28
  def input_data
26
- @options[:input_data] || {}
29
+ (@options[:input_data] || {}).tap do |data|
30
+ if has_errors?
31
+ data[:invalid] = "true"
32
+ end
33
+ end
27
34
  end
28
35
 
29
36
  def label_data
@@ -41,4 +48,12 @@ class LightningUiKit::InputComponent < LightningUiKit::BaseComponent
41
48
  end
42
49
  end
43
50
  end
51
+
52
+ def error_data
53
+ {slot: "error"}.merge(@options[:error_data] || {}).tap do |data|
54
+ if @disabled
55
+ data[:disabled] = "true"
56
+ end
57
+ end
58
+ end
44
59
  end
@@ -9,12 +9,32 @@
9
9
  <%= @description %>
10
10
  </p>
11
11
  <% end %>
12
- <span data-slot="control" class="group relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset has-data-focus:after:ring-2 has-data-focus:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none">
12
+ <span data-slot="control"
13
+ class="group relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset focus:after:ring-2 focus:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none">
13
14
  <% if @form %>
14
- <%= @form.select(@name, @options_for_select, {multiple: @multiple}, { class: "relative block w-full appearance-none rounded-lg py-[calc(--spacing(2.5)-1px)] sm:py-[calc(--spacing(1.5)-1px)] pr-[calc(--spacing(10)-1px)] pl-[calc(--spacing(3.5)-1px)] sm:pr-[calc(--spacing(9)-1px)] sm:pl-[calc(--spacing(3)-1px)] [&_optgroup]:font-semibold text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20 data-disabled:opacity-100"}) %>
15
+ <%= @form.select(
16
+ @name,
17
+ @options_for_select,
18
+ {multiple: @multiple},
19
+ class: "relative block w-full appearance-none rounded-lg py-[calc(--spacing(2.5)-1px)] sm:py-[calc(--spacing(1.5)-1px)] pr-[calc(--spacing(10)-1px)] pl-[calc(--spacing(3.5)-1px)] sm:pr-[calc(--spacing(9)-1px)] sm:pl-[calc(--spacing(3)-1px)] [&_optgroup]:font-semibold text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500 data-disabled:border-zinc-950/20 data-disabled:opacity-100",
20
+ data: select_data
21
+ ) %>
15
22
  <% else %>
16
- <%= select_tag(@name, @options_for_select, multiple: @multiple, class: "relative block w-full appearance-none rounded-lg py-[calc(--spacing(2.5)-1px)] sm:py-[calc(--spacing(1.5)-1px)] pr-[calc(--spacing(10)-1px)] pl-[calc(--spacing(3.5)-1px)] sm:pr-[calc(--spacing(9)-1px)] sm:pl-[calc(--spacing(3)-1px)] [&_optgroup]:font-semibold text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20 data-disabled:opacity-100") %>
23
+ <%= select_tag(
24
+ @name,
25
+ @options_for_select,
26
+ multiple: @multiple,
27
+ class: "relative block w-full appearance-none rounded-lg py-[calc(--spacing(2.5)-1px)] sm:py-[calc(--spacing(1.5)-1px)] pr-[calc(--spacing(10)-1px)] pl-[calc(--spacing(3.5)-1px)] sm:pr-[calc(--spacing(9)-1px)] sm:pl-[calc(--spacing(3)-1px)] [&_optgroup]:font-semibold text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500 data-disabled:border-zinc-950/20 data-disabled:opacity-100",
28
+ data: select_data
29
+ ) %>
17
30
  <% end %>
18
31
  <span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"><svg class="size-5 stroke-zinc-500 group-has-data-disabled:stroke-zinc-600 sm:size-4 forced-colors:stroke-[CanvasText]" viewBox="0 0 16 16" aria-hidden="true" fill="none"><path d="M5.75 10.75L8 13L10.25 10.75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.25 5.25L8 3L5.75 5.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg></span>
19
32
  </span>
33
+ <% if has_errors? %>
34
+ <%= tag.p(
35
+ error_messages,
36
+ class: "text-base/6 text-red-600 data-disabled:opacity-50 sm:text-sm/6",
37
+ data: error_data
38
+ ) %>
39
+ <% end %>
20
40
  <% end %>
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class LightningUiKit::SelectComponent < LightningUiKit::BaseComponent
4
- def initialize(name:, form: nil, label: nil, description: nil, disabled: false, options_for_select: [], multiple: false, **options)
4
+ include LightningUiKit::Errors
5
+
6
+ def initialize(name:, form: nil, label: nil, errors: nil, description: nil, disabled: false, options_for_select: [], multiple: false, **options)
5
7
  @name = name
6
8
  @form = form
7
9
  @label = label
10
+ @errors = errors
8
11
  @multiple = multiple
9
12
  @description = description
10
13
  @disabled = disabled
@@ -22,4 +25,31 @@ class LightningUiKit::SelectComponent < LightningUiKit::BaseComponent
22
25
 
23
26
  default_data.merge(@options[:data] || {})
24
27
  end
28
+
29
+ def select_data
30
+ {}.tap do |data|
31
+ if has_errors?
32
+ data[:invalid] = "true"
33
+ end
34
+ end
35
+ end
36
+
37
+ def control_data
38
+ {slot: "control"}.merge(@options[:control_data] || {}).tap do |data|
39
+ if @disabled
40
+ data[:disabled] = "true"
41
+ end
42
+ if has_errors?
43
+ data[:invalid] = "true"
44
+ end
45
+ end
46
+ end
47
+
48
+ def error_data
49
+ {slot: "error"}.merge(@options[:error_data] || {}).tap do |data|
50
+ if @disabled
51
+ data[:disabled] = "true"
52
+ end
53
+ end
54
+ end
25
55
  end
@@ -1,6 +1,6 @@
1
1
  <div
2
2
  data-slot="field"
3
- class="grid grid-cols-[1fr_auto] items-center gap-x-8 gap-y-1 sm:grid-cols-[1fr_auto] *:data-[slot=control]:col-start-2 *:data-[slot=control]:self-center *:data-[slot=label]:col-start-1 *:data-[slot=label]:row-start-1 *:data-[slot=label]:justify-self-start *:data-[slot=description]:col-start-1 *:data-[slot=description]:row-start-2 has-data-[slot=description]:**:data-[slot=label]:font-medium"
3
+ class="grid grid-cols-[1fr_auto] items-center gap-x-8 gap-y-1 sm:grid-cols-[1fr_auto] *:data-[slot=control]:col-start-2 *:data-[slot=control]:self-center *:data-[slot=label]:col-start-1 *:data-[slot=label]:row-start-1 *:data-[slot=label]:justify-self-start *:data-[slot=description]:col-start-1 *:data-[slot=description]:row-start-2 has-data-[slot=description]:**:data-[slot=label]:font-medium *:data-[slot=error]:col-start-1 *:data-[slot=error]:row-start-3"
4
4
  data-controller="lui-switch"
5
5
  >
6
6
  <% if @label %>
@@ -13,8 +13,15 @@
13
13
  <%= @description %>
14
14
  </p>
15
15
  <% end %>
16
+ <% if has_errors? %>
17
+ <%= tag.p(
18
+ error_messages,
19
+ class: "text-base/6 text-red-600 data-disabled:opacity-50 sm:text-sm/6",
20
+ data: error_data
21
+ ) %>
22
+ <% end %>
16
23
  <%= tag.button(
17
- class: "group relative isolate inline-flex h-6 w-10 cursor-default rounded-full p-[3px] sm:h-5 sm:w-8 transition duration-0 ease-in-out data-changing:duration-200 forced-colors:outline forced-colors:[--switch-bg:Highlight] ] bg-zinc-200 ring-1 ring-black/5 ring-inset data-checked:bg-(--switch-bg) data-checked:ring-(--switch-bg-ring) ) ) focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-blue-500 data-hover:ring-black/15 data-hover:data-checked:ring-(--switch-bg-ring) ) data-disabled:bg-zinc-200 data-disabled:opacity-50 data-disabled:data-checked:bg-zinc-200 data-disabled:data-checked:ring-black/5 [--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] ] [--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white]",
24
+ class: "group relative isolate inline-flex h-6 w-10 cursor-default rounded-full p-[3px] sm:h-5 sm:w-8 transition duration-0 ease-in-out data-changing:duration-200 forced-colors:outline forced-colors:[--switch-bg:Highlight] ] bg-zinc-200 ring-1 ring-black/5 ring-inset data-checked:bg-(--switch-bg) data-checked:ring-(--switch-bg-ring) ) ) focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-blue-500 hover:ring-black/15 hover:data-checked:ring-(--switch-bg-ring) ) data-disabled:bg-zinc-200 data-disabled:opacity-50 data-disabled:data-checked:bg-zinc-200 data-disabled:data-checked:ring-black/5 [--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] ] [--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white]",
18
25
  aria: { checked: @enabled },
19
26
  role: "switch",
20
27
  type: "button",
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class LightningUiKit::SwitchComponent < LightningUiKit::BaseComponent
4
- def initialize(name:, form: nil, label: nil, description: nil, enabled: false, disabled: false, **options)
4
+ include LightningUiKit::Errors
5
+
6
+ def initialize(name:, form: nil, label: nil, error: nil, description: nil, enabled: false, disabled: false, **options)
5
7
  @name = name
6
8
  @form = form
7
9
  @label = label
10
+ @error = error
8
11
  @description = description
9
12
  @enabled = enabled
10
13
  @disabled = disabled
@@ -28,4 +31,12 @@ class LightningUiKit::SwitchComponent < LightningUiKit::BaseComponent
28
31
  end
29
32
  end
30
33
  end
34
+
35
+ def error_data
36
+ {slot: "error"}.merge(@options[:error_data] || {}).tap do |data|
37
+ if @disabled
38
+ data[:disabled] = "true"
39
+ end
40
+ end
41
+ end
31
42
  end
@@ -2,12 +2,12 @@
2
2
  class: "[&>[data-slot=label]+[data-slot=control]]:mt-3 [&>[data-slot=label]+[data-slot=description]]:mt-1 [&>[data-slot=description]+[data-slot=control]]:mt-3 [&>[data-slot=control]+[data-slot=description]]:mt-3 [&>[data-slot=control]+[data-slot=error]]:mt-3 *:data-[slot=label]:font-medium",
3
3
  data:
4
4
  ) do %>
5
- <% if @label %>
6
- <%= tag.label(
7
- @label,
8
- class: "text-base/6 text-zinc-950 select-none data-disabled:opacity-50 sm:text-sm/6",
9
- data: label_data
10
- ) %>
5
+ <% if @label %>
6
+ <%= tag.label(
7
+ @label,
8
+ class: "text-base/6 text-zinc-950 select-none data-disabled:opacity-50 sm:text-sm/6",
9
+ data: label_data
10
+ ) %>
11
11
  <% end %>
12
12
  <% if @description %>
13
13
  <%= tag.p(
@@ -17,7 +17,7 @@
17
17
  ) %>
18
18
  <% end %>
19
19
  <% if @form %>
20
- <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
20
+ <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
21
21
  <%= @form.text_area(
22
22
  @name,
23
23
  value: @value,
@@ -25,13 +25,13 @@
25
25
  cols: @cols,
26
26
  multiple: @multiple,
27
27
  data: input_data,
28
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
28
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500 data-disabled:border-zinc-950/20",
29
29
  disabled: @disabled,
30
30
  autofocus: @autofocus
31
31
  ) %>
32
32
  </span>
33
33
  <% else %>
34
- <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
34
+ <span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
35
35
  <%= text_area_tag(
36
36
  @name,
37
37
  @value,
@@ -39,10 +39,17 @@
39
39
  cols: @cols,
40
40
  multiple: @multiple,
41
41
  data: input_data,
42
- class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
42
+ class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:hover:border-red-500 data-disabled:border-zinc-950/20",
43
43
  disabled: @disabled,
44
44
  autofocus: @autofocus
45
45
  ) %>
46
46
  </span>
47
47
  <% end %>
48
+ <% if has_errors? %>
49
+ <%= tag.p(
50
+ error_messages,
51
+ class: "text-base/6 text-red-600 data-disabled:opacity-50 sm:text-sm/6",
52
+ data: error_data
53
+ ) %>
54
+ <% end %>
48
55
  <% end %>
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class LightningUiKit::TextareaComponent < LightningUiKit::BaseComponent
4
- def initialize(name:, value: nil, autofocus: false, label: nil, form: nil, type: :text, description: nil, disabled: false, multiple: false, rows: 3, cols: nil, **options)
4
+ include LightningUiKit::Errors
5
+
6
+ def initialize(name:, value: nil, autofocus: false, label: nil, form: nil, type: :text, error: nil, description: nil, disabled: false, multiple: false, rows: 3, cols: nil, **options)
5
7
  @name = name
6
8
  @value = value
7
9
  @disabled = disabled
@@ -9,6 +11,7 @@ class LightningUiKit::TextareaComponent < LightningUiKit::BaseComponent
9
11
  @rows = rows
10
12
  @multiple = multiple
11
13
  @cols = cols
14
+ @error = error
12
15
  @label = label
13
16
  @form = form
14
17
  @type = type
@@ -21,7 +24,11 @@ class LightningUiKit::TextareaComponent < LightningUiKit::BaseComponent
21
24
  end
22
25
 
23
26
  def input_data
24
- @options[:input_data] || {}
27
+ (@options[:input_data] || {}).tap do |data|
28
+ if has_errors?
29
+ data[:invalid] = "true"
30
+ end
31
+ end
25
32
  end
26
33
 
27
34
  def label_data
@@ -39,4 +46,12 @@ class LightningUiKit::TextareaComponent < LightningUiKit::BaseComponent
39
46
  end
40
47
  end
41
48
  end
49
+
50
+ def error_data
51
+ {slot: "error"}.merge(@options[:error_data] || {}).tap do |data|
52
+ if @disabled
53
+ data[:disabled] = "true"
54
+ end
55
+ end
56
+ end
42
57
  end
@@ -2,9 +2,14 @@ import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class extends Controller {
4
4
  static targets = ["content", "item"];
5
+ static values = {
6
+ openFirst: { type: Boolean, default: true }
7
+ };
5
8
 
6
9
  connect() {
7
- this.openItem(this.itemTargets[0])
10
+ if (this.openFirstValue) {
11
+ this.openItem(this.itemTargets[0]);
12
+ }
8
13
  }
9
14
 
10
15
  toggle(e) {
@@ -24,27 +29,27 @@ export default class extends Controller {
24
29
  }
25
30
 
26
31
  openItem(item) {
27
- const content = item.querySelector("[data-accordion-target=content]");
32
+ const content = item.querySelector("[data-lui-accordion-target=content]");
28
33
  content.classList.remove("grid-rows-[0fr]");
29
34
  content.classList.add("grid-rows-[1fr]");
30
35
  content.classList.remove("opacity-0");
31
36
  content.classList.add("opacity-100");
32
- const arrow = item.querySelector("[data-accordion-target=arrow]");
37
+ const arrow = item.querySelector("[data-lui-accordion-target=arrow]");
33
38
  arrow.classList.add("rotate-180");
34
39
  }
35
40
 
36
41
  closeItem(item) {
37
- const content = item.querySelector("[data-accordion-target=content]");
42
+ const content = item.querySelector("[data-lui-accordion-target=content]");
38
43
  content.classList.remove("grid-rows-[1fr]");
39
44
  content.classList.add("grid-rows-[0fr]");
40
45
  content.classList.remove("opacity-100");
41
46
  content.classList.add("opacity-0");
42
- const arrow = item.querySelector("[data-accordion-target=arrow]");
47
+ const arrow = item.querySelector("[data-lui-accordion-target=arrow]");
43
48
  arrow.classList.remove("rotate-180");
44
49
  }
45
50
 
46
51
  isOpen(item) {
47
- const content = item.querySelector("[data-accordion-target=content]");
52
+ const content = item.querySelector("[data-lui-accordion-target=content]");
48
53
  return content.classList.contains("grid-rows-[1fr]");
49
54
  }
50
55
  }
@@ -0,0 +1,79 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { DirectUpload } from "@rails/activestorage"
3
+
4
+ class Upload {
5
+ constructor(file, controller) {
6
+ this.controller = controller
7
+ this.file = file;
8
+ this.directUpload = new DirectUpload(file, this.controller.inputTarget.dataset.directUploadUrl, this);
9
+ }
10
+
11
+ process() {
12
+ this.insertUpload();
13
+
14
+ this.directUpload.create((error, blob) => {
15
+ if (error) {
16
+ const fileContainer = this.controller.filesTarget.querySelector(`#upload_${this.directUpload.id}`)
17
+ const status = fileContainer.querySelector("[data-lui-dropzone-target='status']")
18
+ status.textContent = "Failed. " + error
19
+ status.classList.add("text-red-500")
20
+ } else {
21
+ const hiddenField = document.createElement('input')
22
+ hiddenField.setAttribute("type", "hidden");
23
+ hiddenField.setAttribute("value", blob.signed_id);
24
+ hiddenField.name = this.controller.inputTarget.name;
25
+ this.controller.filesTarget
26
+ .querySelector(`#upload_${this.directUpload.id}`)
27
+ .appendChild(hiddenField);
28
+ this.controller.inputTarget.value = "";
29
+ }
30
+ });
31
+ }
32
+
33
+ insertUpload() {
34
+ const template = this.controller.templateTarget.content.cloneNode(true)
35
+ template.querySelector('#\\#NEW_FILE').id = `upload_${this.directUpload.id}`
36
+ template.querySelector("[data-lui-dropzone-target='filename']").textContent = this.file.name
37
+ template.querySelector("[data-lui-dropzone-target='status']").textContent = "Uploading..."
38
+
39
+ this.controller.filesTarget.appendChild(template)
40
+ }
41
+
42
+ directUploadWillStoreFileWithXHR(request) {
43
+ request.upload.addEventListener("progress", (event) => this.updateProgress(event));
44
+ }
45
+
46
+ updateProgress(event) {
47
+ const progress = ((event.loaded / event.total) * 100).toFixed(0)
48
+ const status = progress == 100 ? "Uploaded" : "Uploading..."
49
+
50
+ const fileContainer = this.controller.filesTarget.querySelector(`#upload_${this.directUpload.id}`)
51
+ fileContainer.querySelector("[data-lui-dropzone-target='status']").textContent = status
52
+ fileContainer.querySelector("[data-lui-dropzone-target='progressbar']").style.width = `${progress}%`
53
+ fileContainer.querySelector("[data-lui-dropzone-target='percentage-progress']").textContent = `${progress}%`
54
+ }
55
+ }
56
+
57
+ export default class extends Controller {
58
+ static targets = ["input", "template", "files"]
59
+
60
+ selectFiles(_event) {
61
+ this.inputTarget.click()
62
+ }
63
+
64
+ uploadFiles(event) {
65
+ event.preventDefault();
66
+ const files = event.dataTransfer ? event.dataTransfer.files : event.target.files;
67
+ [...files].forEach(f => new Upload(f, this).process())
68
+ }
69
+
70
+ activate(event) {
71
+ event.preventDefault()
72
+ }
73
+
74
+ removeFile(event) {
75
+ event.preventDefault()
76
+ const container = event.target.closest("[data-lui-dropzone-target='file']")
77
+ container.remove()
78
+ }
79
+ }
@@ -2,6 +2,9 @@ const namespace = 'lui'
2
2
 
3
3
  import { Application } from "@hotwired/stimulus"
4
4
 
5
+ import * as ActiveStorage from "@rails/activestorage"
6
+ ActiveStorage.start()
7
+
5
8
  const application = Application.start()
6
9
  window.Stimulus = application
7
10
 
@@ -14,6 +17,7 @@ import ModalController from './controllers/modal_controller'
14
17
  import RevealController from './controllers/reveal_controller'
15
18
  import SwitchController from './controllers/switch_controller'
16
19
  import DropdownController from './controllers/dropdown_controller'
20
+ import DropzoneController from './controllers/dropzone_controller'
17
21
 
18
22
  export function registerLuiControllers(application) {
19
23
  application.register(`${namespace}-clipboard`, ClipboardController)
@@ -25,6 +29,7 @@ export function registerLuiControllers(application) {
25
29
  application.register(`${namespace}-reveal`, RevealController)
26
30
  application.register(`${namespace}-switch`, SwitchController)
27
31
  application.register(`${namespace}-dropdown`, DropdownController)
32
+ application.register(`${namespace}-dropzone`, DropzoneController)
28
33
  }
29
34
  registerLuiControllers(application)
30
35
 
@@ -0,0 +1,3 @@
1
+ en:
2
+ dropzone:
3
+ default_message: "Drag and Drop your files here"
@@ -1,5 +1,4 @@
1
1
  require "view_component"
2
- require "stimulus-rails"
3
2
 
4
3
  module LightningUiKit
5
4
  class Engine < ::Rails::Engine
@@ -1,3 +1,3 @@
1
1
  module LightningUiKit
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.4"
3
3
  end