drawio_dsl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6af520857fe09be7e990c254d75364ae3c4f145e77beb8aaf1850c7e99954666
4
- data.tar.gz: ed9f9debe4437765f426cb873cc226eb8721a89b2be257a30345122d59a25217
3
+ metadata.gz: 96f9176e2e3f9b37f0a02f462d075835aae641a43d14dc45c747a1434fcb17df
4
+ data.tar.gz: 07ef19170c1721bb9ebe7a58d9cc6d20ca000d620b555941c2e396d83a8b27e0
5
5
  SHA512:
6
- metadata.gz: 13a320ccac8daefe68036dafe8982451e28cb1ba56dd0be1b22694106fee82aa4625e8eee214a8a0cecdff476a5b2b56b90b45a6bbf8df82c1a315d36ea18a57
7
- data.tar.gz: 912a578341300d29b610fb053b5d4d98a161e7be9c1decd65b72af0398305a216d785245572b020b7463ad708ed74d6f7ddf936708175337eb6834f92611dcd8
6
+ metadata.gz: ccb338c21e1a14d55e0fb7965c05c014714e37081a1b2f365ea01168017d15327043255a23346dde6c7674419c5458ea9516411240108e5e5c4cb688b9210a9f
7
+ data.tar.gz: ab033223b347fa65e1e6e2ddb91d93085276052dd21685d5bd76db0c9cc0f92c1c0a2e5cade339f4e1f7a67569ee42153249d61ec6f15bdb8401e7b52d25d57b
@@ -5,9 +5,22 @@ KManager.action :bootstrap do
5
5
  on_exist: :skip, # %i[skip write compare]
6
6
  on_action: :queue # %i[queue execute]
7
7
  )
8
+ .diagram(theme: :style_01)
9
+ .page('Grid-Right', page_shadow: 1) do
10
+ grid_layout(wrap_at: 3)
11
+ square(title: 'Square')
12
+ rectangle(title: 'Rectangle')
13
+ rectangle(title: 'Rectangle (Rounded)', rounded: 1)
14
+ circle(title: 'Circle')
15
+ process(title: 'Process')
16
+ ellipse(title: 'Ellipse')
17
+ end
8
18
 
9
- File.write('spec/.samples/drawio/10-layout.xml', director.build)
10
- File.write('spec/.samples/drawio/10-layout.drawio', director.build)
19
+ diagram = DrawioDsl::XmlBuilder.new(director.builder.diagram)
20
+
21
+
22
+ File.write('../spec/.samples/drawio/10-layout.xml', diagram.build)
23
+ File.write('../spec/.samples/drawio/10-layout.drawio', diagram.build)
11
24
  end
12
25
  end
13
26
 
data/.rubocop.yml CHANGED
@@ -67,6 +67,10 @@ Naming/VariableNumber:
67
67
  Naming/MethodParameterName:
68
68
  AllowedNames:
69
69
  - as
70
+ - x
71
+ - y
72
+ - w
73
+ - h
70
74
  Style/EmptyMethod:
71
75
  Exclude:
72
76
  - "**/spec/**/*"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # [0.1.0](https://github.com/klueless-io/drawio_dsl/compare/v0.0.1...v0.1.0) (2022-03-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * initial draw-io dsl ([ac197c8](https://github.com/klueless-io/drawio_dsl/commit/ac197c88b66b883b5fcce93090c285bc9ccdb7a3))
