fron-ui 1.0.0rc2

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 (140) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +38 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +7 -0
  7. data/.yardopts +8 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.lock +105 -0
  10. data/Rakefile +37 -0
  11. data/Readme.md +4 -0
  12. data/db.json +192 -0
  13. data/fron-ui.gemspec +21 -0
  14. data/lib/fron-ui.rb +1 -0
  15. data/lib/fron_ui.rb +5 -0
  16. data/lib/fron_ui/version.rb +7 -0
  17. data/opal/fron-ui/base.rb +49 -0
  18. data/opal/fron-ui/behaviors/action.rb +40 -0
  19. data/opal/fron-ui/behaviors/actions.rb +40 -0
  20. data/opal/fron-ui/behaviors/confirmation.rb +23 -0
  21. data/opal/fron-ui/behaviors/dropdown.rb +27 -0
  22. data/opal/fron-ui/behaviors/file.rb +48 -0
  23. data/opal/fron-ui/behaviors/intendable_children.rb +76 -0
  24. data/opal/fron-ui/behaviors/keydown.rb +31 -0
  25. data/opal/fron-ui/behaviors/loop.rb +41 -0
  26. data/opal/fron-ui/behaviors/render.rb +30 -0
  27. data/opal/fron-ui/behaviors/rest.rb +121 -0
  28. data/opal/fron-ui/behaviors/selectable_children.rb +67 -0
  29. data/opal/fron-ui/behaviors/serialize.rb +32 -0
  30. data/opal/fron-ui/behaviors/shortcuts.rb +35 -0
  31. data/opal/fron-ui/behaviors/state.rb +56 -0
  32. data/opal/fron-ui/behaviors/transition.rb +63 -0
  33. data/opal/fron-ui/components/action.rb +18 -0
  34. data/opal/fron-ui/components/box.rb +17 -0
  35. data/opal/fron-ui/components/button.rb +61 -0
  36. data/opal/fron-ui/components/calendar.rb +129 -0
  37. data/opal/fron-ui/components/checkbox.rb +57 -0
  38. data/opal/fron-ui/components/chooser.rb +246 -0
  39. data/opal/fron-ui/components/color_panel.rb +235 -0
  40. data/opal/fron-ui/components/color_picker.rb +111 -0
  41. data/opal/fron-ui/components/container.rb +61 -0
  42. data/opal/fron-ui/components/date_picker.rb +141 -0
  43. data/opal/fron-ui/components/drag.rb +76 -0
  44. data/opal/fron-ui/components/dropdown.rb +72 -0
  45. data/opal/fron-ui/components/icon.rb +29 -0
  46. data/opal/fron-ui/components/image.rb +77 -0
  47. data/opal/fron-ui/components/input.rb +30 -0
  48. data/opal/fron-ui/components/label.rb +9 -0
  49. data/opal/fron-ui/components/list.rb +34 -0
  50. data/opal/fron-ui/components/loader.rb +63 -0
  51. data/opal/fron-ui/components/modal.rb +0 -0
  52. data/opal/fron-ui/components/notifications.rb +73 -0
  53. data/opal/fron-ui/components/number.rb +202 -0
  54. data/opal/fron-ui/components/progress.rb +52 -0
  55. data/opal/fron-ui/components/slider.rb +47 -0
  56. data/opal/fron-ui/components/tabs.rb +149 -0
  57. data/opal/fron-ui/components/textarea.rb +13 -0
  58. data/opal/fron-ui/components/time.rb +65 -0
  59. data/opal/fron-ui/components/title.rb +34 -0
  60. data/opal/fron-ui/examples/blog/index.rb +289 -0
  61. data/opal/fron-ui/examples/comments/components/comment.rb +75 -0
  62. data/opal/fron-ui/examples/comments/components/comments.rb +93 -0
  63. data/opal/fron-ui/examples/comments/components/footer.rb +36 -0
  64. data/opal/fron-ui/examples/comments/components/header.rb +35 -0
  65. data/opal/fron-ui/examples/comments/components/list.rb +12 -0
  66. data/opal/fron-ui/examples/comments/index.rb +6 -0
  67. data/opal/fron-ui/examples/contacts/components/contacts.rb +100 -0
  68. data/opal/fron-ui/examples/contacts/components/details.rb +92 -0
  69. data/opal/fron-ui/examples/contacts/components/item.rb +46 -0
  70. data/opal/fron-ui/examples/contacts/components/list.rb +10 -0
  71. data/opal/fron-ui/examples/contacts/components/sidebar.rb +30 -0
  72. data/opal/fron-ui/examples/contacts/index.rb +6 -0
  73. data/opal/fron-ui/examples/editor/index.rb +164 -0
  74. data/opal/fron-ui/examples/kitchensink/index.rb +193 -0
  75. data/opal/fron-ui/examples/todos/components/item.rb +84 -0
  76. data/opal/fron-ui/examples/todos/components/options.rb +26 -0
  77. data/opal/fron-ui/examples/todos/components/todos.rb +145 -0
  78. data/opal/fron-ui/examples/todos/index.rb +6 -0
  79. data/opal/fron-ui/examples/webshop/index.rb +0 -0
  80. data/opal/fron-ui/fonts/ionicons.rb +2954 -0
  81. data/opal/fron-ui/fonts/open_sans.rb +19 -0
  82. data/opal/fron-ui/lib/collection.rb +138 -0
  83. data/opal/fron-ui/lib/date.rb +23 -0
  84. data/opal/fron-ui/lib/debounce.rb +14 -0
  85. data/opal/fron-ui/lib/image_loader.rb +13 -0
  86. data/opal/fron-ui/lib/lorem.rb +93 -0
  87. data/opal/fron-ui/lib/nil.rb +29 -0
  88. data/opal/fron-ui/lib/record.rb +23 -0
  89. data/opal/fron-ui/lib/state_serializer.rb +129 -0
  90. data/opal/fron-ui/lib/storage.rb +57 -0
  91. data/opal/fron-ui/spec/setup.rb +40 -0
  92. data/opal/fron-ui/ui.rb +40 -0
  93. data/opal/fron-ui/utils/theme_roller.rb +63 -0
  94. data/opal/fron-ui/vendor/autoprefixer.js +21114 -0
  95. data/opal/fron-ui/vendor/marked.js +1291 -0
  96. data/opal/fron-ui/vendor/md5.js +274 -0
  97. data/opal/fron-ui/vendor/moment.js +3083 -0
  98. data/opal/fron-ui/vendor/uuid.js +92 -0
  99. data/opal/fron_ui.rb +13 -0
  100. data/spec/behaviors/action_spec.rb +34 -0
  101. data/spec/behaviors/actions_spec.rb +38 -0
  102. data/spec/behaviors/confirmation_spec.rb +23 -0
  103. data/spec/behaviors/dropdown_spec.rb +32 -0
  104. data/spec/behaviors/render_spec.rb +20 -0
  105. data/spec/behaviors/rest_spec.rb +70 -0
  106. data/spec/behaviors/selectable_children_spec.rb +40 -0
  107. data/spec/behaviors/serialize_spec.rb +34 -0
  108. data/spec/components/action_spec.rb +7 -0
  109. data/spec/components/base_spec.rb +19 -0
  110. data/spec/components/box_spec.rb +7 -0
  111. data/spec/components/button_spec.rb +9 -0
  112. data/spec/components/calendar_spec.rb +58 -0
  113. data/spec/components/checkbox_spec.rb +20 -0
  114. data/spec/components/chooser_spec.rb +75 -0
  115. data/spec/components/color_panel_spec.rb +49 -0
  116. data/spec/components/color_picker_spec.rb +41 -0
  117. data/spec/components/container_spec.rb +23 -0
  118. data/spec/components/date_picker_spec.rb +71 -0
  119. data/spec/components/drag_spec.rb +20 -0
  120. data/spec/components/dropdown_spec.rb +33 -0
  121. data/spec/components/image_spec.rb +33 -0
  122. data/spec/components/input_spec.rb +8 -0
  123. data/spec/components/list_spec.rb +10 -0
  124. data/spec/components/loader_spec.rb +9 -0
  125. data/spec/components/notifications_spec.rb +17 -0
  126. data/spec/components/number_spec.rb +64 -0
  127. data/spec/components/progress_spec.rb +23 -0
  128. data/spec/components/slider_spec.rb +25 -0
  129. data/spec/components/tabs_spec.rb +50 -0
  130. data/spec/components/textarea_spec.rb +7 -0
  131. data/spec/components/time_spec.rb +37 -0
  132. data/spec/components/title_spec.rb +11 -0
  133. data/spec/examples/comments_spec.rb +72 -0
  134. data/spec/examples/todos_spec.rb +39 -0
  135. data/spec/lib/collection_spec.rb +38 -0
  136. data/spec/lib/lorem_spec.rb +55 -0
  137. data/spec/lib/state_serializer_spec.rb +58 -0
  138. data/spec/lib/storage_spec.rb +39 -0
  139. data/spec/spec_helper.rb +1 -0
  140. metadata +223 -0
