fron-ui 1.0.0rc2

Sign up to get free protection for your applications and to get access to all the features.
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