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.
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,67 @@
1
+ module UI
2
+ module Behaviors
3
+ # Behavior for handling for selecting
4
+ # one of the children of the component.
5
+ #
6
+ # @author Gusztáv Szikszai
7
+ # @since 0.1.0
8
+ module SelectableChildren
9
+ # Sets up the behavior:
10
+ #
11
+ # * Sets up click event for the selecting
12
+ # * Sets styles for children to have pointer cursor
13
+ #
14
+ # @param base [Fron::Component] The includer
15
+ def self.included(base)
16
+ base.on :click, :on_select
17
+ base.attribute_accessor :multiple, default: false
18
+ base.attribute_accessor :deselectable, default: false
19
+ base.style '> *' => { cursor: :pointer }
20
+ end
21
+
22
+ # Runs when a child is clicked
23
+ #
24
+ # @param event [DOM::Event] The event
25
+ def on_select(event)
26
+ select children.find { |child| child.include?(event.target) }
27
+ end
28
+
29
+ # Selects the given child
30
+ #
31
+ # @param child [Fron::Component] The child
32
+ def select(child)
33
+ return unless child
34
+ return if !deselectable && selected == child
35
+ deselect if !multiple && selected != child
36
+ child.toggle_class :selected
37
+ trigger :selected_change
38
+ true
39
+ end
40
+
41
+ # Deselects all selected children
42
+ def deselect
43
+ Array(selected).each { |item| item.remove_class :selected }
44
+ end
45
+
46
+ # Selects the first child
47
+ def select_first
48
+ select children.first
49
+ end
50
+
51
+ # Selects the last child
52
+ def select_last
53
+ select children.last
54
+ end
55
+
56
+ # Returns the selected child
57
+ #
58
+ # @return [Fron::Component] The child
59
+ def selected
60
+ selected = children.select { |child| child.has_class :selected }
61
+ return nil if selected.empty?
62
+ return selected.first if selected.count == 1
63
+ selected
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ module UI
2
+ module Behaviors
3
+ # Behavior for seralizing and
4
+ # deseralizing data from named
5
+ # (having a name attribute) components
6
+ #
7
+ # @author Gusztáv Szikszai
8
+ # @since 0.1.0
9
+ module Serialize
10
+ # Populates items with the given data
11
+ #
12
+ # @param data [Hash] The data
13
+ def load(data)
14
+ @data = data
15
+ find_all('[name]').each do |item|
16
+ item.value = @data[item[:name]]
17
+ end
18
+ end
19
+
20
+ # Gathers the data from elements
21
+ #
22
+ # :reek:FeatureEnvy
23
+ #
24
+ # @return [Hash] The data
25
+ def data
26
+ find_all('[name]').each_with_object(@data || {}) do |item, memo|
27
+ memo[item[:name]] = item.value
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ module UI
2
+ module Behaviors
3
+ module Shortcuts
4
+ def self.included(base)
5
+ base.register self, [:shortcut]
6
+ end
7
+
8
+ def self.shortcut(item)
9
+ initialize_shortcuts
10
+ shortcut, action = item[:args]
11
+ shortcuts << { parts: shortcut.split(Fron::Keyboard::DELIMETERS), action: action }
12
+ end
13
+
14
+ def initialize_shortcuts
15
+ @shortcut_event = DOM::Window.on(:keyup) do |event|
16
+ break if DOM::Document.active_element
17
+ break unless visible?
18
+
19
+ combo = Fron::Keyboard.calculate_shortcut event
20
+
21
+ shortcuts.each do |shortcut|
22
+ next unless shortcut[:parts].sort == combo.sort
23
+ send shortcut[:action]
24
+ event.stop
25
+ break
26
+ end
27
+ end
28
+ end
29
+
30
+ def shortcuts
31
+ @shortcuts ||= []
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,56 @@
1
+ module UI
2
+ module Behaviors
3
+ # Behavior for managing state into
4
+ # search part of the location.
5
+ #
6
+ # @author Gusztáv Szikszai
7
+ # @since 0.1.0
8
+ module State
9
+ # Sets up the behavior.
10
+ #
11
+ # @param base [Fron::Component] The includer
12
+ def self.included(base)
13
+ base.register self, [:state_changed]
14
+ end
15
+
16
+ # Sets up events for popstate to handle
17
+ # back and forward buttons.
18
+ #
19
+ # @param item [Hash] The arguments
20
+ def self.state_changed(item)
21
+ method = item[:args][0]
22
+ DOM::Window.on('popstate') { send method }
23
+ timeout do
24
+ send method
25
+ end
26
+ end
27
+
28
+ # Returns the decoded state.
29
+ #
30
+ # @return The state
31
+ def state
32
+ search = `location.search`[1..-1]
33
+ StateSerializer.decode(`window.atob(#{search})`)
34
+ rescue
35
+ ''
36
+ end
37
+
38
+ # Sets the state
39
+ #
40
+ # @param new_state The new state
41
+ def state=(new_state, options = {})
42
+ return if state == new_state
43
+ path = yield if block_given?
44
+ hash = `window.btoa(#{StateSerializer.encode(new_state)})`
45
+ new_url = path.to_s + '?' + hash
46
+ if options.to_h[:replace]
47
+ DOM::Window.replace_state new_url
48
+ else
49
+ DOM::Window.state = new_url
50
+ end
51
+ end
52
+
53
+ alias set_state state=
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,63 @@
1
+ module UI
2
+ module Behaviors
3
+ # Behavior for managing style transitions.
4
+ #
5
+ # @author Gusztáv Szikszai
6
+ # @since 0.1.0
7
+ module Transition
8
+ # Sets up the behavior:
9
+ #
10
+ # * Listens on animation end events (prefixed)
11
+ #
12
+ # @param base [Fron::Component] The includer
13
+ def self.included(base)
14
+ base.on :animationend, :on_transition_end
15
+ base.on :webkitAnimationEnd, :on_transition_end
16
+ base.on :oanimationend, :on_transition_end
17
+ base.on :MSAnimationEnd, :on_transition_end
18
+ base.meta_def :transition do |animation_name, options|
19
+ name = "#{tagname}-#{animation_name}"
20
+ options[:animation_name] = name
21
+ Fron::Sheet.add_animation name, options[:frames]
22
+ @registry << { method: Transition.method(:init_transition),
23
+ args: [animation_name, options],
24
+ id: SecureRandom.uuid }
25
+ end
26
+ end
27
+
28
+ # Handles animation end events, and
29
+ # calls the callback if sepcified.
30
+ #
31
+ # @param event [DOM::Event] The event
32
+ def on_transition_end(event)
33
+ options = @transitions[event.animationName.split('-').last]
34
+ send options[:callback] if options &&
35
+ options[:callback] &&
36
+ respond_to?(options[:callback])
37
+ @transition_callback.call if @transition_callback
38
+ end
39
+
40
+ # Adds the transitions animations to
41
+ # the stylesheet.
42
+ #
43
+ # @param item [Hash] The item data
44
+ def self.init_transition(item)
45
+ name = item[:args][0]
46
+ options = item[:args][1]
47
+ @transitions ||= {}
48
+ @transitions[name] = options
49
+ end
50
+
51
+ # Starts the transition with the given name.
52
+ #
53
+ # @param name [String] The name of the transition
54
+ def transition!(name, &block)
55
+ options = @transitions[name]
56
+ @style.animation = [options[:animation_name],
57
+ options[:duration],
58
+ options[:delay], 'both'].join(' ')
59
+ @transition_callback = block
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ module UI
2
+ # Base component for elements that handle
3
+ # *action* (enter or space) as well as clicks.
4
+ #
5
+ # @author Gusztáv Szikszai
6
+ # @since 0.1.0
7
+ class Action < Base
8
+ include UI::Behaviors::Action
9
+
10
+ tag 'ui-action'
11
+
12
+ # Initializes the component by setting the tabindex.
13
+ def initialize
14
+ super
15
+ self[:tabindex] ||= 0
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'fron-ui/components/container'
2
+
3
+ module UI
4
+ # Box component with background, padding
5
+ # and border radius
6
+ #
7
+ # @author Gusztáv Szikszai
8
+ # @since 0.1.0
9
+ class Box < Container
10
+ tag 'ui-box'
11
+
12
+ style borderRadius: -> { (theme.border_radius * 2).em },
13
+ color: -> { readable_color colors.background },
14
+ padding: -> { (theme.spacing * 1.75).em },
15
+ background: -> { colors.background }
16
+ end
17
+ end
@@ -0,0 +1,61 @@
1
+ require 'fron-ui/components/action'
2
+
3
+ module UI
4
+ # Button component.
5
+ #
6
+ # It has the following types:
7
+ # * primary
8
+ # * success
9
+ # * danger
10
+ #
11
+ # @author Gusztáv Szikszai
12
+ # @since 0.1.0
13
+ class Button < Action
14
+ tag 'ui-button'
15
+
16
+ style padding: -> { "0 #{(theme.spacing * 1.4).em}" },
17
+ borderRadius: -> { theme.border_radius.em },
18
+ fontFamily: -> { theme.font_family },
19
+ lineHeight: -> { theme.size.em },
20
+ height: -> { theme.size.em },
21
+ textOverflow: :ellipsis,
22
+ display: 'inline-block',
23
+ whiteSpace: :nowrap,
24
+ textAlign: :center,
25
+ userSelect: :none,
26
+ fontWeight: '600',
27
+ overflow: :hidden,
28
+ '&[type]:focus' => { boxShadow: -> { theme.focus_box_shadow.call } },
29
+ '&[shape=square]' => { minWidth: -> { theme.size.em },
30
+ height: -> { theme.size.em },
31
+ justifyContent: :center,
32
+ display: 'inline-flex',
33
+ alignItems: :center,
34
+ padding: '0' },
35
+ '> *' => { pointerEvents: :none }
36
+
37
+ Fron::Sheet.helper.colors.to_h.keys.each do |type|
38
+ style "&[type=#{type}]" => { color: -> { readable_color(colors[type]) },
39
+ background: -> { colors[type] } }
40
+ end
41
+
42
+ # Initializes the button by setting
43
+ # the type to primary.
44
+ def initialize
45
+ super
46
+ self[:type] ||= :primary
47
+ end
48
+
49
+ def loading!(text = '')
50
+ @_text = self.text
51
+ self.text = text
52
+ self.disabled = true
53
+ end
54
+
55
+ def reset!
56
+ self.disabled = false
57
+ self.text = @_text
58
+ @_text = nil
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,129 @@
1
+ module UI
2
+ # A component for rendering a month calendar for a given date.
3
+ #
4
+ # It has the following features:
5
+ # * Navigate to next / previous month
6
+ # * Yields all the days (with their cells) for extended usage
7
+ # * Displays the name of the rendered month (and year if it's not the same)
8
+ #
9
+ # @author Gusztáv Szikszai
10
+ # @since 0.1.0
11
+ class Calendar < Base
12
+ include UI::Behaviors::Actions
13
+ extend Forwardable
14
+
15
+ tag 'ui-calendar'
16
+
17
+ component :header, UI::Container, direction: :row do
18
+ component :icon, UI::Icon, glyph: 'arrow-left-b', clickable: true, action: :prev_month
19
+ component :label, UI::Label, flex: 1
20
+ component :icon, UI::Icon, glyph: 'arrow-right-b', clickable: true, action: :next_month
21
+ end
22
+
23
+ component :table, :table do
24
+ component :thead, :thead do
25
+ component :tr, :tr
26
+ end
27
+
28
+ component :tbody, :tbody
29
+ end
30
+
31
+ def_delegators :table, :tbody, :thead
32
+ def_delegators :header, :label
33
+
34
+ style color: -> { readable_color colors.input },
35
+ borderRadius: -> { theme.border_radius.em },
36
+ padding: -> { (theme.spacing * 1.25).em },
37
+ background: -> { colors.input },
38
+ display: :block,
39
+ minWidth: 18.em,
40
+ 'ui-label' => { textAlign: :center,
41
+ fontWeight: 700 },
42
+ 'ui-icon' => { width: -> { (theme.spacing * 2).em } },
43
+ 'ui-container' => { borderBottomWidth: -> { (theme.border_size / 2).em },
44
+ borderBottomColor: -> { dampen colors.input, 0.05 },
45
+ paddingBottom: -> { theme.spacing.em },
46
+ borderBottomStyle: :solid },
47
+ table: { borderSpacing: 0.4.em,
48
+ 'td[date]' => { background: -> { dampen colors.input, 0.05 },
49
+ color: -> { readable_color(dampen(colors.input, 0.05)) } },
50
+ 'th' => { fontSize: 0.8.em,
51
+ padding: -> { "#{theme.spacing.em} 0" },
52
+ opacity: 0.6 },
53
+ 'td, th' => { borderRadius: -> { theme.border_radius.em },
54
+ textAlign: :center,
55
+ height: 2.em,
56
+ width: 2.em } }
57
+
58
+ # Initializes the calendar with todays date
59
+ def initialize
60
+ super
61
+ render
62
+ end
63
+
64
+ # Renders the next month in the calendar
65
+ def next_month
66
+ render @date.next_month
67
+ end
68
+
69
+ # Renders the previous month in the calendar
70
+ def prev_month
71
+ render @date.prev_month
72
+ end
73
+
74
+ # Renders the calendar for the given date
75
+ #
76
+ # @param date [Date] The date
77
+ def render(date = Date.today)
78
+ @date = date
79
+ label.text = date.strftime date.year == Date.today.year ? '%B' : '%B %Y'
80
+ render_header
81
+ render_body(date) do |*args|
82
+ yield(*args) if block_given?
83
+ end
84
+ trigger :rendered
85
+ end
86
+
87
+ private
88
+
89
+ # Renders the table header with the shortnames
90
+ def render_header
91
+ thead.tr.empty
92
+ Date.this_week.each do |day|
93
+ thead.tr << DOM::Element.new("th #{day.strftime('%a')}")
94
+ end
95
+ end
96
+
97
+ # Renders the table body with the days
98
+ def render_body
99
+ tbody.empty
100
+
101
+ @date.days_for_calendar.each_slice(7) do |data|
102
+ row = DOM::Element.new('tr')
103
+ row >> tbody
104
+
105
+ data.each do |day|
106
+ td = Td.new(day)
107
+ td >> row
108
+ next if day.is_a?(String)
109
+ yield day, td
110
+ end
111
+ end
112
+ end
113
+
114
+ # Cell for the calendar body
115
+ class Td < Fron::Component
116
+ tag 'td'
117
+
118
+ # Initialize the cell with the day
119
+ #
120
+ # @param day [Date] The day
121
+ def initialize(day)
122
+ super nil
123
+ return if day.is_a?(String)
124
+ self[:date] = day
125
+ self.text = day.day
126
+ end
127
+ end
128
+ end
129
+ end