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,29 @@
|
|
1
|
+
module UI
|
2
|
+
# Simple Icon component
|
3
|
+
#
|
4
|
+
# @author Gusztáv Szikszai
|
5
|
+
# @since 0.1.0
|
6
|
+
class Icon < UI::Base
|
7
|
+
tag 'ui-icon'
|
8
|
+
|
9
|
+
style justifyContent: :center,
|
10
|
+
display: 'inline-flex',
|
11
|
+
alignItems: :center,
|
12
|
+
fontWeight: :normal,
|
13
|
+
'&:not([clickable])' => { pointerEvents: :none },
|
14
|
+
'&[clickable]' => { cursor: :pointer,
|
15
|
+
'&:hover' => { color: -> { colors.focus } } }
|
16
|
+
|
17
|
+
# Sets the glyph
|
18
|
+
#
|
19
|
+
# @param value [String] The glyph
|
20
|
+
def glyph=(value)
|
21
|
+
self[:glyph] = value
|
22
|
+
self[:class] = "ion-#{value}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def glyph
|
26
|
+
self[:glyph]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'fron-ui/components/loader'
|
2
|
+
|
3
|
+
module UI
|
4
|
+
# Component for displaying an image, with
|
5
|
+
# a transition after it loads.
|
6
|
+
#
|
7
|
+
# @author Gusztáv Szikszai
|
8
|
+
# @since 0.1.0
|
9
|
+
class Image < Fron::Component
|
10
|
+
tag 'ui-image'
|
11
|
+
|
12
|
+
component :img, :img
|
13
|
+
component :loader, UI::Loader
|
14
|
+
|
15
|
+
style borderRadius: -> { (theme.border_radius * 2).em },
|
16
|
+
background: -> { dampen colors.background, 0.05 },
|
17
|
+
display: 'inline-block',
|
18
|
+
position: :relative,
|
19
|
+
'ui-loader' => { color: -> { dampen colors.background, 0.2 },
|
20
|
+
position: :absolute,
|
21
|
+
bottom: 0,
|
22
|
+
right: 0,
|
23
|
+
left: 0,
|
24
|
+
top: 0 },
|
25
|
+
img: { transition: 'opacity 320ms',
|
26
|
+
borderRadius: :inherit,
|
27
|
+
height: :inherit,
|
28
|
+
width: :inherit,
|
29
|
+
opacity: 0,
|
30
|
+
'&.loaded' => { opacity: 1 } }
|
31
|
+
|
32
|
+
# Initializes the component
|
33
|
+
# and listens on the load event
|
34
|
+
# because it doesn't bubble.
|
35
|
+
def initialize
|
36
|
+
super
|
37
|
+
@img.on(:load) { loaded }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Adds the loaded class
|
41
|
+
def loaded
|
42
|
+
@img.add_class :loaded
|
43
|
+
@loader.loading = false
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sets the width of the image
|
47
|
+
#
|
48
|
+
# @param value [Numeric] The width
|
49
|
+
def width=(value)
|
50
|
+
@style.width = value
|
51
|
+
end
|
52
|
+
|
53
|
+
# Sets the height of the image
|
54
|
+
#
|
55
|
+
# @param value [Numeric] The height
|
56
|
+
def height=(value)
|
57
|
+
@style.height = value
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the src attribute of the image
|
61
|
+
#
|
62
|
+
# @return [String] The attribute
|
63
|
+
def src
|
64
|
+
@img[:src]
|
65
|
+
end
|
66
|
+
|
67
|
+
# Sets the src of the image
|
68
|
+
#
|
69
|
+
# @param value [String] The URL
|
70
|
+
def src=(value)
|
71
|
+
return if !value || @img[:src] == value
|
72
|
+
@img.remove_class :loaded
|
73
|
+
@loader.loading = true
|
74
|
+
timeout(320) { @img[:src] = value }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module UI
|
2
|
+
# Input component, basically the input element wrapped
|
3
|
+
# and styled.
|
4
|
+
#
|
5
|
+
# @author Gusztáv Szikszai
|
6
|
+
# @since 0.1.0
|
7
|
+
class Input < Base
|
8
|
+
tag 'input'
|
9
|
+
|
10
|
+
defaults type: 'text'
|
11
|
+
|
12
|
+
attribute_accessor :placeholder
|
13
|
+
attribute_accessor :readonly, default: false
|
14
|
+
|
15
|
+
style borderRadius: -> { theme.border_radius.em },
|
16
|
+
color: -> { readable_color(colors.input) },
|
17
|
+
padding: -> { "0 #{theme.spacing.em}" },
|
18
|
+
fontFamily: -> { theme.font_family },
|
19
|
+
lineHeight: -> { theme.size.em },
|
20
|
+
background: -> { colors.input },
|
21
|
+
height: -> { theme.size.em },
|
22
|
+
fontSize: :inherit,
|
23
|
+
border: 0,
|
24
|
+
'&:focus' => {
|
25
|
+
boxShadow: -> { theme.focus_box_shadow.call },
|
26
|
+
borderColor: :transparent,
|
27
|
+
outline: :none
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module UI
|
2
|
+
# List component
|
3
|
+
#
|
4
|
+
# Features:
|
5
|
+
# * Empty message display in an :after element
|
6
|
+
# * Striped backgrounds for items
|
7
|
+
# * Overflow auto
|
8
|
+
#
|
9
|
+
# @author Gusztáv Szikszai
|
10
|
+
# @since 0.1.0
|
11
|
+
class List < Collection
|
12
|
+
tag 'ui-list'
|
13
|
+
|
14
|
+
style overflow: :auto,
|
15
|
+
display: :block,
|
16
|
+
'> *' => { display: :block,
|
17
|
+
'& + *' => { borderTop: -> { "1px solid #{dampen colors.background, 0.025}" } } },
|
18
|
+
'&:empty' => { padding: -> { theme.spacing.em },
|
19
|
+
justifyContent: :center,
|
20
|
+
alignItems: :center,
|
21
|
+
textAlign: :center,
|
22
|
+
display: :flex },
|
23
|
+
'&:empty:after' => { content: 'attr(empty_message)',
|
24
|
+
fontSize: 2.em,
|
25
|
+
opacity: 0.25 }
|
26
|
+
|
27
|
+
# Sets the flex value
|
28
|
+
#
|
29
|
+
# @param value [Numeric] The value
|
30
|
+
def flex=(value)
|
31
|
+
@style.flex = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# rubocop:disable DoubleNegation
|
2
|
+
|
3
|
+
module UI
|
4
|
+
# Loader component
|
5
|
+
#
|
6
|
+
# @author Gusztáv Szikszai
|
7
|
+
# @since 0.1.0
|
8
|
+
class Loader < Base
|
9
|
+
tag 'ui-loader'
|
10
|
+
|
11
|
+
component :div, 'ui-loader-div'
|
12
|
+
|
13
|
+
keyframes 'ui-loader', '0%' => { transform: 'rotate(0deg)' },
|
14
|
+
'50%' => { transform: 'rotate(179.9deg)' },
|
15
|
+
'100%' => { transform: 'rotate(359.9deg)' }
|
16
|
+
|
17
|
+
style transition: 'opacity 320ms',
|
18
|
+
justifyContent: :center,
|
19
|
+
alignItems: :center,
|
20
|
+
minHeight: 2.em,
|
21
|
+
minWidth: 2.em,
|
22
|
+
display: :flex,
|
23
|
+
color: -> { colors.primary },
|
24
|
+
opacity: 0,
|
25
|
+
pointerEvents: :none,
|
26
|
+
'&.loading' => {
|
27
|
+
opacity: 1
|
28
|
+
},
|
29
|
+
'ui-loader-div' => {
|
30
|
+
animation: 'ui-loader 1.2s infinite linear',
|
31
|
+
border: '0.15em solid currentColor',
|
32
|
+
borderTop: '0.15em solid transparent',
|
33
|
+
borderBottom: '0.15em solid transparent',
|
34
|
+
display: :flex,
|
35
|
+
justifyContent: :center,
|
36
|
+
alignItems: :center,
|
37
|
+
borderRadius: '50%',
|
38
|
+
height: 2.em,
|
39
|
+
width: 2.em,
|
40
|
+
'&:before' => {
|
41
|
+
content: '""',
|
42
|
+
border: '0.15em solid currentColor',
|
43
|
+
borderRadius: '50%',
|
44
|
+
height: 1.em,
|
45
|
+
width: 1.em
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
# Sets the loading flag
|
50
|
+
#
|
51
|
+
# @param value [Boolean] The value
|
52
|
+
def loading=(value)
|
53
|
+
toggle_class :loading, !!value
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the loading flag
|
57
|
+
#
|
58
|
+
# @return [Boolean] The value
|
59
|
+
def loading
|
60
|
+
has_class(:loading)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
File without changes
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module UI
|
2
|
+
# Component for displaying notifications.
|
3
|
+
#
|
4
|
+
# @author Gusztáv Szikszai
|
5
|
+
# @since 0.1.0
|
6
|
+
class Notifications < Base
|
7
|
+
# Notification component-
|
8
|
+
#
|
9
|
+
# @author Gusztáv Szikszai
|
10
|
+
# @since 0.1.0
|
11
|
+
class Notification < Base
|
12
|
+
include UI::Behaviors::Transition
|
13
|
+
|
14
|
+
tag 'ui-notification'
|
15
|
+
|
16
|
+
style background: -> { rgba colors.primary, 0.85 },
|
17
|
+
borderRadius: -> { theme.border_radius.em },
|
18
|
+
color: -> { readable_color colors.primary },
|
19
|
+
fontSize: 1.2.em,
|
20
|
+
fontWeight: 600,
|
21
|
+
display: :block,
|
22
|
+
lineHeight: 1,
|
23
|
+
span: {
|
24
|
+
display: :block,
|
25
|
+
padding: -> { theme.spacing.em },
|
26
|
+
paddingBottom: -> { (theme.spacing * 1.2).em }
|
27
|
+
}
|
28
|
+
|
29
|
+
component :span, :span
|
30
|
+
|
31
|
+
transition :show, duration: '500ms',
|
32
|
+
frames: { '0%' => { opacity: 0 },
|
33
|
+
'100%' => { opacity: 1 } },
|
34
|
+
callback: :hide
|
35
|
+
|
36
|
+
transition :hide, duration: '500ms',
|
37
|
+
delay: '3s',
|
38
|
+
frames: { '0%' => { opacity: 1 },
|
39
|
+
'100%' => { opacity: 0 } },
|
40
|
+
callback: :remove!
|
41
|
+
|
42
|
+
# Initializes the component, starting
|
43
|
+
# the show transition.
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
transition! :show
|
47
|
+
end
|
48
|
+
|
49
|
+
# Hides the component with the hide transition.
|
50
|
+
def hide
|
51
|
+
transition! :hide
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
tag 'ui-notifications'
|
56
|
+
|
57
|
+
style position: :absolute,
|
58
|
+
bottom: 1.5.em,
|
59
|
+
left: 1.5.em,
|
60
|
+
'> * + *' => { marginTop: -> { (theme.spacing / 1.5).em } }
|
61
|
+
|
62
|
+
# Pushes a new notification with the given
|
63
|
+
# message into the queue.
|
64
|
+
#
|
65
|
+
# @param message [String] The message
|
66
|
+
def push(message)
|
67
|
+
noti = Notification.new
|
68
|
+
noti.span.text = message
|
69
|
+
noti >> self
|
70
|
+
noti
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'fron/utils/drag'
|
2
|
+
|
3
|
+
module UI
|
4
|
+
# Range input element. This element is editable just like a normal
|
5
|
+
# input element can, the difference is that this element is draggable.
|
6
|
+
# When dragging the value is incremeted or decremented depending on the
|
7
|
+
# direction of the drag. The element can be limited to an upper and a lower value.
|
8
|
+
#
|
9
|
+
# @attr_reader drag [Fron::Drag] The drag instance
|
10
|
+
# @attr affix [String] The affix (px, %, em, ...)
|
11
|
+
# @attr label [String] The label to be displayed
|
12
|
+
# @attr step [Float] The step modifier to apply to the step, default to 1
|
13
|
+
# @attr min [Float] The minimal possible value, defaults to -infinity
|
14
|
+
# @attr max [Float] The maximum possible value, defaults to infinity
|
15
|
+
# @attr round [Float] The rounding value, defaults to 2
|
16
|
+
#
|
17
|
+
# @author Gusztáv Szikszai
|
18
|
+
# @since 0.1.0
|
19
|
+
class NumberRange < Base
|
20
|
+
# Input component
|
21
|
+
class Input < Fron::Component
|
22
|
+
tag 'ui-number-range-input'
|
23
|
+
|
24
|
+
attribute_accessor :label
|
25
|
+
attribute_accessor :affix
|
26
|
+
|
27
|
+
style borderRadius: -> { theme.border_radius.em },
|
28
|
+
lineHeight: -> { theme.size.em },
|
29
|
+
background: -> { colors.input },
|
30
|
+
height: -> { theme.size.em },
|
31
|
+
textAlign: :center,
|
32
|
+
display: :block,
|
33
|
+
fontWeight: 600,
|
34
|
+
'&:before' => { content: 'attr(label)',
|
35
|
+
marginRight: 0.2.em,
|
36
|
+
fontWeight: :normal },
|
37
|
+
'&:after' => { content: 'attr(affix)' },
|
38
|
+
'&:focus' => { boxShadow: -> { theme.focus_box_shadow.call },
|
39
|
+
outline: :none }
|
40
|
+
end
|
41
|
+
|
42
|
+
extend Forwardable
|
43
|
+
|
44
|
+
attr_reader :drag
|
45
|
+
|
46
|
+
tag 'ui-number-range'
|
47
|
+
|
48
|
+
component :input, Input, contenteditable: true
|
49
|
+
|
50
|
+
def_delegators :@input, :affix=, :affix, :label=, :label
|
51
|
+
|
52
|
+
on :keydown, :keydown
|
53
|
+
on :input, :input
|
54
|
+
on :mousemove, :on_mouse_move
|
55
|
+
on :mousedown, :on_mouse_down
|
56
|
+
|
57
|
+
attribute_accessor :step, default: 1, coerce: :to_f
|
58
|
+
attribute_accessor :round, default: 1, coerce: :to_f
|
59
|
+
attribute_accessor :max, default: Float::INFINITY, coerce: :to_f
|
60
|
+
attribute_accessor :min, default: -Float::INFINITY, coerce: :to_f
|
61
|
+
|
62
|
+
style color: -> { readable_color(colors.input) },
|
63
|
+
minWidth: -> { (theme.size * 5).em },
|
64
|
+
display: 'inline-block',
|
65
|
+
position: :relative,
|
66
|
+
'&:after, &:before' => { borderStyle: :solid,
|
67
|
+
position: :absolute,
|
68
|
+
marginTop: -0.3.em,
|
69
|
+
content: "''",
|
70
|
+
opacity: 0.5,
|
71
|
+
top: '50%',
|
72
|
+
height: 0,
|
73
|
+
width: 0 },
|
74
|
+
'&:after' => { borderColor: 'transparent currentColor transparent transparent',
|
75
|
+
borderWidth: '0.35em 0.4em 0.35em 0',
|
76
|
+
left: 0.75.em },
|
77
|
+
'&:before' => { borderColor: 'transparent transparent transparent currentColor',
|
78
|
+
borderWidth: '0.35em 0 0.35em 0.4em',
|
79
|
+
right: 0.75.em }
|
80
|
+
|
81
|
+
# Creates a new instance
|
82
|
+
def initialize
|
83
|
+
super
|
84
|
+
@input.on(:blur) { blur }
|
85
|
+
self.value = 0
|
86
|
+
setup_drag
|
87
|
+
end
|
88
|
+
|
89
|
+
def _attribute_changed
|
90
|
+
reset_value
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the value of the field
|
94
|
+
#
|
95
|
+
# @return [Float] The value of the field
|
96
|
+
def value
|
97
|
+
@value.to_f
|
98
|
+
end
|
99
|
+
|
100
|
+
def _value(value)
|
101
|
+
value = value.to_f.clamp(min, max)
|
102
|
+
return if @value == value
|
103
|
+
@value = value
|
104
|
+
end
|
105
|
+
|
106
|
+
# Sets the value of the field
|
107
|
+
#
|
108
|
+
# @param value [Float] The value
|
109
|
+
def value=(value)
|
110
|
+
_value value
|
111
|
+
@input.text = format "%.#{round}f", @value
|
112
|
+
trigger 'change'
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# Resets the value when attribute changes
|
118
|
+
def reset_value
|
119
|
+
self.value = value
|
120
|
+
end
|
121
|
+
|
122
|
+
# Sets up dragging.
|
123
|
+
def setup_drag
|
124
|
+
@drag = Fron::Drag.new self, 0
|
125
|
+
|
126
|
+
@drag.on('start') { @start_value = value }
|
127
|
+
@drag.on('move') { on_drag_move }
|
128
|
+
@drag.on('end') { on_drag_end }
|
129
|
+
end
|
130
|
+
|
131
|
+
# Runs when drag end
|
132
|
+
def on_drag_end
|
133
|
+
DOM::Document.body.style.cursor = ''
|
134
|
+
blur
|
135
|
+
end
|
136
|
+
|
137
|
+
# Runs when drag moves
|
138
|
+
def on_drag_move
|
139
|
+
DOM::Document.body.style.cursor = 'move'
|
140
|
+
value = (@start_value - @drag.diff.x * step).round(round)
|
141
|
+
self.value = value if @value != value
|
142
|
+
end
|
143
|
+
|
144
|
+
# Runs when input changes. It sets the value if the emelement
|
145
|
+
# is not empty, otherwise it inserts a non-width space to prevent
|
146
|
+
# caret jumping in chrome.
|
147
|
+
def input
|
148
|
+
@input.html = '' if text.strip == ''
|
149
|
+
_value @input.html
|
150
|
+
end
|
151
|
+
|
152
|
+
# Runs when key is pressed. Prevents hitting the enter key.
|
153
|
+
#
|
154
|
+
# :reek:FeatureEnvy
|
155
|
+
#
|
156
|
+
# @param event [Event] The event
|
157
|
+
def keydown(event)
|
158
|
+
return unless event.key == :enter
|
159
|
+
event.prevent_default
|
160
|
+
@input.blur
|
161
|
+
end
|
162
|
+
|
163
|
+
# Runs when the element is blurred. Sets the value form
|
164
|
+
# the elements text.
|
165
|
+
def blur
|
166
|
+
self.value = text
|
167
|
+
end
|
168
|
+
|
169
|
+
# Runs on pointer move. Sets the cursor to move when not
|
170
|
+
# in the center of the element
|
171
|
+
#
|
172
|
+
# @param event [Event] The event
|
173
|
+
def on_mouse_move(event)
|
174
|
+
@style.cursor = in_select_region?(event.page_x) ? '' : 'move'
|
175
|
+
end
|
176
|
+
|
177
|
+
# Runs on pointer down. Prevents propagation so dragging
|
178
|
+
# cannot start.
|
179
|
+
#
|
180
|
+
# :reek:FeatureEnvy
|
181
|
+
#
|
182
|
+
# @param event [Event] The event
|
183
|
+
def on_mouse_down(event)
|
184
|
+
@input.focus
|
185
|
+
if in_select_region?(event.page_x)
|
186
|
+
event.stop_immediate_propagation
|
187
|
+
else
|
188
|
+
event.prevent_default
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns form to position if it is in the region that
|
193
|
+
# allow selection.
|
194
|
+
#
|
195
|
+
# @param position [type] The position (horizontal) to be checked
|
196
|
+
#
|
197
|
+
# @return [Boolean] True if the position is in the region otherwise false.
|
198
|
+
def in_select_region?(position)
|
199
|
+
(left + width / 2 - position).abs <= width / 4.5
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|