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