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,75 @@
1
+ module Examples
2
+ class Comments < UI::Container
3
+ # Comment
4
+ class Comment < UI::Container
5
+ # Includes
6
+ include UI::Behaviors::Confirmation
7
+ include UI::Behaviors::Actions
8
+ include UI::Behaviors::Rest
9
+ include ::Record
10
+
11
+ # REST
12
+ rest url: 'http://localhost:3000/comments'
13
+
14
+ # Tagname
15
+ tag 'ui-comment'
16
+
17
+ # Style
18
+ style 'ui-comment-body' => { borderBottom: -> { "#{(theme.border_size / 2).em} solid #{colors.background}" },
19
+ paddingBottom: -> { theme.spacing.em },
20
+ 'p' => { margin: 0,
21
+ '& + p' => { marginTop: -> { theme.spacing.em } } } },
22
+ '&:hover ui-comment-header ui-action' => { display: :block }
23
+
24
+ # Components
25
+ component :image, UI::Image, width: 4.em, height: 4.em
26
+ component :box, UI::Container, flex: 1 do
27
+ component :header, Header, direction: :row
28
+ component :body, 'ui-comment-body'
29
+ component :footer, Footer, direction: :row
30
+ end
31
+
32
+ # Confirmation for destroy
33
+ confirmation :destroy!, 'Are you sure?'
34
+
35
+ # Initializes the comment by setting
36
+ # the direction attribute
37
+ def initialize
38
+ super
39
+ self[:direction] = :row
40
+ end
41
+
42
+ # Votes up the comment
43
+ def vote_up
44
+ update_votes(1)
45
+ end
46
+
47
+ # Votes down the comment
48
+ def vote_down
49
+ update_votes(-1)
50
+ end
51
+
52
+ # Updates the vote count by the given number
53
+ #
54
+ # @param count [Integer] The count
55
+ def update_votes(count)
56
+ update votes: @data[:votes] + count do |data|
57
+ self.data = data
58
+ end
59
+ end
60
+
61
+ # Destroys the comment and trigger refresh
62
+ def destroy!
63
+ destroy { trigger :refresh }
64
+ end
65
+
66
+ # Renders the comment
67
+ def render
68
+ @image.src = @data[:user][:image]
69
+ @box.body.html = @data[:body]
70
+ @box.header.render @data
71
+ @box.footer.render @data
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,93 @@
1
+ require_relative 'header'
2
+ require_relative 'footer'
3
+ require_relative 'comment'
4
+ require_relative 'list'
5
+
6
+ module Examples
7
+ # Comments example
8
+ class Comments < UI::Container
9
+ # Includes
10
+ include UI::Behaviors::Actions
11
+ include UI::Behaviors::Rest
12
+
13
+ # Extends
14
+ extend Forwardable
15
+
16
+ tag 'ui-comments'
17
+
18
+ # REST
19
+ rest url: 'http://localhost:3000/comments'
20
+
21
+ # Style
22
+ style margin: '2em auto',
23
+ maxWidth: 42.em,
24
+ fontSize: 16.px,
25
+ '> ui-container' => { borderBottom: -> { "#{(theme.border_size / 1.5).em} dashed #{dampen colors.background, 0.05}" },
26
+ paddingBottom: -> { theme.spacing.em },
27
+ marginBottom: -> { theme.spacing.em },
28
+ 'ui-button' => { maxWidth: 10.em,
29
+ alignSelf: 'flex-end' },
30
+ textarea: { border: -> { "#{(theme.border_size / 1.5).em} solid #{dampen colors.background, 0.05}" },
31
+ boxSizing: 'border-box',
32
+ minHeight: 7.em,
33
+ flex: 1 } }
34
+
35
+ # Components
36
+ component :form, UI::Container do
37
+ component :title, UI::Title, text: 'Leave a comment', flex: '0 0 auto'
38
+ component :box, UI::Container, direction: :row do
39
+ component :image, UI::Image, src: Lorem.avatar, width: 4.em, height: 4.em
40
+ component :input, UI::Textarea, spellcheck: false
41
+ end
42
+ component :button, UI::Button, text: 'Comment', action: :add
43
+ end
44
+ component :list, List, base: Comment
45
+
46
+ # Delegations
47
+ def_delegators :form, :box
48
+ def_delegators :box, :image, :input
49
+
50
+ # Events
51
+ on :refresh, :load
52
+
53
+ # Adds a new comment
54
+ def add
55
+ return if input.value.empty?
56
+
57
+ data = {
58
+ id: SecureRandom.uuid,
59
+ date: Time.now,
60
+ votes: 0,
61
+ user: {
62
+ name: name,
63
+ image: image.src
64
+ },
65
+ body: input.value.split("\n").map { |paragraph| "<p>#{paragraph}</p>" }.join('')
66
+ }
67
+
68
+ create data do
69
+ input.value = ''
70
+ load
71
+ end
72
+ end
73
+
74
+ # Starts to reply by focusing the input field
75
+ def reply
76
+ input.focus
77
+ end
78
+
79
+ # Returns the current users name
80
+ #
81
+ # @return [String] The name
82
+ def name
83
+ @name ||= Lorem.name
84
+ end
85
+
86
+ # Loads comments from the server
87
+ def load
88
+ all do |items|
89
+ @list.items = items.reverse
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,36 @@
1
+ module Examples
2
+ class Comments < UI::Container
3
+ # Comment footer
4
+ class Footer < UI::Container
5
+ # Tagname
6
+ tag 'ui-comment-footer'
7
+
8
+ # Style
9
+ style color: -> { dampen colors.background, 0.3 },
10
+ span: { opacity: 0.8 },
11
+ fontSize: 0.8.em,
12
+ '&[direction=row]:not([compact]) > * + *' => { marginLeft: -> { (theme.spacing / 2).em } },
13
+ 'ui-label' => { marginRight: -> { (theme.spacing / 2).em } },
14
+ 'ui-action' => { display: :flex, fontWeight: 600 }
15
+
16
+ # Components
17
+ component :vote_up, UI::Action, direction: :row, action: :vote_up do
18
+ component :label, UI::Label, text: 0
19
+ component :icon, UI::Icon, glyph: 'chevron-up'
20
+ end
21
+ component :span, :span, text: '|'
22
+ component :vote_down, UI::Action, action: :vote_down do
23
+ component :icon, UI::Icon, glyph: 'chevron-down'
24
+ end
25
+ component :span, :span, text: '|'
26
+ component :reply, UI::Action, text: 'Reply', action: :reply
27
+
28
+ # Renders the component
29
+ #
30
+ # @param data [Hash] The data
31
+ def render(data)
32
+ @vote_up.label.text = data[:votes]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ module Examples
2
+ class Comments < UI::Container
3
+ # Comment header
4
+ class Header < UI::Container
5
+ # Tagname
6
+ tag 'ui-comment-header'
7
+
8
+ # Style
9
+ style 'ui-time' => { fontSize: 0.85.em,
10
+ lineHeight: 1.5,
11
+ opacity: 0.4,
12
+ '&:before' => { content: '"-"',
13
+ marginRight: -> { (theme.spacing / 2).em } } },
14
+ '&[direction=row]:not([compact]) > * + *' => { marginLeft: -> { (theme.spacing / 2).em } },
15
+ 'ui-label' => { fontWeight: 600 },
16
+ 'ui-action' => { display: :none }
17
+
18
+ # Components
19
+ component :user, UI::Label
20
+ component :date, UI::Time, from_now: true
21
+ component :spacer, UI::Base, flex: 1
22
+ component :action, UI::Action, action: :confirm_destroy! do
23
+ component :icon, UI::Icon, glyph: 'android-close'
24
+ end
25
+
26
+ # Renders the component
27
+ #
28
+ # @param data [Hash] The data
29
+ def render(data)
30
+ @date.value = data[:date]
31
+ @user.text = data[:user][:name]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ module Examples
2
+ class Comments < UI::Container
3
+ # Comment List
4
+ class List < Collection
5
+ # Tagname
6
+ tag 'ui-comments-list'
7
+
8
+ # Styles
9
+ style '> * + *' => { marginTop: -> { (theme.spacing * 2).em } }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ require 'fron_ui'
2
+ require_tree './components'
3
+ comments = Examples::Comments.new
4
+ comments >> DOM::Document.body
5
+ comments.load
6
+ Fron::Sheet.render_style_tag
@@ -0,0 +1,100 @@
1
+ require_relative 'details'
2
+ require_relative 'sidebar'
3
+
4
+ module Examples
5
+ # Contacts example
6
+ class Contacts < UI::Container
7
+ include UI::Behaviors::Actions
8
+ include UI::Behaviors::Render
9
+ include UI::Behaviors::Rest
10
+ include UI::Behaviors::State
11
+
12
+ extend Forwardable
13
+
14
+ rest url: 'http://localhost:3000/contacts'
15
+
16
+ component :sidebar, Sidebar, flex: '0 0 20em'
17
+ component :details, Details, flex: 1
18
+
19
+ style fontSize: 14.px,
20
+ width: 67.5.em,
21
+ margin: '0 auto',
22
+ height: 57.5.em,
23
+ padding: -> { theme.spacing.em },
24
+ boxSizing: 'border-box'
25
+
26
+ render :render!
27
+
28
+ def_delegators :class, :storage
29
+
30
+ on :selected_change, :select
31
+ on :input, 'ui-sidebar input', :render
32
+ on :refresh, :refresh
33
+ on :destroyed, :destroyed
34
+
35
+ state_changed :state_changed
36
+
37
+ # Initializes the component
38
+ def initialize
39
+ super
40
+ @items = []
41
+ self[:direction] = :row
42
+ end
43
+
44
+ # Handles state change
45
+ def state_changed
46
+ load state
47
+ render!
48
+ end
49
+
50
+ # Adds a new contact
51
+ def add
52
+ data = {
53
+ id: SecureRandom.uuid
54
+ }
55
+
56
+ create data do |item|
57
+ refresh do
58
+ @sidebar.input.value = ''
59
+ render!
60
+ self.state = item[:id]
61
+ end
62
+ end
63
+ end
64
+
65
+ # Handles item selection from the list
66
+ def select
67
+ self.state = @sidebar.selected.data[:id]
68
+ end
69
+
70
+ # Loads the contact with the given id
71
+ #
72
+ # @param id [String] The ID
73
+ def load(id)
74
+ @details.load id
75
+ render!
76
+ end
77
+
78
+ # Handles the destroyed event
79
+ #
80
+ # @param event [DOM::Event] The event
81
+ def destroyed(event)
82
+ self.state = nil if event.target.data[:id] == state
83
+ refresh
84
+ end
85
+
86
+ # Refreshes the list
87
+ def refresh
88
+ all do |items|
89
+ @items = items
90
+ render { yield if block_given? }
91
+ end
92
+ end
93
+
94
+ # Renders the list
95
+ def render!
96
+ @sidebar.items = @items.select { |item| item[:name].to_s.match Regexp.new(@sidebar.input.value || '.*', 'i') }
97
+ @sidebar.select state
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,92 @@
1
+ module Examples
2
+ class Contacts < UI::Container
3
+ # Details
4
+ class Details < UI::Box
5
+ include UI::Behaviors::Confirmation
6
+ include UI::Behaviors::Serialize
7
+ include UI::Behaviors::Actions
8
+ include UI::Behaviors::Rest
9
+
10
+ tag 'ui-details'
11
+
12
+ rest url: 'http://localhost:3000/contacts'
13
+
14
+ style 'ui-image' => { margin: -> { theme.spacing.em },
15
+ height: 15.em,
16
+ width: 15.em },
17
+ 'ui-container' => { padding: -> { theme.spacing.em } },
18
+ 'ui-container ui-container' => { maxWidth: 30.em },
19
+ 'ui-label' => { fontWeight: 600 },
20
+ 'input' => { fontSize: 1.2.em },
21
+ 'ui-loader' => { fontSize: 2.em },
22
+ '&.empty' => {
23
+ '> ui-container, > ui-button' => {
24
+ display: :none
25
+ },
26
+ '&:after' => {
27
+ content: "'No contact selected!'",
28
+ padding: -> { (theme.spacing * 3).em },
29
+ textAlign: :center,
30
+ fontSize: 2.em,
31
+ opacity: 0.25
32
+ }
33
+ }
34
+
35
+ component :title, UI::Title, text: 'Contact Details'
36
+
37
+ component :box, UI::Container, flex: 1, direction: :row do
38
+ component :image, UI::Image
39
+ component :form, UI::Container, flex: 1 do
40
+ component :label, UI::Label, text: 'Full Name:'
41
+ component :input, UI::Input, placeholder: 'Tony Stark', name: :name
42
+ component :label, UI::Label, text: 'E-mail:'
43
+ component :input, UI::Input, placeholder: 'tony@stark-industries.com', name: :email
44
+ component :label, UI::Label, text: 'Address:'
45
+ component :input, UI::Input, placeholder: '10880 Malibu Point, Malibu, Calif', name: :address
46
+ component :label, UI::Label, text: 'Phone:'
47
+ component :input, UI::Input, placeholder: '+1-202-555-0160', name: :phone
48
+ end
49
+ end
50
+
51
+ component :button, UI::Button, type: 'danger', text: 'Remove Contact', action: :confirm_destroy!
52
+
53
+ confirmation :destroy!, 'Are you sure you want to remove this contact?'
54
+
55
+ on :change, :save
56
+
57
+ # Loads the contact with the given id
58
+ #
59
+ # @param id [String] The ID
60
+ def load(id)
61
+ return add_class :empty if id.empty?
62
+ request :get, id do |data|
63
+ break add_class :empty unless data
64
+ super data
65
+ remove_class :empty
66
+ render
67
+ end
68
+ end
69
+
70
+ # Saves the contact
71
+ def save
72
+ update data do
73
+ @data.merge! data
74
+ render
75
+ trigger :refresh
76
+ end
77
+ end
78
+
79
+ # Destroys the contact
80
+ def destroy!
81
+ destroy do
82
+ trigger :destroyed
83
+ end
84
+ end
85
+
86
+ # Renders the contact image
87
+ def render
88
+ @box.image.src = 'http://www.gravatar.com/avatar/' + `md5(#{data[:email] || ''})` + '?s=200&d=identicon'
89
+ end
90
+ end
91
+ end
92
+ end