dom-rb 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ require 'dom/content'
2
+ require 'dom/builder'
3
+ require 'dom/util'
4
+ require 'dom/debug'
5
+
6
+ module CSR
7
+ module DOM
8
+ class Table
9
+ class Caption
10
+ include CSR::DOM::Builder
11
+ include CSR::DOM::Debug
12
+
13
+ attr_reader :table, :content
14
+
15
+ def initialize(table, content)
16
+ # self.debug_level = 1
17
+ @table = table
18
+ @content = content
19
+ end
20
+
21
+ def update
22
+ if @root
23
+ debug 1, ->{[ __FILE__, __LINE__, __method__ ]}
24
+ animate do
25
+ @root.clear
26
+ @root << resolve_content
27
+ end
28
+ end
29
+ end
30
+
31
+ def root
32
+ debug 1, ->{[ __FILE__, __LINE__, __method__ ]}
33
+ @root ||= tag(
34
+ :caption,
35
+ attributes: @table.caption_attributes,
36
+ content: resolve_content
37
+ )
38
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@root=#{@root}" ]}
39
+ @root
40
+ end
41
+
42
+ def resolve_content
43
+ result = case content
44
+ when Content
45
+ content.element(:caption, attributes: attributes, context: @table)
46
+ when Proc
47
+ content.call
48
+ else
49
+ content
50
+ end
51
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "result=#{result}" ]}
52
+ result
53
+ end
54
+ end
55
+
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,84 @@
1
+ require 'dom/content'
2
+
3
+ module CSR
4
+ module DOM
5
+ class Table
6
+ class Column
7
+
8
+ attr_reader :id # unique in table, default to field
9
+
10
+ # Content procs with arity 2 should expect context to be [table, section, row]
11
+ # where table is a Table; section is :head, :body or :foot; row is an element in
12
+ # head_rows, body_rows or foot_rows.
13
+
14
+ attr_reader :head_content # Content or String or nil.
15
+ attr_reader :body_content # Content or String or nil.
16
+ attr_reader :foot_content # Content or String or nil.
17
+
18
+ # OPTIONS:
19
+ # :id # unique in table
20
+ # :sort # column may be sorted : boolean or nil
21
+ # :hide # column may be hidden : boolean or nil
22
+ # :master # column used for grouping in accordion table
23
+ # :head_content # Content or String or nil for column head
24
+ # :body_content # Content or String or nil for column body
25
+ # :foot_content # Content or String or nil for column foot
26
+
27
+ # TODO:
28
+ # width:
29
+
30
+ def initialize(**options)
31
+ @id = options[:id]
32
+ @sort_callback = options[:sort]
33
+ @hide = !!options[:hide]
34
+ @master = !!options[:master]
35
+ @head_content = options[:head_content]
36
+ @body_content = options[:body_content]
37
+ @foot_content = options[:foot_content]
38
+ end
39
+
40
+ def hash
41
+ @id.hash
42
+ end
43
+
44
+ def eql?(other)
45
+ @id == other.id
46
+ end
47
+
48
+ def ==(other)
49
+ @id == other.id
50
+ end
51
+
52
+ def master?
53
+ @master
54
+ end
55
+
56
+ def sort_callback
57
+ @sort_callback
58
+ end
59
+
60
+ def sort?
61
+ !!@sort_callback
62
+ end
63
+
64
+ def hide?
65
+ @hide
66
+ end
67
+
68
+ # section is :head, :body or :foot
69
+ def section_content(section)
70
+ send(:"#{section}_content")
71
+ end
72
+
73
+ # Returns a (probably) unique column id.
74
+ def self.unique_id
75
+ @@id_chars ||= ('0'..'9').to_a + ('A'..'F').to_a
76
+ result = 'COL_'
77
+ 8.times { result = result + @@id_chars.sample }
78
+ result
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,118 @@
1
+ require 'dom/table/column'
2
+ require 'dom/builder'
3
+ require 'dom/util'
4
+ require 'dom/debug'
5
+
6
+ module CSR
7
+ module DOM
8
+ class Table
9
+ class Row
10
+ include CSR::DOM::Builder
11
+ include CSR::DOM::Util
12
+ include CSR::DOM::Debug
13
+
14
+ attr_reader :table, :section, :index
15
+
16
+ def initialize(table, section, index)
17
+ # self.debug_level = 1
18
+ @table, @section, @index = table, section, index
19
+ end
20
+
21
+ def root
22
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@root=#{@root}" ]}
23
+ @root ||= tag(
24
+ :tr,
25
+ attributes: attributes,
26
+ content: cells
27
+ )
28
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@root=#{@root}" ]}
29
+ @root
30
+ end
31
+
32
+ def attributes
33
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@section_id=#{@section_id} @index=@{index}" ]}
34
+ result = section.row_attributes(@index)
35
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "result=#{result}" ]}
36
+ result
37
+ end
38
+
39
+ # columns may be indexes in table columns or Column's
40
+ def update(_columns = nil)
41
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "_columns=#{_columns}" ]}
42
+ if @root
43
+ animate do
44
+ if _columns
45
+ columns = _columns.map { |c| Integer === c ? @table.columns[c] : c }
46
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "columns=#{columns.map{|e|e.id}}" ]}
47
+ @table.visible_columns.each_with_index do |c, i|
48
+ if columns.include?(c)
49
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "updating column[#{i}] #{c.id}" ]}
50
+ new_cell = cell(c)
51
+ @cells[i].replace(new_cell)
52
+ @cells[i] = new_cell
53
+ end
54
+ end
55
+ else
56
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "updating all columns" ]}
57
+ @cells = nil
58
+ @root.clear
59
+ @root << cells
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ # Returns array of cell elements in table's visible column order
66
+ def cells
67
+ unless @cells
68
+ @cells = @table.visible_columns.map do |column|
69
+ cell(column)
70
+ end
71
+ end
72
+ @cells
73
+ end
74
+
75
+ def head?
76
+ @section.head?
77
+ end
78
+
79
+ def row_source
80
+ @section.row_source(@index)
81
+ end
82
+
83
+ def cell(column)
84
+ attributes, content = cell_attributes_content(column)
85
+ tag(
86
+ head? ? :th : :td,
87
+ attributes: attributes,
88
+ content: content
89
+ )
90
+ end
91
+
92
+ def cell_attributes_content(column)
93
+ content = column.section_content(@section.id)
94
+ attributes = nil
95
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "section.id=#{@section.id} row_index=#{@index} column.id=#{column.id} content=#{content}" ]}
96
+ if Content === content
97
+ content, attributes = content.value_and_attributes(context: row_source)
98
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "section.id=#{@section.id} row_index=#{@index} column.id=#{column.id} content=#{content} attributes=#{attributes}" ]}
99
+ end
100
+ if head? && column.sort?
101
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "section.id=#{@section.id} row_index=#{@index} column.id=#{column.id} content=#{content}" ]}
102
+ content = div_with_sort_icon(
103
+ column.sort_callback,
104
+ direction: @table.sort_column_id == column.id ? @table.sort_order : 0,
105
+ content: content
106
+ )
107
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "section.id=#{@section.id} column.id=#{column.id} content=#{content}" ]}
108
+ end
109
+ attributes ||= {}
110
+ attributes[:id] ||= hex_id
111
+ [attributes, content]
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,208 @@
1
+ require 'dom/table/row'
2
+ require 'dom/builder'
3
+ require 'dom/util'
4
+ require 'dom/debug'
5
+
6
+ module CSR
7
+ module DOM
8
+ class Table
9
+ class Section
10
+ include CSR::DOM::Builder
11
+ include CSR::DOM::Util
12
+ include CSR::DOM::Debug
13
+
14
+ attr_reader :table, :id
15
+
16
+ def initialize(table, id)
17
+ # self.debug_level = 1
18
+ @table, @id = table, id
19
+ end
20
+
21
+ def head?
22
+ @id == :head
23
+ end
24
+
25
+ def foot?
26
+ @id == :foot
27
+ end
28
+
29
+ def body?
30
+ @id == :body
31
+ end
32
+
33
+ def update
34
+ if @root
35
+ animate do
36
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} @root=#{@root}" ]}
37
+ @row_sources = @row_indexes = @rows = nil
38
+ @root.clear
39
+ @root << content
40
+ end
41
+ else
42
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} @root=nil" ]}
43
+ end
44
+ end
45
+
46
+ def row_attributes(index)
47
+ # TODO: implement
48
+ end
49
+
50
+ # source should be source (unsorted) index or row source (model)
51
+ # columns may be column indexes or Column's
52
+ def update_row(source, _columns = nil)
53
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "source=#{source} _columns=#{_columns} @rows.size=#{@rows ? @rows.size : 'nil'}" ]}
54
+ if @rows
55
+ columns = if _columns
56
+ _columns.map { |c| Integer === c ? @table.columns[c] : c }
57
+ end
58
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "source=#{source} columns=#{columns}" ]}
59
+ if @id == :body && (columns.nil? || (@table.sort_column && columns.include?(@table.sort_column)))
60
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "updating whole section" ]}
61
+ update
62
+ else
63
+ row_index = Integer === source ? source : source_index(source)
64
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "calling @rows[#{row_index}].update(#{columns})" ]}
65
+ @rows[row_index].update(columns)
66
+ end
67
+ end
68
+ end
69
+
70
+ # source should be source (unsorted) index or row source (model)
71
+ # column may be visible column index or a Column
72
+ def update_cell(source, column)
73
+ update_row(source, [column])
74
+ end
75
+
76
+ def root
77
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id}" ]}
78
+ @root ||= tag(
79
+ "t#{@id}",
80
+ attributes: attributes,
81
+ content: content
82
+ )
83
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} @root=#{@root}" ]}
84
+ @root
85
+ end
86
+
87
+ def empty?
88
+ row_sources.size == 0
89
+ end
90
+
91
+ def attributes
92
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id}" ]}
93
+ result = table.section_attributes(@id)
94
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} result=#{result}" ]}
95
+ result
96
+ end
97
+
98
+ def content
99
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id}" ]}
100
+ result = rows.map { |row| row.root }
101
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} result=#{result}" ]}
102
+ result
103
+ end
104
+
105
+ # Returns array of Table::Row's (in table sort order)
106
+ def rows
107
+ unless @rows
108
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id}" ]}
109
+ @rows = []
110
+ row_indexes.each do |model_index|
111
+ @rows << Row.new(@table, self, model_index)
112
+ end
113
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} @rows=#{@rows.map(&:index)}}" ]}
114
+ end
115
+ @rows
116
+ end
117
+
118
+ def row_source(index)
119
+ row_sources[index]
120
+ end
121
+
122
+ def source_index(source)
123
+ row_sources.index(source)
124
+ end
125
+
126
+ def row_sources
127
+ unless @row_sources
128
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id}" ]}
129
+ source = @table.row_source(@id)
130
+ @row_sources = case source
131
+ when Fixnum
132
+ source.times.to_a
133
+ when Proc
134
+ source.call
135
+ when NilClass
136
+ []
137
+ else
138
+ source
139
+ end
140
+ # if body?
141
+ # debug 1, ->{[ __FILE__, __LINE__, __method__, "@id=#{@id} @row_sources=#{@row_sources.map(&:code)}" ]}
142
+ # end
143
+ end
144
+ @row_sources
145
+ end
146
+
147
+ # Returns array of indexes to row models
148
+ # in sorted display order.
149
+ def row_indexes
150
+ unless @row_indexes
151
+ models = row_sources
152
+ @row_indexes = models.size.times.to_a
153
+ if @id == :body && @table.sorted?
154
+ order = @table.sort_order
155
+ sort_column = @table.sort_column
156
+ @row_indexes = @row_indexes.sort do |a, b|
157
+ val_a = sort_value(sort_column, models[a])
158
+ val_b = sort_value(sort_column, models[b])
159
+ # debug 2, ->{[__FILE__, __LINE__, __method__, "a=#{val_a} b=#{val_b} order=#{order}"]}
160
+ compare(val_a, val_b) * order
161
+ end
162
+ end
163
+ end
164
+ @row_indexes
165
+ end
166
+
167
+ # Returns the display index for an unsorted row index or row model
168
+ def row_display_index(row)
169
+ sources = row_sources
170
+ if Integer === row
171
+ return row_index.index(row)
172
+ else
173
+ row_indexes.each_with_index do |source_index, display_index|
174
+ if row == sources[source_index]
175
+ return display_index
176
+ end
177
+ end
178
+ end
179
+ nil
180
+ end
181
+
182
+ private
183
+
184
+ def sort_value(column, row_source)
185
+ content = column.section_content(@id)
186
+ result = if Content === content
187
+ content.sort_value(context: row_source)
188
+ else
189
+ content
190
+ end
191
+ result
192
+ end
193
+
194
+ def compare(a, b)
195
+ if a && b
196
+ a <=> b
197
+ elsif a
198
+ 1
199
+ elsif b
200
+ -1
201
+ else
202
+ 0
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
data/csr/dom/table.rb ADDED
@@ -0,0 +1 @@
1
+ require 'dom/table/base'
data/csr/dom/util.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'opal-browser'
2
+ require 'browser/animation_frame'
3
+
4
+ module CSR
5
+ module DOM
6
+ module Util
7
+ module_function
8
+
9
+ def set_interval(milliseconds, callback)
10
+ `setInterval(callback, milliseconds)`
11
+ end
12
+
13
+ def clear_interval(interval)
14
+ `clearInterval(interval)` if interval
15
+ end
16
+
17
+ def cursor(which, &block)
18
+ current = $document.body.style.cursor
19
+ $document.body.style.cursor = which
20
+ if block
21
+ yield
22
+ $document.body.style.cursor = current
23
+ end
24
+ end
25
+
26
+ def cursor_wait(&block)
27
+ cursor('wait', &block)
28
+ end
29
+
30
+ def cursor_auto(&block)
31
+ cursor('auto', &block)
32
+ end
33
+
34
+ def cursor_normal(&block)
35
+ cursor_auto(&block)
36
+ end
37
+
38
+ def animate(&block)
39
+ # Browser::AnimationFrame.new(window, &block)
40
+ animation_frame(&block)
41
+ end
42
+
43
+ # Returns a (probably) unique element id
44
+ # composed of hexadecimal digits.
45
+ def hex_id
46
+ @@hex_digits ||= ('0'..'9').to_a + ('A'..'F').to_a
47
+ result = ''
48
+ 8.times { result = result + @@hex_digits.sample }
49
+ result
50
+ end
51
+
52
+ # make a single array from args
53
+ # args may be mix of non-enumerables and enumerables
54
+ # nils will not be included
55
+ def arrify(*args)
56
+ result = []
57
+ args.each do |arg|
58
+ if arg
59
+ if Enumerable === arg
60
+ arg.each do |e|
61
+ result << e if e
62
+ end
63
+ else
64
+ result << arg if arg
65
+ end
66
+ end
67
+ end
68
+ result
69
+ end
70
+
71
+ def normalize_style(style)
72
+ unless style.respond_to?(:to_h)
73
+ raise TypeError, "#{__FILE__}[#{__LINE__}] : style #{style.class} must respond to :to_h"
74
+ end
75
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "style=#{style}" ]}
76
+ result = {}
77
+ style.to_h.each do |k,v|
78
+ k = k.to_s.gsub('_', '-')
79
+ result[k] = v.to_s
80
+ end
81
+ debug 1, ->{[ __FILE__, __LINE__, __method__, "result=#{result}" ]}
82
+ result
83
+ end
84
+ end
85
+ end
86
+ end
data/csr/dom.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'dom/builder'
2
+ require 'dom/factory'
3
+ require 'dom/content'
4
+ require 'dom/table'
5
+ require 'dom/util'
@@ -0,0 +1,5 @@
1
+ module CSR
2
+ module DOM
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
data/lib/dom-rb.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'opal'
2
+ require 'paggio'
3
+ require 'opal-browser'
4
+
5
+ module CSR
6
+ module DOM
7
+ require_relative "dom/version"
8
+ end
9
+ end
10
+
11
+ Opal.append_path(File.expand_path(File.join("..", "..", "csr"), __FILE__).untaint)
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dom-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Colin Gunn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: opal-browser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: DOM management in client-side Ruby.
28
+ email:
29
+ - colgunn@icloud.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - csr/dom.rb
35
+ - csr/dom/browser_ext/node.rb
36
+ - csr/dom/builder.rb
37
+ - csr/dom/builder/base.rb
38
+ - csr/dom/builder/binding.rb
39
+ - csr/dom/builder/helpers.rb
40
+ - csr/dom/content.rb
41
+ - csr/dom/debug.rb
42
+ - csr/dom/globals.rb
43
+ - csr/dom/root.rb
44
+ - csr/dom/table.rb
45
+ - csr/dom/table/base.rb
46
+ - csr/dom/table/caption.rb
47
+ - csr/dom/table/column.rb
48
+ - csr/dom/table/row.rb
49
+ - csr/dom/table/section.rb
50
+ - csr/dom/util.rb
51
+ - lib/dom-rb.rb
52
+ - lib/dom/version.rb
53
+ homepage: https://github.com/balmoral/dom-rb
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.4.6
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: DOM management in Ruby
77
+ test_files: []
78
+ has_rdoc: