fron-ui 1.0.0rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +38 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/.yardopts +8 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +105 -0
- data/Rakefile +37 -0
- data/Readme.md +4 -0
- data/db.json +192 -0
- data/fron-ui.gemspec +21 -0
- data/lib/fron-ui.rb +1 -0
- data/lib/fron_ui.rb +5 -0
- data/lib/fron_ui/version.rb +7 -0
- data/opal/fron-ui/base.rb +49 -0
- data/opal/fron-ui/behaviors/action.rb +40 -0
- data/opal/fron-ui/behaviors/actions.rb +40 -0
- data/opal/fron-ui/behaviors/confirmation.rb +23 -0
- data/opal/fron-ui/behaviors/dropdown.rb +27 -0
- data/opal/fron-ui/behaviors/file.rb +48 -0
- data/opal/fron-ui/behaviors/intendable_children.rb +76 -0
- data/opal/fron-ui/behaviors/keydown.rb +31 -0
- data/opal/fron-ui/behaviors/loop.rb +41 -0
- data/opal/fron-ui/behaviors/render.rb +30 -0
- data/opal/fron-ui/behaviors/rest.rb +121 -0
- data/opal/fron-ui/behaviors/selectable_children.rb +67 -0
- data/opal/fron-ui/behaviors/serialize.rb +32 -0
- data/opal/fron-ui/behaviors/shortcuts.rb +35 -0
- data/opal/fron-ui/behaviors/state.rb +56 -0
- data/opal/fron-ui/behaviors/transition.rb +63 -0
- data/opal/fron-ui/components/action.rb +18 -0
- data/opal/fron-ui/components/box.rb +17 -0
- data/opal/fron-ui/components/button.rb +61 -0
- data/opal/fron-ui/components/calendar.rb +129 -0
- data/opal/fron-ui/components/checkbox.rb +57 -0
- data/opal/fron-ui/components/chooser.rb +246 -0
- data/opal/fron-ui/components/color_panel.rb +235 -0
- data/opal/fron-ui/components/color_picker.rb +111 -0
- data/opal/fron-ui/components/container.rb +61 -0
- data/opal/fron-ui/components/date_picker.rb +141 -0
- data/opal/fron-ui/components/drag.rb +76 -0
- data/opal/fron-ui/components/dropdown.rb +72 -0
- data/opal/fron-ui/components/icon.rb +29 -0
- data/opal/fron-ui/components/image.rb +77 -0
- data/opal/fron-ui/components/input.rb +30 -0
- data/opal/fron-ui/components/label.rb +9 -0
- data/opal/fron-ui/components/list.rb +34 -0
- data/opal/fron-ui/components/loader.rb +63 -0
- data/opal/fron-ui/components/modal.rb +0 -0
- data/opal/fron-ui/components/notifications.rb +73 -0
- data/opal/fron-ui/components/number.rb +202 -0
- data/opal/fron-ui/components/progress.rb +52 -0
- data/opal/fron-ui/components/slider.rb +47 -0
- data/opal/fron-ui/components/tabs.rb +149 -0
- data/opal/fron-ui/components/textarea.rb +13 -0
- data/opal/fron-ui/components/time.rb +65 -0
- data/opal/fron-ui/components/title.rb +34 -0
- data/opal/fron-ui/examples/blog/index.rb +289 -0
- data/opal/fron-ui/examples/comments/components/comment.rb +75 -0
- data/opal/fron-ui/examples/comments/components/comments.rb +93 -0
- data/opal/fron-ui/examples/comments/components/footer.rb +36 -0
- data/opal/fron-ui/examples/comments/components/header.rb +35 -0
- data/opal/fron-ui/examples/comments/components/list.rb +12 -0
- data/opal/fron-ui/examples/comments/index.rb +6 -0
- data/opal/fron-ui/examples/contacts/components/contacts.rb +100 -0
- data/opal/fron-ui/examples/contacts/components/details.rb +92 -0
- data/opal/fron-ui/examples/contacts/components/item.rb +46 -0
- data/opal/fron-ui/examples/contacts/components/list.rb +10 -0
- data/opal/fron-ui/examples/contacts/components/sidebar.rb +30 -0
- data/opal/fron-ui/examples/contacts/index.rb +6 -0
- data/opal/fron-ui/examples/editor/index.rb +164 -0
- data/opal/fron-ui/examples/kitchensink/index.rb +193 -0
- data/opal/fron-ui/examples/todos/components/item.rb +84 -0
- data/opal/fron-ui/examples/todos/components/options.rb +26 -0
- data/opal/fron-ui/examples/todos/components/todos.rb +145 -0
- data/opal/fron-ui/examples/todos/index.rb +6 -0
- data/opal/fron-ui/examples/webshop/index.rb +0 -0
- data/opal/fron-ui/fonts/ionicons.rb +2954 -0
- data/opal/fron-ui/fonts/open_sans.rb +19 -0
- data/opal/fron-ui/lib/collection.rb +138 -0
- data/opal/fron-ui/lib/date.rb +23 -0
- data/opal/fron-ui/lib/debounce.rb +14 -0
- data/opal/fron-ui/lib/image_loader.rb +13 -0
- data/opal/fron-ui/lib/lorem.rb +93 -0
- data/opal/fron-ui/lib/nil.rb +29 -0
- data/opal/fron-ui/lib/record.rb +23 -0
- data/opal/fron-ui/lib/state_serializer.rb +129 -0
- data/opal/fron-ui/lib/storage.rb +57 -0
- data/opal/fron-ui/spec/setup.rb +40 -0
- data/opal/fron-ui/ui.rb +40 -0
- data/opal/fron-ui/utils/theme_roller.rb +63 -0
- data/opal/fron-ui/vendor/autoprefixer.js +21114 -0
- data/opal/fron-ui/vendor/marked.js +1291 -0
- data/opal/fron-ui/vendor/md5.js +274 -0
- data/opal/fron-ui/vendor/moment.js +3083 -0
- data/opal/fron-ui/vendor/uuid.js +92 -0
- data/opal/fron_ui.rb +13 -0
- data/spec/behaviors/action_spec.rb +34 -0
- data/spec/behaviors/actions_spec.rb +38 -0
- data/spec/behaviors/confirmation_spec.rb +23 -0
- data/spec/behaviors/dropdown_spec.rb +32 -0
- data/spec/behaviors/render_spec.rb +20 -0
- data/spec/behaviors/rest_spec.rb +70 -0
- data/spec/behaviors/selectable_children_spec.rb +40 -0
- data/spec/behaviors/serialize_spec.rb +34 -0
- data/spec/components/action_spec.rb +7 -0
- data/spec/components/base_spec.rb +19 -0
- data/spec/components/box_spec.rb +7 -0
- data/spec/components/button_spec.rb +9 -0
- data/spec/components/calendar_spec.rb +58 -0
- data/spec/components/checkbox_spec.rb +20 -0
- data/spec/components/chooser_spec.rb +75 -0
- data/spec/components/color_panel_spec.rb +49 -0
- data/spec/components/color_picker_spec.rb +41 -0
- data/spec/components/container_spec.rb +23 -0
- data/spec/components/date_picker_spec.rb +71 -0
- data/spec/components/drag_spec.rb +20 -0
- data/spec/components/dropdown_spec.rb +33 -0
- data/spec/components/image_spec.rb +33 -0
- data/spec/components/input_spec.rb +8 -0
- data/spec/components/list_spec.rb +10 -0
- data/spec/components/loader_spec.rb +9 -0
- data/spec/components/notifications_spec.rb +17 -0
- data/spec/components/number_spec.rb +64 -0
- data/spec/components/progress_spec.rb +23 -0
- data/spec/components/slider_spec.rb +25 -0
- data/spec/components/tabs_spec.rb +50 -0
- data/spec/components/textarea_spec.rb +7 -0
- data/spec/components/time_spec.rb +37 -0
- data/spec/components/title_spec.rb +11 -0
- data/spec/examples/comments_spec.rb +72 -0
- data/spec/examples/todos_spec.rb +39 -0
- data/spec/lib/collection_spec.rb +38 -0
- data/spec/lib/lorem_spec.rb +55 -0
- data/spec/lib/state_serializer_spec.rb +58 -0
- data/spec/lib/storage_spec.rb +39 -0
- data/spec/spec_helper.rb +1 -0
- metadata +223 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'fron-ui/components/input'
|
2
|
+
require 'fron-ui/components/dropdown'
|
3
|
+
require 'fron-ui/components/color_panel'
|
4
|
+
|
5
|
+
module UI
|
6
|
+
# Component for selecting colors by a color picker
|
7
|
+
# or just typing.
|
8
|
+
#
|
9
|
+
# Features:
|
10
|
+
# * Typing a CSS color (hex or named) and
|
11
|
+
# pressing enter will update the color panel.
|
12
|
+
# * Has a rectange for showing the selected color.
|
13
|
+
#
|
14
|
+
# @author Gusztáv Szikszai
|
15
|
+
# @since 0.1.0
|
16
|
+
class ColorPicker < Base
|
17
|
+
include UI::Behaviors::Dropdown
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
tag 'ui-color-picker'
|
22
|
+
|
23
|
+
component :input, UI::Input, spellcheck: false
|
24
|
+
component :div, 'div'
|
25
|
+
|
26
|
+
component :dropdown, UI::Dropdown do
|
27
|
+
component :color_panel, UI::ColorPanel
|
28
|
+
end
|
29
|
+
|
30
|
+
style display: 'inline-block',
|
31
|
+
position: :relative,
|
32
|
+
div: { boxShadow: '0 0 1px 1px rgba(0,0,0,0.2) inset',
|
33
|
+
borderRadius: -> { theme.border_radius.em },
|
34
|
+
pointerEvents: :none,
|
35
|
+
position: :absolute,
|
36
|
+
bottom: 0.3.em,
|
37
|
+
right: 0.3.em,
|
38
|
+
top: 0.3.em,
|
39
|
+
width: 2.em },
|
40
|
+
input: { paddingRight: -> { (theme.size * 1.25).em },
|
41
|
+
boxSizing: 'border-box',
|
42
|
+
width: '100%' }
|
43
|
+
|
44
|
+
on :change, :input, :update_dropdown
|
45
|
+
on :change, 'ui-color-wheel', :update_input
|
46
|
+
on :change, 'ui-color-wheel, input', :delegate_change
|
47
|
+
|
48
|
+
def_delegators :input, :value
|
49
|
+
def_delegators :dropdown, :color_panel
|
50
|
+
|
51
|
+
dropdown :input, :dropdown
|
52
|
+
|
53
|
+
# Delegates the change event
|
54
|
+
# so the target would be the picker
|
55
|
+
# not the color panel
|
56
|
+
#
|
57
|
+
# @param event [DOM::Event] The event
|
58
|
+
def delegate_change(event)
|
59
|
+
event.stop
|
60
|
+
trigger :change
|
61
|
+
end
|
62
|
+
|
63
|
+
# Initializes the component:
|
64
|
+
#
|
65
|
+
# * When the input gets the focus
|
66
|
+
# update the dropdown.
|
67
|
+
# * Set default value to **white**
|
68
|
+
def initialize
|
69
|
+
super
|
70
|
+
@input.on(:focus) { update_dropdown }
|
71
|
+
@input.value = '#FFFFFF'
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sets the value of the component
|
75
|
+
#
|
76
|
+
# @param value [String] The CSS color (hex or named)
|
77
|
+
def value=(value)
|
78
|
+
return if @input.value == value
|
79
|
+
@input.value = value
|
80
|
+
update_dropdown
|
81
|
+
trigger :change
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Updates the dropdown with the
|
87
|
+
# inputs value:
|
88
|
+
#
|
89
|
+
# * If the color is invalid update the input
|
90
|
+
# * Alwas render color of the rectange
|
91
|
+
def update_dropdown
|
92
|
+
color_panel.color = @input.value
|
93
|
+
rescue
|
94
|
+
update_input
|
95
|
+
ensure
|
96
|
+
render
|
97
|
+
end
|
98
|
+
|
99
|
+
# Update the value of the input
|
100
|
+
# from the color panel.
|
101
|
+
def update_input
|
102
|
+
@input.value = color_panel.to_css
|
103
|
+
render
|
104
|
+
end
|
105
|
+
|
106
|
+
# Renders the rectange
|
107
|
+
def render
|
108
|
+
@div.style.background = color_panel.to_css
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module UI
|
2
|
+
# Component for layout out children either
|
3
|
+
# vertically or horizontally using flexbox.
|
4
|
+
#
|
5
|
+
# Attributes:
|
6
|
+
# * *direction*:
|
7
|
+
# * *column* - lays out chilren vertically
|
8
|
+
# * *row* (default) - lays out children horizontally
|
9
|
+
# * *compact*:
|
10
|
+
# * *true* - no spacing between the children
|
11
|
+
# * *false* (default) - there is spacing between the children
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# component :box, UI::Container, compact: true, direction: :column
|
15
|
+
#
|
16
|
+
# @author Gusztáv Szikszai
|
17
|
+
# @since 0.1.0
|
18
|
+
class Container < Base
|
19
|
+
tag 'ui-container'
|
20
|
+
|
21
|
+
style display: :flex,
|
22
|
+
fontFamily: -> { theme.font_family },
|
23
|
+
'&[direction=column]' => {
|
24
|
+
flexDirection: :column,
|
25
|
+
'&:not([compact]) > * + *' => {
|
26
|
+
marginTop: -> { theme.spacing.em }
|
27
|
+
}
|
28
|
+
},
|
29
|
+
'&[align=center]' => {
|
30
|
+
justifyContent: 'center'
|
31
|
+
},
|
32
|
+
'&[align=end]' => {
|
33
|
+
justifyContent: 'flex-end'
|
34
|
+
},
|
35
|
+
'&[direction=row]:not([compact]) > * + *' => {
|
36
|
+
marginLeft: -> { theme.spacing.em }
|
37
|
+
}
|
38
|
+
|
39
|
+
attribute_accessor :direction
|
40
|
+
attribute_accessor :align
|
41
|
+
|
42
|
+
# Initializes the container by setting
|
43
|
+
# default value for direction to column.
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
self[:direction] ||= :column
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets / removes the compact attribute
|
50
|
+
# based on the value.
|
51
|
+
#
|
52
|
+
# @param value [Boolean] The value
|
53
|
+
def compact=(value)
|
54
|
+
if !value
|
55
|
+
remove_attribute :compact
|
56
|
+
else
|
57
|
+
self[:compact] = ''
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'fron-ui/components/input'
|
2
|
+
require 'fron-ui/components/icon'
|
3
|
+
require 'fron-ui/components/dropdown'
|
4
|
+
require 'fron-ui/components/calendar'
|
5
|
+
|
6
|
+
module UI
|
7
|
+
# Component for selecting a date
|
8
|
+
#
|
9
|
+
# Features:
|
10
|
+
# * Input field for manual input
|
11
|
+
# * Dropdown with calendar for date selection
|
12
|
+
# * Selected date is highlighted
|
13
|
+
#
|
14
|
+
# @attr_reader [Date] value The value of the component
|
15
|
+
#
|
16
|
+
# @author Gusztáv Szikszai
|
17
|
+
# @since 0.1.0
|
18
|
+
class DatePicker < Base
|
19
|
+
include UI::Behaviors::Keydown
|
20
|
+
include UI::Behaviors::Dropdown
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
attr_reader :value
|
24
|
+
|
25
|
+
tag 'ui-date-picker'
|
26
|
+
|
27
|
+
style color: -> { readable_color colors.input },
|
28
|
+
display: 'inline-block',
|
29
|
+
position: :relative,
|
30
|
+
'> ui-icon' => { width: -> { theme.size.em },
|
31
|
+
position: :absolute,
|
32
|
+
fontSize: 1.2.em,
|
33
|
+
bottom: 0,
|
34
|
+
right: 0,
|
35
|
+
top: 0 },
|
36
|
+
input: { paddingRight: -> { (theme.size * 1.25).em },
|
37
|
+
boxSizing: 'border-box',
|
38
|
+
width: '100%' },
|
39
|
+
'table td[date]' => { cursor: :pointer,
|
40
|
+
'&:hover' => { color: -> { readable_color(colors.primary) },
|
41
|
+
background: -> { colors.primary },
|
42
|
+
fontWeight: 600 } },
|
43
|
+
'table td[date].selected' => { color: -> { readable_color(colors.focus) },
|
44
|
+
background: -> { colors.focus },
|
45
|
+
fontWeight: 600 }
|
46
|
+
|
47
|
+
component :input, UI::Input
|
48
|
+
component :icon, UI::Icon, glyph: :calendar
|
49
|
+
|
50
|
+
component :dropdown, UI::Dropdown do
|
51
|
+
component :calendar, UI::Calendar
|
52
|
+
end
|
53
|
+
|
54
|
+
def_delegators :input, :active?, :focus, :blur
|
55
|
+
|
56
|
+
on :click, 'td[date]', :select
|
57
|
+
on :change, 'input', :changed
|
58
|
+
on :rendered, :render
|
59
|
+
|
60
|
+
keydown :down, :next
|
61
|
+
keydown :up, :prev
|
62
|
+
|
63
|
+
dropdown :input, :dropdown
|
64
|
+
|
65
|
+
# Initialize the component by
|
66
|
+
# setting the default value for today.
|
67
|
+
def initialize
|
68
|
+
super
|
69
|
+
@input.on(:focus) { render }
|
70
|
+
@dropdown.on :mousedown, &:prevent_default
|
71
|
+
self.value = Date.today
|
72
|
+
end
|
73
|
+
|
74
|
+
# Selects the next date, if shift is down
|
75
|
+
# it select the same date in the next month,
|
76
|
+
#
|
77
|
+
# @param event [DOM::Event] The event
|
78
|
+
def next(event)
|
79
|
+
self.value = if event.shift?
|
80
|
+
@value.next_month
|
81
|
+
else
|
82
|
+
@value + 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Selects the previous date, if shift is down
|
87
|
+
# it select the same date in the previous month,
|
88
|
+
#
|
89
|
+
# @param event [DOM::Event] The event
|
90
|
+
def prev(event)
|
91
|
+
self.value = if event.shift?
|
92
|
+
@value.prev_month
|
93
|
+
else
|
94
|
+
@value - 1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the value of the field
|
99
|
+
#
|
100
|
+
# @param date [Date] The date
|
101
|
+
def value=(date)
|
102
|
+
date = parse_date(date)
|
103
|
+
raise "Supplied value '#{date}' is not a date or cound't be coerced into one!" unless date.is_a?(Date)
|
104
|
+
changed = @value != date
|
105
|
+
@value = date
|
106
|
+
@input.value = @value.strftime
|
107
|
+
@dropdown.calendar.render @value
|
108
|
+
trigger :change if changed
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_date(raw)
|
112
|
+
Date.parse(raw)
|
113
|
+
rescue
|
114
|
+
raw
|
115
|
+
end
|
116
|
+
|
117
|
+
# Selects the clicked td
|
118
|
+
#
|
119
|
+
# @param event [DOM::Event] The event
|
120
|
+
def select(event)
|
121
|
+
self.value = Date.parse(event.target[:date])
|
122
|
+
end
|
123
|
+
|
124
|
+
# Updates the value after the change
|
125
|
+
# of the input, if it's a wrong date
|
126
|
+
# it sets it for today.
|
127
|
+
def changed
|
128
|
+
self.value = Date.parse(@input.value)
|
129
|
+
rescue
|
130
|
+
self.value = Date.today
|
131
|
+
warn 'Wrong date input!'
|
132
|
+
end
|
133
|
+
|
134
|
+
# Renders the component
|
135
|
+
def render
|
136
|
+
cell = @dropdown.find("td[date='#{value}']")
|
137
|
+
return unless cell
|
138
|
+
cell.add_class :selected
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module UI
|
2
|
+
# Base component for handling drags.
|
3
|
+
#
|
4
|
+
# @see UI::ColorPanel
|
5
|
+
# @see UI::Slider
|
6
|
+
#
|
7
|
+
# @attr value_x [Float] The value of the x axis (0..1)
|
8
|
+
# @attr value_y [Float] The value of the y axis (0..1)
|
9
|
+
# @attr vertical [Boolean] Whether or not to allow vertical drag
|
10
|
+
# @attr horizontal [Boolean] Whether or not to allow horizontal drag
|
11
|
+
# @attr_reader drag [Fron::Drag] The drag instance
|
12
|
+
#
|
13
|
+
# @author Gusztáv Szikszai
|
14
|
+
# @since 0.1.0
|
15
|
+
# @abstract
|
16
|
+
class Drag < Base
|
17
|
+
tag 'ui-drag'
|
18
|
+
|
19
|
+
attr_accessor :vertical, :horizontal
|
20
|
+
attr_reader :drag
|
21
|
+
|
22
|
+
component :handle, 'ui-drag-handle'
|
23
|
+
|
24
|
+
style background: -> { colors.input },
|
25
|
+
display: 'inline-block',
|
26
|
+
position: :relative,
|
27
|
+
'ui-drag-handle' => {
|
28
|
+
background: -> { rgba(readable_color(colors.input), 0.5) },
|
29
|
+
transform: 'translateX(-50%) translateY(-50%)',
|
30
|
+
pointerEvents: :none,
|
31
|
+
position: :absolute,
|
32
|
+
borderRadius: '50%',
|
33
|
+
height: 1.em,
|
34
|
+
width: 1.em
|
35
|
+
}
|
36
|
+
|
37
|
+
# Axes for the drag
|
38
|
+
AXES = {
|
39
|
+
horizontal: { coord: :x, style: :left, side: :width },
|
40
|
+
vertical: { coord: :y, style: :top, side: :height }
|
41
|
+
}
|
42
|
+
|
43
|
+
# Initializes the component
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
AXES.keys.each { |axis| send("#{axis}=", true) }
|
47
|
+
|
48
|
+
@drag = Fron::Drag.new self, 0
|
49
|
+
@drag.on(:move) { |position| on_drag_move position }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Moves the handle from the position in each axis
|
53
|
+
#
|
54
|
+
# @param position [Fron::Point] The point
|
55
|
+
def on_drag_move(position)
|
56
|
+
point = position - Fron::Point.new(left, top)
|
57
|
+
|
58
|
+
AXES.each do |key, axis|
|
59
|
+
next unless send(key)
|
60
|
+
@handle.style[axis[:style]] = point.send(axis[:coord]).clamp(0, send(axis[:side])).px
|
61
|
+
end
|
62
|
+
|
63
|
+
trigger 'change'
|
64
|
+
end
|
65
|
+
|
66
|
+
AXES.values.each do |axis|
|
67
|
+
define_method "value_#{axis[:coord]}" do
|
68
|
+
@handle.style.send(axis[:style]).to_i / send(axis[:side])
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method "value_#{axis[:coord]}=" do |position|
|
72
|
+
@handle.style[axis[:style]] = (position.clamp(0, 1) * send(axis[:side])).px
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'fron-ui/components/container'
|
2
|
+
|
3
|
+
module UI
|
4
|
+
# Component for handling open / closed state
|
5
|
+
# and stop events when clicking inside the
|
6
|
+
# component to prevent closing.
|
7
|
+
#
|
8
|
+
# @author Gusztáv Szikszai
|
9
|
+
# @since 0.1.0
|
10
|
+
class Dropdown < Container
|
11
|
+
include UI::Behaviors::Transition
|
12
|
+
|
13
|
+
tag 'ui-dropdown'
|
14
|
+
|
15
|
+
style boxShadow: '0 0.3em 0.625em -0.3em rgba(0,0,0,0.75)',
|
16
|
+
transition: 'opacity 320ms, transform 320ms',
|
17
|
+
borderRadius: -> { theme.border_radius.em },
|
18
|
+
background: -> { colors.input },
|
19
|
+
pointerEvents: :none,
|
20
|
+
position: :absolute,
|
21
|
+
display: :none,
|
22
|
+
zIndex: 100,
|
23
|
+
'&.open' => { display: :block, pointerEvents: :auto },
|
24
|
+
'&[vertical=top]' => { marginBottom: -> { (theme.spacing / 2).em },
|
25
|
+
bottom: '100%' },
|
26
|
+
'&[vertical=bottom]' => { marginTop: -> { (theme.spacing / 2).em },
|
27
|
+
top: '100%' },
|
28
|
+
'&[horizontal=left]' => { right: 0 },
|
29
|
+
'&[horizontal=right]' => { left: 0 }
|
30
|
+
|
31
|
+
transition :show, duration: '320ms',
|
32
|
+
frames: { '0%' => { opacity: 0, transform: 'translateY(0.5em)' },
|
33
|
+
'100%' => { opacity: 1, transform: 'translateY(0)' } }
|
34
|
+
|
35
|
+
transition :hide, duration: '320ms',
|
36
|
+
frames: { '0%' => { opacity: 1, transform: 'translateY(0)' },
|
37
|
+
'100%' => { opacity: 0, transform: 'translateY(0.5em)' } }
|
38
|
+
|
39
|
+
# Opens the component:
|
40
|
+
#
|
41
|
+
# * adds the *open* class
|
42
|
+
# * sets the *position* attribute to either top or bottom depending
|
43
|
+
# where is more space in the screen
|
44
|
+
def open
|
45
|
+
self[:vertical] = position?(:top, :scroll_y, :innerHeight) ? :top : :bottom
|
46
|
+
self[:horizontal] = position?(:left, :scroll_x, :innerWidth) ? :left : :right
|
47
|
+
add_class 'open'
|
48
|
+
transition! :show
|
49
|
+
end
|
50
|
+
|
51
|
+
# Closes the component
|
52
|
+
def close
|
53
|
+
transition! :hide do
|
54
|
+
remove_class 'open'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Returns which side to display the dropdown
|
61
|
+
# to based on parameters.
|
62
|
+
#
|
63
|
+
# @param style [Symbol] Which style to use
|
64
|
+
# @param scroll [Symbol] Which scroll position to use
|
65
|
+
# @param size [Symbol] Which window size to use
|
66
|
+
#
|
67
|
+
# @return [Bollean] True or False
|
68
|
+
def position?(style, scroll, size)
|
69
|
+
parent && (parent.send(style) - DOM::Window.send(scroll)) > `window[#{size}] / 2`
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|