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.
- 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
|