@@ -0,0 +1,57 @@
1
+ module UI
2
+ # Checkbox component.
3
+ #
4
+ # Features:
5
+ # * checked and checked=
6
+ # * actionable
7
+ #
8
+ # @attr value [Boolean] The value
9
+ #
10
+ # @author Gusztáv Szikszai
11
+ # @since 0.1.0
12
+ class Checkbox < Action
13
+ extend Forwardable
14
+
15
+ tag 'ui-checkbox'
16
+
17
+ def_delegators :@input, :checked, :checked=
18
+
19
+ component :input, :input, tabindex: -1, type: :checkbox
20
+ component :label, :label, tabindex: -1 do
21
+ component :icon, UI::Icon, glyph: :checkmark
22
+ end
23
+
24
+ style height: -> { theme.size.em },
25
+ width: -> { theme.size.em },
26
+ display: 'inline-block',
27
+ input: { display: :none,
28
+ '&:checked + label' => { 'ui-icon' => { transform: 'scale(1)',
29
+ opacity: 1 } } },
30
+ label: { transition: 'opacity 320ms, transform 320ms',
31
+ borderRadius: -> { theme.border_radius.em },
32
+ color: -> { readable_color colors.input },
33
+ background: -> { colors.input },
34
+ justifyContent: :center,
35
+ alignItems: :center,
36
+ cursor: :pointer,
37
+ height: :inherit,
38
+ display: :flex,
39
+ width: :inherit,
40
+ 'ui-icon' => { transform: 'scale(0.4) rotate(45deg)',
41
+ transition: :inherit,
42
+ fontSize: 1.em,
43
+ opacity: 0 } },
44
+ '&:focus label' => { boxShadow: -> { theme.focus_box_shadow.call } }
45
+
46
+ on :click, :action
47
+
48
+ # Toggles the checkbox
49
+ def action
50
+ self.checked = !checked
51
+ trigger :change
52
+ end
53
+
54
+ alias value= checked=
55
+ alias value checked
56
+ end
57
+ end
@@ -0,0 +1,246 @@
1
+ require 'fron-ui/components/dropdown'
2
+ require 'fron-ui/components/input'
3
+
4
+ module UI
5
+ # Chooser component
6
+ #
7
+ # Features
8
+ # * Select an item from the dropdown
9
+ # * Allows multiple selection (multiple attribute)
10
+ # * Search the items from the input
11
+ # * Navigate / select with keyboard
12
+ #
13
+ # @author Gusztáv Szikszai
14
+ # @since 0.1.0
15
+ class Chooser < Base
16
+ # Item for the chooser
17
+ class Item < Fron::Component
18
+ include ::Record
19
+
20
+ tag 'ui-chooser-item'
21
+
22
+ style display: :block,
23
+ padding: -> { (theme.spacing / 2).em }
24
+
25
+ # Renders the item
26
+ def render
27
+ self.text = label
28
+ end
29
+
30
+ def label
31
+ @data[:value]
32
+ end
33
+
34
+ # Retunrs the value
35
+ #
36
+ # @return [String] The value
37
+ def value
38
+ text
39
+ end
40
+ end
41
+
42
+ # List for the chooser
43
+ class List < Collection
44
+ include UI::Behaviors::SelectableChildren
45
+ include UI::Behaviors::IntendableChildren
46
+
47
+ tag 'ui-chooser-list'
48
+
49
+ style background: -> { colors.input },
50
+ borderRadius: -> { theme.border_radius.em },
51
+ color: -> { readable_color colors.input },
52
+ overflow: :auto,
53
+ '> *.selected' => { background: -> { colors.primary },
54
+ color: -> { readable_color colors.primary },
55
+ '&:first-child' => { borderTopLeftRadius: :inherit,
56
+ borderTopRightRadius: :inherit },
57
+ '&:last-child' => { borderBottomLeftRadius: :inherit,
58
+ borderBottomRightRadius: :inherit } }
59
+
60
+ # Selects the currently intended item
61
+ def select_intended
62
+ select intended
63
+ end
64
+ end
65
+
66
+ include UI::Behaviors::Dropdown
67
+ include UI::Behaviors::Keydown
68
+ extend Forwardable
69
+
70
+ tag 'ui-chooser'
71
+
72
+ component :input, UI::Input
73
+ component :dropdown, UI::Dropdown do
74
+ component :list, List, base: Item, deselectable: true
75
+ end
76
+
77
+ dropdown :input, :dropdown
78
+
79
+ def_delegators :dropdown, :list
80
+ def_delegators :input, :placeholder, :placeholder=, :blur, :active?, :focus
81
+ def_delegators :list, :base, :base=, :key, :key=, :multiple, :multiple=,
82
+ :intend_next, :intend_previous, :select_intended, :items,
83
+ :select_first, :select_last, :deselectable, :deselectable=
84
+
85
+ style position: :relative,
86
+ color: -> { readable_color colors.input },
87
+ input: { cursor: :pointer,
88
+ width: '100%' },
89
+ 'ui-dropdown' => { left: 0,
90
+ right: 0,
91
+ maxHeight: 300.px,
92
+ overflowY: :auto },
93
+ '&:not([focused]):after,
94
+ &:not([searchable]):after' => { background: -> { "linear-gradient(90deg, #{rgba(colors.input, 0)}, #{colors.input} 70%)" },
95
+ borderBottomRightRadius: :inherit,
96
+ borderTopRightRadius: :inherit,
97
+ pointerEvents: :none,
98
+ position: :absolute,
99
+ content: '""',
100
+ width: 4.em,
101
+ bottom: 0.15.em,
102
+ right: 0.15.em,
103
+ top: 0.15.em },
104
+ '&:before' => { borderStyle: :solid,
105
+ position: :absolute,
106
+ marginTop: -0.2.em,
107
+ content: "''",
108
+ opacity: 0.5,
109
+ top: '50%',
110
+ height: 0,
111
+ pointerEvents: :none,
112
+ width: 0,
113
+ borderColor: 'currentColor transparent transparent transparent',
114
+ borderWidth: '0.4em 0.35em 0 0.35em',
115
+ right: 0.75.em,
116
+ zIndex: 1 }
117
+
118
+ keydown :esc, :blur
119
+ keydown :up, :intend_previous
120
+ keydown :down, :intend_next
121
+
122
+ on :input, :filter
123
+ on :selected_change, :selected_changed
124
+ on :keydown, :keydown
125
+
126
+ # Initilaizes the component, setting
127
+ # up not bubbling events on the input
128
+ def initialize
129
+ super
130
+ @dropdown.on :mousedown, &:prevent_default
131
+
132
+ self.searchable = true
133
+ @input.on(:focus) do
134
+ self[:focused] = ''
135
+ show_items
136
+ empty_input
137
+ end
138
+ @input.on(:blur) do
139
+ remove_attribute :focused
140
+ update_input
141
+ end
142
+ end
143
+
144
+ def keydown(event)
145
+ return unless [:space, :enter].include?(event.key)
146
+ result = select_intended
147
+ event.stop if result
148
+ end
149
+
150
+ # Sets the searchable value, by setting
151
+ # the inputs to read only attribute.
152
+ #
153
+ # @param value [Boolean] The value
154
+ def searchable=(value)
155
+ @input.readonly = !value
156
+ if @input.readonly
157
+ remove_attribute :searchable
158
+ else
159
+ self[:searchable] = ''
160
+ end
161
+ end
162
+
163
+ # Returns if the component is searchable or not
164
+ #
165
+ # @return [Boolean] The value
166
+ def searchable
167
+ !@input.readonly
168
+ end
169
+
170
+ # Sets the items
171
+ #
172
+ # @param items [Array<Hash>] The items
173
+ def items=(items)
174
+ items = items.each_with_index.map { |item, index| { id: index, value: item } } unless items[0].is_a?(Hash)
175
+ list.items = items
176
+ update_input
177
+ end
178
+
179
+ # Empties the input
180
+ def empty_input
181
+ return if @input.readonly
182
+ @input.value = ''
183
+ end
184
+
185
+ # Updates the input after
186
+ # an item has been selected
187
+ def selected_changed
188
+ return if searchable
189
+ update_input
190
+ trigger :change
191
+ @input.blur unless multiple
192
+ end
193
+
194
+ # Updates the input text with
195
+ # the selected items values
196
+ def update_input
197
+ timeout(320) { show_items }
198
+ @input.value = Array(label).join(', ')
199
+ end
200
+
201
+ # Shows the items
202
+ def show_items
203
+ list.children.each { |item| item.remove_class :hidden }
204
+ end
205
+
206
+ # Filters the items by the input label
207
+ def filter
208
+ list.children.each do |item|
209
+ item.toggle_class :hidden, !(item.label =~ Regexp.new(@input.value, 'i'))
210
+ end
211
+ end
212
+
213
+ # Returns the value
214
+ #
215
+ # @return The value
216
+ def value
217
+ selected_items(:value)
218
+ end
219
+
220
+ def selected_items(property)
221
+ selected = list.selected
222
+ return nil unless selected
223
+ if selected.is_a?(Array)
224
+ return nil if selected.empty?
225
+ selected.map { |item| item.send(property) }
226
+ else
227
+ selected.send(property)
228
+ end
229
+ end
230
+
231
+ def label
232
+ selected_items(:label)
233
+ end
234
+
235
+ def value=(new_value)
236
+ return if attribute?(:focused)
237
+ values = Array(new_value)
238
+ return if Array(value) == values
239
+ list.deselect
240
+ values.each do |val|
241
+ list.select list.children.find { |child| child.value == val }
242
+ end
243
+ update_input
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,235 @@
1
+ require 'fron-ui/components/container'
2
+ require 'fron-ui/components/drag'
3
+
4
+ require 'native'
5
+
6
+ module Color
7
+ class RGB
8
+ def css_rgba(alpha)
9
+ alpha = format('%.1f', alpha)
10
+ "rgba(#{red.round},#{green.round},#{blue.round},#{alpha})"
11
+ end
12
+ end
13
+ end
14
+
15
+ module UI
16
+ # Color selector component with a draggable field for
17
+ # selecting the saturation / value and
18
+ # a slider for selecting hue.
19
+ #
20
+ # @attr_reader [Integer] hue The hue of the represented color
21
+ # @attr_reader [Integer] value The value of the represented color
22
+ # @attr_reader [Integer] saturation The saturation of the represented color
23
+ #
24
+ # @author Gusztáv Szikszai
25
+ # @since 0.1.0
26
+ class ColorPanel < UI::Container
27
+ extend Forwardable
28
+
29
+ CHECKER = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wkUCDAEK+8FsQAAADNJREFUKM9jPHPmDAM2YGxsjFWciYFEMKqBGMD4//9/rBJnz54d8p7G5TecGhgZGQfIDwBAQAuAAa2FpwAAAABJRU5ErkJggg=='
30
+
31
+ tag 'ui-color-wheel'
32
+
33
+ defaults direction: :column
34
+
35
+ style borderRadius: -> { theme.border_radius.em },
36
+ padding: -> { (theme.spacing * 2).em },
37
+ background: -> { colors.input },
38
+ 'ui-drag' => { boxShadow: '0 0 1px 1px rgba(0,0,0,0.2) inset',
39
+ cursor: :pointer },
40
+ '> ui-drag' => {
41
+ backgroundPosition: :center,
42
+ height: 1.2.em,
43
+ 'ui-drag-handle' => { transform: 'translateX(-50%)',
44
+ borderRadius: 0.5.em,
45
+ top: -0.25.em,
46
+ bottom: -0.25.em,
47
+ width: 0.5.em,
48
+ height: 'auto' }
49
+ },
50
+ 'ui-container' => {
51
+ 'ui-drag:first-of-type' => { backgroundImage: 'linear-gradient(0deg, #000 0%, transparent 100%),
52
+ linear-gradient(90deg, #fff 0%, rgba(255,255,255,0) 100%)',
53
+ width: 14.em,
54
+ height: 14.em },
55
+ 'ui-drag:last-of-type' => { backgroundImage: 'linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%,
56
+ #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)',
57
+ width: 1.2.em,
58
+ 'ui-drag-handle' => { transform: 'translateY(-50%)',
59
+ borderRadius: 0.5.em,
60
+ right: -0.25.em,
61
+ left: -0.25.em,
62
+ height: 0.5.em,
63
+ width: 'auto' } }
64
+ }
65
+
66
+ component :container, UI::Container, direction: :row do
67
+ component :recta, UI::Drag
68
+ component :hued, UI::Drag, horizontal: false
69
+ end
70
+ component :alphad, UI::Drag, vertical: false
71
+
72
+ on :change, 'ui-drag', :change
73
+ on :mousedown, :stop
74
+
75
+ def_delegators :container, :recta, :hued
76
+
77
+ attr_reader :hue, :saturation, :value
78
+
79
+ # Initializes the component with default
80
+ # values for hue, value and saturation
81
+ def initialize
82
+ super
83
+
84
+ @hue = 0
85
+ @value = 100
86
+ @saturation = 0
87
+ @alpha = 0
88
+
89
+ render
90
+ end
91
+
92
+ def stop(event)
93
+ event.stop
94
+ end
95
+
96
+ # Handles changes from the fields, sets the
97
+ # hue, value and saturation from on the fields.
98
+ #
99
+ # @param event [DOM::Event] The event
100
+ def change(event)
101
+ event.stop
102
+ hue = hued.value_y * 360
103
+ value = (1 - recta.value_y) * 100
104
+ saturation = recta.value_x * 100
105
+ alpha = @alphad.value_x * 100
106
+ return if hue == @hue &&
107
+ value == @value &&
108
+ saturation == @saturation &&
109
+ alpha == @alpha
110
+ @hue = hue
111
+ @value = value
112
+ @saturation = saturation
113
+ @alpha = alpha
114
+ render
115
+ trigger :change
116
+ end
117
+
118
+ # Sets the hue to the given value
119
+ #
120
+ # @param value [Integer] Integer between 0 and 360
121
+ def hue=(value)
122
+ @hue = value
123
+ hued.value_y = value / 360
124
+ render
125
+ end
126
+
127
+ # Sets the saturation to the given value
128
+ #
129
+ # @param value [Integer] Integer between 0 and 100
130
+ def saturation=(value)
131
+ @saturation = value
132
+ recta.value_x = value / 100
133
+ render
134
+ end
135
+
136
+ # Sets the value to the given value
137
+ #
138
+ # @param value [Integer] Integer between 0 and 100
139
+ def value=(value)
140
+ @value = value
141
+ recta.value_y = (100 - value) / 100
142
+ render
143
+ end
144
+
145
+ def alpha=(value)
146
+ @alpha = value
147
+ @alphad.value_x = value / 100
148
+ render
149
+ end
150
+
151
+ def to_css
152
+ if @alpha.round == 100
153
+ '#' + color.hex.upcase
154
+ else
155
+ color.css_rgba(@alpha / 100)
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ # Returns the color
162
+ #
163
+ # :reek:UncommunicativeVariableName
164
+ #
165
+ # @return [Color::RGB] The color
166
+ def color
167
+ s = saturation / 100
168
+ v = value / 100
169
+ h = hue
170
+
171
+ c = v * s
172
+ x = c * (1 - ((h / 60).modulo(2) - 1).abs)
173
+ m = v - c
174
+
175
+ r, g, b = if (0...60).cover?(h)
176
+ [c, x, 0]
177
+ elsif (60...120).cover?(h)
178
+ [x, c, 0]
179
+ elsif (120...180).cover?(h)
180
+ [0, c, x]
181
+ elsif (180...240).cover?(h)
182
+ [0, x, c]
183
+ elsif (240...300).cover?(h)
184
+ [x, 0, c]
185
+ elsif (300..360).cover?(h)
186
+ [c, 0, x]
187
+ end
188
+
189
+ Color::RGB.from_fraction(r + m, g + m, b + m)
190
+ end
191
+
192
+ # Sets the color from a string
193
+ #
194
+ # :reek:UncommunicativeVariableName
195
+ #
196
+ # @param color [String] The hex or CSS representation
197
+ def color=(color)
198
+ if (md = color.match(/rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(\d+\.\d+)\)/))
199
+ color = Color::RGB.new(md[1].to_i, md[2].to_i, md[3].to_i)
200
+ alpha = md[4].to_f * 100
201
+ else
202
+ alpha = 100
203
+ end
204
+ color = Color::RGB.by_css(color) unless color.is_a? Color
205
+ rgb = color.to_rgb.to_a
206
+ r, g, b = rgb
207
+
208
+ cmax = rgb.max
209
+ cmin = rgb.min
210
+
211
+ delta = cmax - cmin
212
+
213
+ self.hue = if delta == 0
214
+ 0
215
+ elsif cmax == r
216
+ 60 * ((g - b) / delta).modulo(6)
217
+ elsif cmax == g
218
+ 60 * (((b - r) / delta) + 2)
219
+ elsif cmax == b
220
+ 60 * (((r - g) / delta) + 4)
221
+ end
222
+
223
+ self.saturation = (cmax == 0 ? 0 : delta / cmax) * 100
224
+ self.value = cmax * 100
225
+ self.alpha = alpha
226
+ end
227
+
228
+ # Renders the component
229
+ def render
230
+ rgb = "#{color.red.round}, #{color.green.round}, #{color.blue.round}"
231
+ alphad.style.background = "linear-gradient(90deg, rgba(#{rgb},0), rgba(#{rgb},1)), url(#{CHECKER})"
232
+ recta.style.backgroundColor = "hsl(#{hue}, 100%, 50%)"
233
+ end
234
+ end
235
+ end