7
+ * initial draw-io dsl ([12c8b98](https://github.com/klueless-io/drawio_dsl/commit/12c8b98392e5f7da598cf2f9c375f1ca7041533b))
8
+ * initial draw-io dsl ([6ef32ad](https://github.com/klueless-io/drawio_dsl/commit/6ef32adcb1287d9993ebe30c437326716357a08e))
9
+ * initial draw-io dsl ([16827b5](https://github.com/klueless-io/drawio_dsl/commit/16827b57767d12c387167b86f17b33115d1d0378))
10
+
1
11
  ## [Unreleased]
2
12
 
3
13
  ## [0.1.0] - 2022-02-25
@@ -17,16 +17,16 @@ module DrawioDsl
17
17
 
18
18
  BaseStyle = Struct.new(:white_space, :html, :rounded, :shadow, :sketch, :glass, keyword_init: true)
19
19
 
20
- ElementDefaults = Struct.new(:type, :x, :y, :w, :h, :style_modifiers)
21
- ElementThemeStyle = Struct.new(:fill_color, :stroke_color, :font_color, :gradient, keyword_init: true)
20
+ ShapeDefaults = Struct.new(:type, :x, :y, :w, :h, :style_modifiers)
21
+ ShapeThemeStyle = Struct.new(:fill_color, :stroke_color, :font_color, :gradient, keyword_init: true)
22
22
 
23
23
  attr_accessor :base_style
24
24
 
25
25
  # Theme colors
26
26
  attr_accessor :themes
27
27
 
28
- # Element shapes
29
- attr_accessor :element
28
+ # Shape shapes
29
+ attr_accessor :shape
30
30
  attr_accessor :square
31
31
  attr_accessor :rectangle
32
32
  attr_accessor :circle
@@ -39,13 +39,13 @@ module DrawioDsl
39
39
  def initialize
40
40
  @base_style = BaseStyle.new(white_space: :wrap, html: 1, rounded: nil, shadow: nil, sketch: nil, glass: nil)
41
41
 
42
- @element = ElementDefaults.new(:element , 0, 0, 20, 20, '')
43
- @square = ElementDefaults.new(:square , 0, 0, 160, 160, '')
44
- @rectangle = ElementDefaults.new(:rectangle , 0, 0, 240, 120, '')
45
- @circle = ElementDefaults.new(:circle , 0, 0, 160, 160, 'ellipse')
46
- @process = ElementDefaults.new(:process , 0, 0, 240, 120, 'shape=process')
47
- @ellipse = ElementDefaults.new(:ellipse , 0, 0, 240, 120, 'ellipse')
48
- @triangle = ElementDefaults.new(:triangle , 0, 0, 160, 160, 'rhombus')
42
+ @shape = ShapeDefaults.new(:shape , 0, 0, 20, 20, '')
43
+ @square = ShapeDefaults.new(:square , 0, 0, 160, 160, '')
44
+ @rectangle = ShapeDefaults.new(:rectangle , 0, 0, 200, 120, '')
45
+ @circle = ShapeDefaults.new(:circle , 0, 0, 160, 160, 'ellipse')
46
+ @process = ShapeDefaults.new(:process , 0, 0, 200, 120, 'shape=process')
47
+ @ellipse = ShapeDefaults.new(:ellipse , 0, 0, 200, 120, 'ellipse')
48
+ @triangle = ShapeDefaults.new(:triangle , 0, 0, 160, 160, 'rhombus')
49
49
 
50
50
  @themes = {}
51
51
  add_themes
@@ -62,7 +62,7 @@ module DrawioDsl
62
62
  private
63
63
 
64
64
  def add_theme(name, **opts)
65
- @themes[name] = ElementThemeStyle.new(**opts)
65
+ @themes[name] = ShapeThemeStyle.new(**opts)
66
66
  end
67
67
 
68
68
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
@@ -7,7 +7,8 @@ module DrawioDsl
7
7
  attr_reader :last_action
8
8
 
9
9
  attr_reader :current_page
10
- attr_reader :current_element
10
+ attr_reader :current_layout_rule
11
+ attr_reader :current_shape
11
12
 
12
13
  def initialize
13
14
  @actions = []
@@ -18,7 +19,7 @@ module DrawioDsl
18
19
  @actions = []
19
20
  @last_action = {}
20
21
  set_diagram
21
- set_layout_engine
22
+ # set_layout_engine
22
23
  end
23
24
 
24
25
  def queue_action(action)
@@ -26,16 +27,6 @@ module DrawioDsl
26
27
  @last_action = action
27
28
  end
28
29
 
29
- def set_layout_engine(**opts)
30
- @layout_engine = DrawioDsl::LayoutEngine.new(**opts)
31
- end
32
-
33
- def layout_engine
34
- return @layout_engine if defined? @layout_engine
35
-
36
- set_layout_engine
37
- end
38
-
39
30
  def set_diagram(**opts)
40
31
  @diagram = DrawioDsl::Schema::Diagram.new(**opts)
41
32
  end
@@ -55,40 +46,67 @@ module DrawioDsl
55
46
  @current_page
56
47
  end
57
48
 
58
- def add_element(element)
59
- @current_element = element
49
+ # ----------------------------------------------------------------------
50
+ # Layout provides rules for positioning components
51
+ # ----------------------------------------------------------------------
52
+
53
+ def add_grid_layout(**opts)
54
+ rule = DrawioDsl::Schema::GridLayout.new(current_page, **opts)
55
+ add_layout_rule(rule)
56
+ end
57
+
58
+ def add_flex_layout(**opts)
59
+ rule = DrawioDsl::Schema::FlexLayout.new(current_page, **opts)
60
+ add_layout_rule(rule)
61
+ end
62
+
63
+ def add_layout_rule(rule)
64
+ @current_layout_rule = rule
65
+
66
+ rule.id = "rule-#{current_page.nodes.length + 1}" unless rule.id
67
+
68
+ current_page.nodes << rule
69
+
70
+ rule
71
+ end
72
+
73
+ # ----------------------------------------------------------------------
74
+ # Shapes represent visual components
75
+ # ----------------------------------------------------------------------
76
+
77
+ def add_shape(shape)
78
+ @current_shape = shape
60
79
 
61
- element.id = "#{current_page.id}-#{current_page.elements.length + 1}" unless element.id
80
+ shape.id = "#{current_page.id}-#{current_page.nodes.length + 1}" unless shape.id
62
81
 
63
- layout_engine.container.place_element(element)
82
+ current_page.nodes << shape
64
83
 
65
- current_page.elements << element
66
- @current_element
84
+ shape
67
85
  end
68
86
 
69
87
  def add_square(**opts)
70
88
  square = DrawioDsl::Schema::Square.new(current_page, **opts)
71
- add_element(square)
89
+ add_shape(square)
72
90
  end
73
91
 
74
92
  def add_rectangle(**opts)
75
93
  rectangle = DrawioDsl::Schema::Rectangle.new(current_page, **opts)
76
- add_element(rectangle)
94
+ add_shape(rectangle)
77
95
  end
78
96
 
79
97
  def add_circle(**opts)
80
98
  circle = DrawioDsl::Schema::Circle.new(current_page, **opts)
81
- add_element(circle)
99
+ add_shape(circle)
82
100
  end
83
101
 
84
102
  def add_process(**opts)
85
103
  process = DrawioDsl::Schema::Process.new(current_page, **opts)
86
- add_element(process)
104
+ add_shape(process)
87
105
  end
88
106
 
89
107
  def add_ellipse(**opts)
90
108
  ellipse = DrawioDsl::Schema::Ellipse.new(current_page, **opts)
91
- add_element(ellipse)
109
+ add_shape(ellipse)
92
110
  end
93
111
 
94
112
  def debug
@@ -5,12 +5,6 @@ module DrawioDsl
5
5
  class Drawio < KDirector::Directors::BaseDirector
6
6
  default_builder_type(DrawioDsl::DomBuilder)
7
7
 
8
- def layout(**opts)
9
- builder.set_layout_engine(**opts)
10
-
11
- self
12
- end
13
-
14
8
  def diagram(**opts)
15
9
  builder.set_diagram(**opts)
16
10
 
@@ -23,6 +17,13 @@ module DrawioDsl
23
17
 
24
18
  self
25
19
  end
20
+
21
+ def apply_layout
22
+ engine = DrawioDsl::LayoutEngine.new(builder.diagram)
23
+ engine.call
24
+
25
+ self
26
+ end
26
27
  end
27
28
 
28
29
  # DrawioDsl::DrawioPage is created when you call .page on the draw-io DSL.
@@ -41,63 +42,42 @@ module DrawioDsl
41
42
  builder.add_page(**opts)
42
43
  end
43
44
 
45
+ def layout_rule(**opts)
46
+ builder.add_layout_rule(**opts)
47
+ end
48
+
49
+ def grid_layout(**opts)
50
+ builder.add_grid_layout(**opts)
51
+ end
52
+
53
+ def flex_layout(**opts)
54
+ builder.add_flex_layout(**opts)
55
+ end
56
+
44
57
  def square(**opts)
45
- opts = attach_xy(**opts)
46
58
  builder.add_square(**opts)
47
- update_layout
48
59
  end
49
60
 
50
61
  def rectangle(**opts)
51
- opts = attach_xy(**opts)
52
62
  builder.add_rectangle(**opts)
53
- update_layout
54
63
  end
55
64
 
56
65
  def circle(**opts)
57
- opts = attach_xy(**opts)
58
66
  builder.add_circle(**opts)
59
- update_layout
60
67
  end
61
68
 
62
69
  def process(**opts)
63
- opts = attach_xy(**opts)
64
70
  builder.add_process(**opts)
65
- update_layout
66
71
  end
67
72
 
68
73
  def ellipse(**opts)
69
- opts = attach_xy(**opts)
70
74
  builder.add_ellipse(**opts)
71
- update_layout
72
75
  end
73
76
 
74
77
  private
75
78
 
76
- def current_element
77
- builder.current_element
78
- end
79
-
80
- # attr_reader :layout_engine
81
- # set_layout
82
-
83
- # layout.container.place_element(element)
84
-
85
- def update_layout
86
- return unless auto_layout
87
-
88
- # puts current_element.page.x
89
- # puts current_element.page.y
90
- # puts current_element.page.w
91
- # puts current_element.page.h
92
-
93
- @current_x += current_element.w + 20
94
- @current_y += current_element.h + 20
95
- end
96
-
97
- def attach_xy(**opts)
98
- opts[:x] ||= current_x
99
- opts[:y] ||= current_y
100
- opts
79
+ def current_shape
80
+ builder.current_shape
101
81
  end
102
82
  end
103
83
  end
@@ -9,39 +9,51 @@ module DrawioDsl
9
9
  # Elements will be placed on the page in the order they are added.
10
10
  # Row/column flow objects will hold information about horizontal and vertical element padding
11
11
  class LayoutEngine
12
- attr_accessor :margin_left
13
- attr_accessor :margin_top
14
- attr_accessor :x
15
- attr_accessor :y
16
-
17
- def initialize(**opts)
18
- @margin_left = opts[:margin_left] || 20
19
- @margin_top = opts[:margin_top] || 20
20
- @x = opts[:x] || @margin_left
21
- @y = opts[:y] || @margin_top
12
+ attr_reader :diagram
13
+
14
+ attr_reader :current_page
15
+ attr_reader :current_layout
16
+ attr_reader :page_margin_top
17
+ attr_reader :page_margin_left
18
+ attr_reader :x
19
+ attr_reader :y
20
+
21
+ def initialize(diagram)
22
+ @diagram = diagram
23
+ @x = 0
24
+ @y = 0
22
25
  end
23
26
 
24
- def container
25
- @container ||= LayoutContainer.new(self)
26
- end
27
-
28
- def go_vertical(**opts)
29
- @container = LayoutContainer.new(self, direction: :vertical, **opts)
30
- end
27
+ def call
28
+ diagram.pages.each do |page|
29
+ focus_on_page(page)
31
30
 
32
- def go_horizontal(**opts)
33
- @container = LayoutContainer.new(self, direction: :horizontal, **opts)
31
+ apply_layout_to_page(page)
32
+ end
34
33
  end
35
34
 
36
- # # Position the incoming element by altering it's x, y coordinates based on the rules engine
37
- # def position_element(element)
38
- # container.horizontal?
39
- # end
35
+ private
40
36
 
41
- # private
37
+ def focus_on_page(page)
38
+ @current_page = page
39
+ page.position_x = page.margin_left
40
+ page.position_y = page.margin_top
41
+ end
42
42
 
43
- # def position_horizontally(element)
43
+ def apply_layout_to_page(page)
44
+ page.nodes.each do |node|
45
+ case node.classification
46
+ when :layout_rule
47
+ @current_layout = node
48
+ when :shape
49
+ current_layout.position_shape(node)
50
+ end
51
+ node.debug(format: :row)
52
+ end
53
+ end
44
54
 
45
- # end
55
+ def define_layout_rule(layout_rule)
56
+ layout_rule.debug
57
+ end
46
58
  end
47
59
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  module DrawioDsl
4
4
  module Schema
5
- # Common Style is the reused on Diagram, Page and Element
5
+ # Common Style is the reused on Diagram, Page and Shape
6
6
  #
7
7
  # When styles are not provided at each level, then they will inherit from
8
8
  # the parent common style.
9
9
  #
10
- # Elements will use the common style of their page
10
+ # Shapes will use the common style of their page
11
11
  class CommonStyle
12
12
  attr_accessor :theme
13
13
  attr_accessor :white_space
@@ -76,7 +76,7 @@ module DrawioDsl
76
76
 
77
77
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
78
78
  def initialize(**args)
79
- @host = args[:host] || SecureRandom.alphanumeric(3)
79
+ @host = args[:host] || SecureRandom.alphanumeric(3)
80
80
 
81
81
  # Apply a random theme to the diagram if none is specified.
82
82
  @theme = args[:theme] || KConfig.configuration.drawio.random_theme
@@ -85,22 +85,22 @@ module DrawioDsl
85
85
  default_style = KConfig.configuration.drawio.base_style
86
86
 
87
87
  # Inherit from configured style when specific style not specified.
88
- @white_space ||= default_style.white_space
89
- @html ||= default_style.html
90
- @rounded ||= default_style.rounded
91
- @shadow ||= default_style.shadow
92
- @sketch ||= default_style.sketch
93
- @glass ||= default_style.glass
88
+ @white_space ||= default_style.white_space
89
+ @html ||= default_style.html
90
+ @rounded ||= default_style.rounded
91
+ @shadow ||= default_style.shadow
92
+ @sketch ||= default_style.sketch
93
+ @glass ||= default_style.glass
94
94
  end
95
95
 
96
96
  @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |diagram|
97
97
  theme_palette = KConfig.configuration.drawio.palette(diagram.theme)
98
98
 
99
99
  # Inherit from theme when specific palette options are not specified.
100
- @fill_color ||= theme_palette.fill_color
100
+ @fill_color ||= theme_palette.fill_color
101
101
  @stroke_color ||= theme_palette.stroke_color
102
- @font_color ||= theme_palette.font_color
103
- @gradient ||= theme_palette.gradient
102
+ @font_color ||= theme_palette.font_color
103
+ @gradient ||= theme_palette.gradient
104
104
  end
105
105
 
106
106
  @pages = args[:pages] || []
@@ -118,16 +118,22 @@ module DrawioDsl
118
118
  end
119
119
  end
120
120
 
121
- # Page is a container for elements
121
+ # Page is a container for nodes
122
122
  class Page
123
123
  attr_accessor :diagram
124
124
 
125
+ # These transient attributes hold the current x, y location for the last element added to the page
126
+ attr_accessor :position_x
127
+ attr_accessor :position_y
128
+
125
129
  attr_accessor :id
126
130
  attr_accessor :name
127
131
  attr_accessor :theme
128
132
  attr_accessor :style
129
133
  attr_accessor :palette
130
- attr_accessor :elements
134
+ attr_accessor :margin_left
135
+ attr_accessor :margin_top
136
+ attr_accessor :nodes
131
137
 
132
138
  # attr_accessor :dx # dx = "2636"
133
139
  # attr_accessor :dy # dy = "2332"
@@ -150,9 +156,14 @@ module DrawioDsl
150
156
  def initialize(diagram, **args)
151
157
  @diagram = diagram
152
158
 
159
+ @position_x = 0
160
+ @position_y = 0
161
+
153
162
  @id = args[:id]
154
163
  @name = args[:name]
155
164
  @theme = args[:theme] || diagram.theme
165
+ @margin_left = args[:margin_left] || 50
166
+ @margin_top = args[:margin_top] || 50
156
167
 
157
168
  @grid = args[:grid] || 0
158
169
  @grid_size = args[:grid_size] || 10
@@ -171,25 +182,25 @@ module DrawioDsl
171
182
 
172
183
  @style = DrawioDsl::Schema::CommonStyle.new(**args) do
173
184
  # Inherit from the diagram style when specific style not specified.
174
- @white_space ||= diagram.style.white_space
175
- @html ||= diagram.style.html
176
- @rounded ||= diagram.style.rounded
177
- @shadow ||= diagram.style.shadow
178
- @sketch ||= diagram.style.sketch
179
- @glass ||= diagram.style.glass
185
+ @white_space ||= diagram.style.white_space
186
+ @html ||= diagram.style.html
187
+ @rounded ||= diagram.style.rounded
188
+ @shadow ||= diagram.style.shadow
189
+ @sketch ||= diagram.style.sketch
190
+ @glass ||= diagram.style.glass
180
191
  end
181
192
 
182
193
  @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |page|
183
194
  theme_palette = KConfig.configuration.drawio.palette(page.theme)
184
195
 
185
196
  # Inherit from theme when specific palette options are not specified.
186
- @fill_color ||= theme_palette.fill_color
197
+ @fill_color ||= theme_palette.fill_color
187
198
  @stroke_color ||= theme_palette.stroke_color
188
- @font_color ||= theme_palette.font_color
189
- @gradient ||= theme_palette.gradient
199
+ @font_color ||= theme_palette.font_color
200
+ @gradient ||= theme_palette.gradient
190
201
  end
191
202
 
192
- @elements = args[:elements] || []
203
+ @nodes = args[:nodes] || []
193
204
  end
194
205
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
195
206
 
@@ -200,15 +211,254 @@ module DrawioDsl
200
211
  theme: theme,
201
212
  palette: palette.to_h,
202
213
  style: style.to_h,
203
- elements: elements.map(&:to_h)
214
+ margin_left: margin_left,
215
+ margin_top: margin_top,
216
+ position_x: position_x,
217
+ position_y: position_y,
218
+ nodes: nodes.map(&:to_h)
204
219
  }
205
220
  end
206
221
  end
207
222
 
208
- # Element is a graphical element, it can be a shape, a text, or a group (todo)
209
- class Element
223
+ # Node is a base for shapes, connections, positioners and layout rules
224
+ class Node
225
+ attr_accessor :id
210
226
  attr_accessor :page
227
+ attr_accessor :classification
228
+
229
+ def initialize(page, **args)
230
+ @page = page
231
+ @id = args[:id]
232
+ @classification = args[:classification] || :unknown
233
+ end
234
+
235
+ def to_h
236
+ {
237
+ id: id,
238
+ classification: classification
239
+ }
240
+ end
241
+
242
+ def debug(format: :detail)
243
+ if format == :detail
244
+ debug_detail(to_h)
245
+ else
246
+ debug_row(classification, id)
247
+ end
248
+ end
249
+
250
+ def debug_detail(**key_values)
251
+ key_values.each do |key, value|
252
+ puts "#{key.to_s.ljust(15)}: #{value}"
253
+ end
254
+ end
255
+
256
+ # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
257
+ def debug_row(classification, id, type = nil, x = nil, y = nil, width = nil, height = nil)
258
+ row = []
259
+ row << classification.to_s.ljust(11)
260
+ row << id.to_s.ljust(6)
261
+ row << (type.nil? ? '' : type).to_s.ljust(15)
262
+ row << (x.nil? ? '' : x).to_s.rjust(5)
263
+ row << (y.nil? ? '' : y).to_s.rjust(5)
264
+ row << (width.nil? ? '' : width).to_s.rjust(5)
265
+ row << (height.nil? ? '' : height).to_s.rjust(5)
266
+ puts row.join(' | ')
267
+ end
268
+ # rubocop:enable Metrics/AbcSize, Metrics/ParameterLists
269
+ end
270
+
271
+ # Provides base configuration for automatic layouts
272
+ class LayoutRule < Node
273
+ attr_accessor :type
274
+
275
+ # represents the x coordinate of the top left corner layout area
276
+ # this coordinate is based on the current location of the page
277
+ attr_accessor :anchor_x
278
+ attr_accessor :anchor_y
279
+
280
+ def initialize(page, **args)
281
+ @after_init_fired = false
282
+
283
+ super(page, **args.merge(classification: :layout_rule))
284
+ end
285
+
286
+ def fire_after_init
287
+ return if @after_init_fired
288
+
289
+ @after_init_fired = true
290
+ after_init
291
+ end
292
+
293
+ def after_init
294
+ @anchor_x ||= page.position_x
295
+ @anchor_y ||= page.position_y
296
+ end
297
+
298
+ def to_h
299
+ super.merge(
300
+ type: type,
301
+ anchor_x: anchor_x,
302
+ anchor_y: anchor_y
303
+ )
304
+ end
305
+ end
306
+
307
+ # Provides grid style layouts
308
+ class GridLayout < LayoutRule
309
+ attr_accessor :direction
310
+ attr_accessor :wrap_at
311
+ attr_accessor :grid_size
312
+ attr_accessor :cell_no
313
+ attr_accessor :h_align
314
+ attr_accessor :v_align
315
+
316
+ def initialize(page, **args)
317
+ @type = :grid_layout
318
+ @direction = args[:direction] || :horizontal
319
+ @wrap_at = args[:wrap_at] || 5
320
+ @grid_size = args[:grid_size] || 220
321
+ @h_align = args[:h_align] || :center
322
+ @v_align = args[:v_align] || :center
323
+ @cell_no = 1
324
+
325
+ super(page, **args)
326
+ end
327
+
328
+ def position_shape(shape)
329
+ fire_after_init
211
330
 
331
+ shape.x = horizontal_shape_alignment(shape)
332
+ shape.y = vertical_shape_alignment(shape)
333
+
334
+ # puts '------------------'
335
+ # puts "cell: #{cell_no}"
336
+ # puts "wrap_at: #{wrap_at}"
337
+ # puts "shape-x: #{shape.x}"
338
+ # puts "shape-y: #{shape.y}"
339
+ # puts "page-x: #{page.position_x}"
340
+ # puts "page-y: #{page.position_y}"
341
+ # puts "anchor-x: #{anchor_x}"
342
+ # puts "anchor-y: #{anchor_y}"
343
+
344
+ move_cell_horizontally if direction == :horizontal
345
+ move_cell_vertically if direction == :vertical
346
+ end
347
+
348
+ def to_h
349
+ super.merge(
350
+ direction: direction,
351
+ wrap_at: wrap_at,
352
+ grid_size: grid_size,
353
+ cell_no: cell_no
354
+ )
355
+ end
356
+
357
+ private
358
+
359
+ # rubocop:disable Metrics/AbcSize
360
+ def horizontal_shape_alignment(shape)
361
+ return page.position_x + ((grid_size - shape.w) / 2) if h_align == :left
362
+ return page.position_x + (grid_size - shape.x) if v_align == :right
363
+
364
+ page.position_x
365
+ end
366
+ # rubocop:enable Metrics/AbcSize
367
+
368
+ # rubocop:disable Metrics/AbcSize
369
+ def vertical_shape_alignment(shape)
370
+ return page.position_y + ((grid_size - shape.h) / 2) if v_align == :middle
371
+ return page.position_y + (grid_size - shape.h) if v_align == :bottom
372
+
373
+ page.position_y
374
+ end
375
+ # rubocop:enable Metrics/AbcSize
376
+
377
+ def move_cell_horizontally
378
+ if cell_no >= wrap_at
379
+ # Flow down to the next row
380
+ page.position_x = anchor_x
381
+ page.position_y += grid_size
382
+ @cell_no = 1
383
+ else
384
+ page.position_x += grid_size
385
+ @cell_no += 1
386
+ end
387
+ end
388
+
389
+ def move_cell_vertically
390
+ if cell_no >= wrap_at
391
+ # Flow right to the next column
392
+ page.position_y = anchor_y
393
+ page.position_x += grid_size
394
+ @cell_no = 0
395
+ else
396
+ page.position_y += grid_size
397
+ @cell_no += 1
398
+ end
399
+ end
400
+ end
401
+
402
+ # Provides flex style layouts
403
+ class FlexLayout < LayoutRule
404
+ attr_accessor :direction
405
+ attr_accessor :wrap_at
406
+
407
+ def initialize(page, **args)
408
+ @type = :flex_layout
409
+ @direction = args[:direction] || :horizontal
410
+ @wrap_at = args[:wrap_at] || (direction == :horizontal ? 1000 : 800)
411
+
412
+ super(page, **args)
413
+ end
414
+
415
+ # rubocop:disable Metrics/AbcSize, Style/GuardClause
416
+ def position_shape(shape)
417
+ fire_after_init
418
+
419
+ shape.x = page.position_x
420
+ shape.y = page.position_y
421
+
422
+ # Flow down to the next row
423
+ if direction == :horizontal
424
+ if page.position_x + shape.w > boundary
425
+ page.position_x = anchor_x
426
+ page.position_y += grid_size
427
+ @cell_no = 0
428
+ else
429
+ page.position_x += grid_size
430
+ end
431
+ end
432
+
433
+ # Flow right to the next column
434
+ if direction == :vertical
435
+ if page.position_y + shape.h > boundary
436
+ page.position_y = anchor_y
437
+ page.position_x += grid_size
438
+ @cell_no = 0
439
+ else
440
+ page.position_y += grid_size
441
+ end
442
+ end
443
+ end
444
+ # rubocop:enable Metrics/AbcSize, Style/GuardClause
445
+
446
+ private
447
+
448
+ def boundary
449
+ return @boundary if defined? @boundary
450
+
451
+ bounds = grid_size * wrap_at
452
+ @boundary = bounds + (direction == :horizontal ? anchor_x : anchor_y)
453
+ end
454
+
455
+ def to_h
456
+ super.merge(direction: direction, wrap_at: wrap_at)
457
+ end
458
+ end
459
+
460
+ # Shape is a graphical element, it can be a shape, a text, or a group (todo)
461
+ class Shape < Node
212
462
  attr_accessor :theme
213
463
  attr_accessor :title
214
464
 
@@ -225,7 +475,6 @@ module DrawioDsl
225
475
  attr_accessor :font_color
226
476
  attr_accessor :gradient
227
477
 
228
- attr_accessor :id
229
478
  attr_accessor :type
230
479
  attr_accessor :x
231
480
  attr_accessor :y
@@ -234,14 +483,14 @@ module DrawioDsl
234
483
  attr_accessor :style_modifiers
235
484
 
236
485
  def initialize(page, **args)
237
- @page = page
238
- @id = args[:id]
486
+ args[:classification] = :shape
487
+ super(page, **args)
239
488
 
240
- apply_defaults(args, KConfig.configuration.drawio.element)
489
+ apply_defaults(args, KConfig.configuration.drawio.shape)
241
490
  end
242
491
 
243
492
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
244
- def apply_defaults(args, element_defaults)
493
+ def apply_defaults(args, shape_defaults)
245
494
  @theme = args[:theme] || page.theme # KConfig.configuration.drawio.themes.sample
246
495
  theme_palette = KConfig.configuration.drawio.palette(page.theme)
247
496
  @title = args[:title] || ''
@@ -253,12 +502,12 @@ module DrawioDsl
253
502
  @sketch = args[:sketch] || page.style.sketch
254
503
  @glass = args[:glass] || page.style.glass
255
504
 
256
- @type = args[:type] || element_defaults.type
257
- @x = args[:x] || element_defaults.x
258
- @y = args[:y] || element_defaults.y
259
- @w = args[:w] || element_defaults.w
260
- @h = args[:h] || element_defaults.h
261
- @style_modifiers = args[:style_modifiers] || element_defaults.style_modifiers
505
+ @type = args[:type] || shape_defaults.type
506
+ @x = args[:x] || shape_defaults.x
507
+ @y = args[:y] || shape_defaults.y
508
+ @w = args[:w] || shape_defaults.w
509
+ @h = args[:h] || shape_defaults.h
510
+ @style_modifiers = args[:style_modifiers] || shape_defaults.style_modifiers
262
511
 
263
512
  @fill_color = args[:fill_color] || theme_palette.fill_color
264
513
  @stroke_color = args[:stroke_color] || theme_palette.stroke_color
@@ -287,6 +536,7 @@ module DrawioDsl
287
536
  def to_h
288
537
  {
289
538
  id: id,
539
+ classification: classification,
290
540
  type: type,
291
541
  x: x,
292
542
  y: y,
@@ -295,10 +545,18 @@ module DrawioDsl
295
545
  style: style
296
546
  }
297
547
  end
548
+
549
+ def debug(format: :detail)
550
+ if format == :detail
551
+ debug_detail({ id: id, classification: classification, type: type })
552
+ else
553
+ debug_row(classification, id, type, x, y, w, h)
554
+ end
555
+ end
298
556
  end
299
557
 
300
- # Graphical element in the shape of a square
301
- class Square < Element
558
+ # Graphical shape in the form of a square
559
+ class Square < Shape
302
560
  def initialize(page, **args)
303
561
  super(page, **args)
304
562
 
@@ -306,8 +564,8 @@ module DrawioDsl
306
564
  end
307
565
  end
308
566
 
309
- # Graphical element in the shape of a rectangle
310
- class Rectangle < Element
567
+ # Graphical shape in the form of a rectangle
568
+ class Rectangle < Shape
311
569
  def initialize(page, **args)
312
570
  super(page, **args)
313
571
 
@@ -315,8 +573,8 @@ module DrawioDsl
315
573
  end
316
574
  end
317
575
 
318
- # Graphical element in the shape of a circle
319
- class Circle < Element
576
+ # Graphical shape in the form of a circle
577
+ class Circle < Shape
320
578
  def initialize(page, **args)
321
579
  super(page, **args)
322
580
 
@@ -324,8 +582,8 @@ module DrawioDsl
324
582
  end
325
583
  end
326
584
 
327
- # Graphical element in the shape of an rectangle with to vertical lines
328
- class Process < Element
585
+ # Graphical shape in the form of an rectangle with to vertical lines
586
+ class Process < Shape
329
587
  def initialize(page, **args)
330
588
  super(page, **args)
331
589
 
@@ -333,8 +591,8 @@ module DrawioDsl
333
591
  end
334
592
  end
335
593
 
336
- # Graphical element in the shape of an ellipse
337
- class Ellipse < Element
594
+ # Graphical shape in the form of an ellipse
595
+ class Ellipse < Shape
338
596
  def initialize(page, **args)
339
597
  super(page, **args)
340
598
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DrawioDsl
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -52,8 +52,8 @@ module DrawioDsl
52
52
  xml.root do
53
53
  xml.mxCell(id: "#{page.id}-A")
54
54
  xml.mxCell(id: "#{page.id}-B", parent: "#{page.id}-A")
55
- page.elements.each do |element|
56
- build_element(xml, element)
55
+ page.nodes.each do |node|
56
+ build_shape(xml, node) if node.classification == :shape
57
57
  end
58
58
  end
59
59
  end
@@ -61,13 +61,13 @@ module DrawioDsl
61
61
  end
62
62
  # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/BlockLength
63
63
 
64
- def build_element(xml, element)
64
+ def build_shape(xml, shape)
65
65
  # puts "opts: #{opts}"
66
- puts element.x
67
- puts element.y
66
+ # puts shape.x
67
+ # puts shape.y
68
68
 
69
- xml.mxCell(id: element.id, value: element.title, style: element.style, vertex: 1, parent: "#{element.page.id}-B") do
70
- xml.mxGeometry(x: element.x, y: element.y, width: element.w, height: element.h, as: 'geometry')
69
+ xml.mxCell(id: shape.id, value: shape.title, style: shape.style, vertex: 1, parent: "#{shape.page.id}-B") do
70
+ xml.mxGeometry(x: shape.x, y: shape.y, width: shape.w, height: shape.h, as: 'geometry')
71
71
  end
72
72
  end
73
73
  end
data/lib/drawio_dsl.rb CHANGED
@@ -12,7 +12,6 @@ require_relative 'drawio_dsl/version'
12
12
  require_relative 'drawio_dsl/schema'
13
13
  require_relative 'drawio_dsl/dom_builder'
14
14
  require_relative 'drawio_dsl/xml_builder'
15
- require_relative 'drawio_dsl/layout_container'
16
15
  require_relative 'drawio_dsl/layout_engine'
17
16
  require_relative 'drawio_dsl/drawio'
18
17
 
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "drawio_dsl",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "drawio_dsl",
9
- "version": "0.1.0",
9
+ "version": "0.2.0",
10
10
  "devDependencies": {
11
11
  "@klueless-js/semantic-release-rubygem": "github:klueless-js/semantic-release-rubygem",
12
12
  "@semantic-release/changelog": "^6.0.1",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drawio_dsl",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "DrawIO DSL can build DrawIO diagrams using a Domain Specific Language",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: drawio_dsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-02 00:00:00.000000000 Z
11
+ date: 2022-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: k_config
@@ -95,7 +95,6 @@ files:
95
95
  - lib/drawio_dsl/dom_builder.rb
96
96
  - lib/drawio_dsl/drawio.rb
97
97
  - lib/drawio_dsl/drawio_dsl-OLD.x
98
- - lib/drawio_dsl/layout_container.rb
99
98
  - lib/drawio_dsl/layout_engine.rb
100
99
  - lib/drawio_dsl/schema.rb
101
100
  - lib/drawio_dsl/version.rb
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DrawioDsl
4
- # The layout engine is responsible for laying out the elements on the page.
5
- #
6
- # The layout engine can automatically place elements on the page.
7
- #
8
- # It will keep track of layout boundaries, current position and flow direction.
9
- # Elements are positioned on the page in the order they are added.
10
- # Row/column flow objects will hold information about horizontal and vertical element padding
11
- class LayoutContainer
12
- DEFAULT_DIRECTION = :vertical
13
- DEFAULT_PADDING = 20
14
- DEFAULT_BOUNDARY_HEIGHT = 800
15
- DEFAULT_BOUNDARY_WIDTH = 1000
16
-
17
- attr_reader :engine
18
- attr_accessor :direction
19
- attr_accessor :padding
20
-
21
- def initialize(engine, **opts)
22
- @engine = engine
23
- @direction = opts[:direction] || DEFAULT_DIRECTION
24
- @padding = opts[:padding] || DEFAULT_PADDING
25
- @boundary = opts[:boundary]
26
- end
27
-
28
- def vertical?
29
- @direction == :vertical
30
- end
31
-
32
- def horizontal?
33
- @direction == :horizontal
34
- end
35
-
36
- attr_writer :boundary
37
-
38
- def boundary
39
- @boundary ||= (vertical? ? DEFAULT_BOUNDARY_HEIGHT : DEFAULT_BOUNDARY_WIDTH)
40
- end
41
-
42
- # Place the incoming element by altering it's x, y coordinates based on the rules engine
43
- def place_element(element)
44
- place_vertical(element) if vertical?
45
- end
46
-
47
- def place_vertical(element)
48
- element.x = engine.x
49
- element.y = engine.y
50
-
51
- engine.y += (element.h + padding)
52
- end
53
- end
54
- end