dom-rb 0.1.1

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.
@@ -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: