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