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,46 @@
1
+ module Examples
2
+ class Contacts < UI::Container
3
+ # Item
4
+ class Item < UI::Container
5
+ include UI::Behaviors::Action
6
+ include ::Record
7
+
8
+ tag 'ui-item'
9
+
10
+ component :image, UI::Image, width: 2.em, height: 2.em
11
+ component :name, UI::Label
12
+
13
+ style padding: -> { (theme.spacing / 2).em },
14
+ lineHeight: 2.em,
15
+ transition: 'border 200ms',
16
+ borderLeft: '0em solid transparent',
17
+ 'ui-loader' => { fontSize: 0.6.em },
18
+ '&.selected' => {
19
+ borderLeft: -> { "0.4em solid #{colors.primary}" }
20
+ },
21
+ '&:focus' => {
22
+ borderLeft: -> { "0.4em solid #{colors.focus}" }
23
+ }
24
+
25
+ # Initializes the item by setting the direction
26
+ def initialize
27
+ super
28
+ self[:tabindex] = 0
29
+ self[:direction] = :row
30
+ end
31
+
32
+ # Returns the id of the item
33
+ #
34
+ # @return [String] The ID
35
+ def id
36
+ @data[:id]
37
+ end
38
+
39
+ # Renders the component
40
+ def render
41
+ @image.src = 'http://www.gravatar.com/avatar/' + `md5(#{data[:email] || ''})` + '?s=100&d=identicon'
42
+ @name.text = data[:name] || ' '
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module Examples
2
+ class Contacts < UI::Container
3
+ # List
4
+ class List < UI::List
5
+ include UI::Behaviors::SelectableChildren
6
+
7
+ tag 'ui-list'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'item'
2
+ require_relative 'list'
3
+
4
+ module Examples
5
+ class Contacts < UI::Container
6
+ # Sidebar
7
+ class Sidebar < UI::Box
8
+ extend Forwardable
9
+
10
+ tag 'ui-sidebar'
11
+
12
+ component :title, UI::Title, text: 'Contacts'
13
+ component :input, UI::Input, placeholder: 'Search...'
14
+ component :list, List, empty_message: 'No contacts to display!', flex: 1, base: Item
15
+ component :button, UI::Button, text: 'Add new contact!', action: :add
16
+
17
+ def_delegators :list, :items, :items=, :selected, :deselect
18
+
19
+ ensure_styles!
20
+
21
+ # Selects the item with the given id
22
+ #
23
+ # @param id [String] The ID
24
+ def select(id)
25
+ return @list.deselect if id.empty?
26
+ @list.select @list.children.find { |item| item.data[:id] == id }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ require 'fron_ui'
2
+ require_tree './components'
3
+ contacts = Examples::Contacts.new
4
+ contacts >> DOM::Document.body
5
+ contacts.refresh
6
+ Fron::Sheet.render_style_tag
@@ -0,0 +1,164 @@
1
+ require 'fron_ui'
2
+
3
+ URL = 'http://localhost:3000/files'
4
+
5
+ class Item < UI::Container
6
+ include UI::Behaviors::Rest
7
+ include UI::Behaviors::Action
8
+ include ::Record
9
+
10
+ style padding: -> { theme.spacing.em },
11
+ '> span' => { fontWeight: 600 }
12
+
13
+ component :span, :span
14
+
15
+ def render
16
+ @span.text = @data[:filename]
17
+ end
18
+
19
+ def action
20
+ trigger :dblclick
21
+ end
22
+ end
23
+
24
+ class List < UI::List
25
+ include UI::Behaviors::Rest
26
+
27
+ rest url: URL
28
+
29
+ def refresh
30
+ request :get, '' do |items|
31
+ self.items = items
32
+ end
33
+ end
34
+ end
35
+
36
+ class FileTab < UI::Container
37
+ include UI::Behaviors::Rest
38
+ include ::Record
39
+
40
+ tag 'ui-file'
41
+
42
+ rest url: URL
43
+
44
+ component :textarea, UI::Textarea, spellcheck: false
45
+
46
+ style position: :relative,
47
+ textarea: { position: :absolute,
48
+ padding: -> { theme.spacing.em },
49
+ width: '100%',
50
+ height: '100%',
51
+ resize: :none }
52
+
53
+ on :change, :save
54
+
55
+ def render
56
+ self[:tab_id] = @data[:id]
57
+ self[:tab_title] = @data[:filename]
58
+ @textarea.value = @data[:content].to_s
59
+ end
60
+
61
+ def save
62
+ update content: @textarea.value
63
+ end
64
+ end
65
+
66
+ class Handle < UI::Tabs::Handle
67
+ include UI::Behaviors::Actions
68
+ include ::Record
69
+
70
+ tag 'ui-tab-handle'
71
+
72
+ component :span, UI::Label, flex: 1
73
+ component :action, UI::Action, action: :close do
74
+ component :icon, UI::Icon, glyph: 'android-close'
75
+ end
76
+
77
+ style display: 'inline-flex',
78
+ '> ui-action' => {
79
+ display: :flex,
80
+ justifyContent: :center,
81
+ alignItems: :center,
82
+ width: 1.em,
83
+ marginLeft: 1.em,
84
+ pointerEvents: :auto }
85
+
86
+ def close
87
+ trigger :close_tab
88
+ end
89
+
90
+ def render
91
+ self.tab_id = @data[:id]
92
+ @span.text = @data[:title]
93
+ end
94
+ end
95
+
96
+ class Main < UI::Container
97
+ include UI::Behaviors::Actions
98
+ include UI::Behaviors::State
99
+ include UI::Behaviors::Rest
100
+
101
+ component :container, UI::Container, direction: :row, flex: 1 do
102
+ component :sidebar, UI::Box do
103
+ component :title, UI::Title, text: 'Files', direction: :row do
104
+ component :button, UI::Button, type: :success, shape: :square, action: :add do
105
+ component :icon, UI::Icon, glyph: :plus
106
+ end
107
+ end
108
+ component :list, List, base: Item
109
+ end
110
+ component :content, UI::Box, flex: 1 do
111
+ component :tabs, UI::Tabs, flex: 1, compact: true, base: Handle
112
+ end
113
+ end
114
+
115
+ rest url: URL
116
+
117
+ style padding: -> { theme.spacing.em },
118
+ boxSizing: 'border-box',
119
+ height: '100vh',
120
+ 'ui-box:first-child' => {
121
+ flex: '0 0 10em'
122
+ }
123
+
124
+ on :click, 'item', :open
125
+ on :close_tab, :close
126
+
127
+ state_changed :state_changed
128
+
129
+ def state_changed
130
+ end
131
+
132
+ def close(event)
133
+ @container.content.tabs.find_by_id(event.target.tab_id).remove!
134
+ end
135
+
136
+ def refresh
137
+ @container.sidebar.list.refresh
138
+ end
139
+
140
+ def add
141
+ name = prompt 'Name:'
142
+ return if name.to_s.strip.empty?
143
+ create id: SecureRandom.uuid, filename: name do
144
+ refresh
145
+ end
146
+ end
147
+
148
+ def open(event)
149
+ request :get, event.target.data[:id] do |data|
150
+ current = @container.content.tabs.find_by_id(data[:id])
151
+ break @container.content.tabs.select current if current
152
+ tab = FileTab.new
153
+ tab.data = data
154
+ @container.content.tabs << tab
155
+ @container.content.tabs.select tab
156
+ end
157
+ end
158
+ end
159
+
160
+ main = Main.new
161
+ main.refresh
162
+
163
+ DOM::Document.body << main
164
+ Fron::Sheet.render_style_tag
@@ -0,0 +1,193 @@
1
+ require 'fron_ui'
2
+ require 'fron-ui/utils/theme_roller'
3
+
4
+ class String
5
+ def to_class
6
+ split('::').inject(Object) do |mod, class_name|
7
+ mod.const_get(class_name)
8
+ end
9
+ end
10
+ end
11
+
12
+ class Item < UI::Label
13
+ include Record
14
+
15
+ style padding: -> { theme.spacing.em }
16
+
17
+ def render
18
+ self.text = @data[:id]
19
+ end
20
+ end
21
+
22
+ class List < UI::List
23
+ include UI::Behaviors::SelectableChildren
24
+ end
25
+
26
+ class Demo < UI::Box
27
+ style display: :flex,
28
+ justifyContent: :center,
29
+ alignItems: :center,
30
+ '> *' => {
31
+ maxHeight: '85%',
32
+ maxWidth: '85%'
33
+ }
34
+ end
35
+
36
+ class Field < UI::Container
37
+ extend Forwardable
38
+
39
+ component :label, UI::Label, flex: 1
40
+
41
+ def_delegators :input, :value, :value=
42
+ def_delegators :label, :text, :text=
43
+
44
+ style width: 20.em
45
+ end
46
+
47
+ class CheckboxField < Field
48
+ component :input, UI::Checkbox
49
+
50
+ style 'ui-checkbox' => { order: 1,
51
+ marginLeft: 0 },
52
+ 'ui-label' => { order: 2,
53
+ lineHeight: 2.em,
54
+ marginLeft: -> { theme.spacing.em } }
55
+
56
+ def initialize
57
+ super
58
+ self[:direction] = :row
59
+ self.compact = true
60
+ end
61
+ end
62
+
63
+ class InputField < Field
64
+ component :input, UI::Input
65
+ end
66
+
67
+ class RangeField < Field
68
+ component :input, UI::NumberRange
69
+ end
70
+
71
+ class Main < UI::Container
72
+ tag 'main'
73
+
74
+ style padding: -> { theme.spacing.em },
75
+ boxSizing: 'border-box',
76
+ overflow: :hidden,
77
+ margin: '0 auto',
78
+ height: '100vh',
79
+ '> ui-container > ui-container > ui-box' => {
80
+ flexWrap: :wrap,
81
+ height: 10.3.em
82
+ }
83
+
84
+ component :container, UI::Container, direction: :row, flex: 1 do
85
+ component :sidebar, UI::Box do
86
+ component :title, UI::Title, text: 'Components'
87
+ component :list, List, base: ::Item
88
+ end
89
+
90
+ component :demo_container, UI::Container, flex: 1 do
91
+ component :demo, Demo, flex: 1
92
+ component :options, UI::Box
93
+ end
94
+
95
+ component :theme, ThemeRoller
96
+ end
97
+
98
+ on :selected_change, 'list', :populate
99
+
100
+ def populate(event)
101
+ data = event.target.selected.data
102
+ el = data[:id].to_class.new
103
+ data[:proc].call el if data[:proc]
104
+ data[:args].to_h.each do |key, value|
105
+ if el.respond_to?("#{key}=")
106
+ el.send("#{key}=", value)
107
+ else
108
+ el[key] = value
109
+ end
110
+ end
111
+ oc = @container.demo_container.options
112
+ oc.empty
113
+ data[:options].to_h.each do |key, value|
114
+ input = case value
115
+ when :range
116
+ RangeField.new
117
+ when :input
118
+ InputField.new
119
+ when :checkbox
120
+ CheckboxField.new
121
+ end
122
+ input.text = key
123
+ input.value = el.send key
124
+ input.on :change do
125
+ el.send("#{key}=", input.value)
126
+ end
127
+ oc << input
128
+ end
129
+ request_animation_frame do
130
+ @container.demo_container.demo.empty
131
+ @container.demo_container.demo << el
132
+ end
133
+ end
134
+ end
135
+
136
+ CHOOSER_ITEMS = [
137
+ { id: 'en', value: 'English' },
138
+ { id: 'hu', value: 'Hungarian' },
139
+ { id: 'fr', value: 'French' },
140
+ { id: 'gr', value: 'German' },
141
+ { id: 'aar', value: 'Afar' },
142
+ { id: 'abk', value: 'Abkhazian' },
143
+ { id: 'afr', value: 'Afrikaans' },
144
+ { id: 'aka', value: 'Akan' },
145
+ { id: 'amh', value: 'Amharic' },
146
+ { id: 'ara', value: 'Arabic' },
147
+ { id: 'arg', value: 'Aragonese' },
148
+ { id: 'asm', value: 'Assamese' },
149
+ { id: 'ava', value: 'Avaric' },
150
+ { id: 'ave', value: 'Avestan' },
151
+ { id: 'aym', value: 'Aymara' },
152
+ { id: 'aze', value: 'Azerbaijani' },
153
+ { id: 'bak', value: 'Bashkir' },
154
+ { id: 'bam', value: 'Bambara' },
155
+ { id: 'bel', value: 'Belarusian' },
156
+ { id: 'ben', value: 'Bengali' },
157
+ { id: 'bih', value: 'Bihari' },
158
+ { id: 'bis', value: 'Bislama' }
159
+ ]
160
+
161
+ Fron::Sheet.add_rule 'body', { margin: 0, fontSize: 16.px }, '0'
162
+
163
+ create_tabs = lambda do |tabs|
164
+ (1..3).each do |index|
165
+ el = UI::Tabs::Tab.new
166
+ el[:tab] = "Tab #{index}"
167
+ el.html = Lorem.paragraph
168
+ tabs << el
169
+ end
170
+ end
171
+
172
+ data = [
173
+ { id: 'UI::Button', args: { text: 'Button...' }, options: { text: :input } },
174
+ { id: 'UI::DatePicker' },
175
+ { id: 'UI::ColorPicker', args: { value: '#328B3F' } },
176
+ { id: 'UI::Checkbox', args: { checked: true } },
177
+ { id: 'UI::Calendar' },
178
+ { id: 'UI::ColorPanel' },
179
+ { id: 'UI::Slider' },
180
+ { id: 'UI::Chooser', args: { items: CHOOSER_ITEMS, placeholder: 'Choose language...' }, options: { disabled: :checkbox, multiple: :checkbox, placeholder: :input, searchable: :checkbox, deselectable: :checkbox } },
181
+ { id: 'UI::Loader', args: { loading: true } },
182
+ { id: 'UI::Image', args: { src: 'http://m3.i.pbase.com/o6/90/547190/1/116543443.KT2b8KYm.IMG_9562.jpg', width: 800.px, height: 533.px } },
183
+ { id: 'UI::Progress', args: { value: 0.1 } },
184
+ { id: 'UI::Tabs', args: {}, proc: create_tabs },
185
+ { id: 'UI::NumberRange', args: { affix: 'em', label: 'width:', min: 0, max: 10, step: 0.1 }, options: { affix: :input, label: :input, min: :range, max: :range, step: :range, round: :range } }
186
+ ]
187
+
188
+ main = Main.new
189
+ main.container.sidebar.list.items = data
190
+ main.container.sidebar.list.select main.container.sidebar.list.children.last
191
+
192
+ DOM::Document.body << main
193
+ Fron::Sheet.render_style_tag