drawio_dsl 0.1.0

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,54 @@
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
@@ -0,0 +1,47 @@
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 will be placed 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 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
22
+ end
23
+
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
31
+
32
+ def go_horizontal(**opts)
33
+ @container = LayoutContainer.new(self, direction: :horizontal, **opts)
34
+ end
35
+
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
40
+
41
+ # private
42
+
43
+ # def position_horizontally(element)
44
+
45
+ # end
46
+ end
47
+ end
@@ -0,0 +1,346 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Common Style is the reused on Diagram, Page and Element
6
+ #
7
+ # When styles are not provided at each level, then they will inherit from
8
+ # the parent common style.
9
+ #
10
+ # Elements will use the common style of their page
11
+ class CommonStyle
12
+ attr_accessor :theme
13
+ attr_accessor :white_space
14
+ attr_accessor :html
15
+ attr_accessor :rounded
16
+ attr_accessor :shadow
17
+ attr_accessor :glass
18
+ attr_accessor :sketch
19
+
20
+ def initialize(**args, &block)
21
+ @white_space = args[:white_space]
22
+ @html = args[:html]
23
+ @rounded = args[:rounded]
24
+ @shadow = args[:shadow]
25
+ @sketch = args[:sketch]
26
+ @glass = args[:glass]
27
+
28
+ instance_eval(&block) if block_given?
29
+ end
30
+
31
+ def to_h
32
+ {
33
+ white_space: white_space,
34
+ html: html,
35
+ rounded: rounded,
36
+ shadow: shadow,
37
+ sketch: sketch,
38
+ glass: glass
39
+ }
40
+ end
41
+ end
42
+
43
+ # Default Palette contains palette information that can be inherited at each level
44
+ class DefaultPalette
45
+ attr_accessor :fill_color
46
+ attr_accessor :stroke_color
47
+ attr_accessor :font_color
48
+ attr_accessor :gradient
49
+
50
+ def initialize(owner, **args, &block)
51
+ @fill_color = args[:fill_color]
52
+ @stroke_color = args[:stroke_color]
53
+ @font_color = args[:font_color]
54
+ @gradient = args[:gradient]
55
+
56
+ instance_exec(owner, &block) if block_given?
57
+ end
58
+
59
+ def to_h
60
+ {
61
+ fill_color: fill_color,
62
+ stroke_color: stroke_color,
63
+ font_color: font_color,
64
+ gradient: gradient
65
+ }
66
+ end
67
+ end
68
+
69
+ # Diagram is the root of the schema, it contains pages
70
+ class Diagram
71
+ attr_accessor :host
72
+ attr_accessor :theme
73
+ attr_accessor :style
74
+ attr_accessor :palette
75
+ attr_accessor :pages
76
+
77
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
78
+ def initialize(**args)
79
+ @host = args[:host] || SecureRandom.alphanumeric(3)
80
+
81
+ # Apply a random theme to the diagram if none is specified.
82
+ @theme = args[:theme] || KConfig.configuration.drawio.random_theme
83
+
84
+ @style = DrawioDsl::Schema::CommonStyle.new(**args) do
85
+ default_style = KConfig.configuration.drawio.base_style
86
+
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
94
+ end
95
+
96
+ @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |diagram|
97
+ theme_palette = KConfig.configuration.drawio.palette(diagram.theme)
98
+
99
+ # Inherit from theme when specific palette options are not specified.
100
+ @fill_color ||= theme_palette.fill_color
101
+ @stroke_color ||= theme_palette.stroke_color
102
+ @font_color ||= theme_palette.font_color
103
+ @gradient ||= theme_palette.gradient
104
+ end
105
+
106
+ @pages = args[:pages] || []
107
+ end
108
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
109
+
110
+ def to_h
111
+ {
112
+ host: host,
113
+ theme: theme,
114
+ palette: palette.to_h,
115
+ style: style.to_h,
116
+ pages: pages.map(&:to_h)
117
+ }
118
+ end
119
+ end
120
+
121
+ # Page is a container for elements
122
+ class Page
123
+ attr_accessor :diagram
124
+
125
+ attr_accessor :id
126
+ attr_accessor :name
127
+ attr_accessor :theme
128
+ attr_accessor :style
129
+ attr_accessor :palette
130
+ attr_accessor :elements
131
+
132
+ # attr_accessor :dx # dx = "2636"
133
+ # attr_accessor :dy # dy = "2332"
134
+ attr_accessor :grid # grid = "0"
135
+ attr_accessor :grid_size # gridSize = "10"
136
+ attr_accessor :guides # guides = "1"
137
+ attr_accessor :tooltips # tooltips = "1"
138
+ attr_accessor :connect # connect = "1"
139
+ attr_accessor :arrows # arrows = "1"
140
+ attr_accessor :fold # fold = "1"
141
+ attr_accessor :page_no # page = "1"
142
+ attr_accessor :page_scale # pageScale = "1"
143
+ attr_accessor :page_width # pageWidth = "583"
144
+ attr_accessor :page_height # pageHeight = "827"
145
+ attr_accessor :background # background = "#FFFACD"
146
+ attr_accessor :page_shadow # shadow = "0"
147
+ attr_accessor :math # math = "0"
148
+
149
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
150
+ def initialize(diagram, **args)
151
+ @diagram = diagram
152
+
153
+ @id = args[:id]
154
+ @name = args[:name]
155
+ @theme = args[:theme] || diagram.theme
156
+
157
+ @grid = args[:grid] || 0
158
+ @grid_size = args[:grid_size] || 10
159
+ @guides = args[:guides] || 1
160
+ @tooltips = args[:tooltips] || 1
161
+ @connect = args[:connect] || 1
162
+ @arrows = args[:arrows] || 1
163
+ @fold = args[:fold] || 1
164
+ @page_no = args[:page_no] || 1
165
+ @page_scale = args[:page_scale] || 1
166
+ @page_width = args[:page_width] || 1169 # A4
167
+ @page_height = args[:page_height] || 827 # A4
168
+ @background = args[:background] || '#FFFACD'
169
+ @page_shadow = args[:page_shadow] || 0
170
+ @math = args[:math] || 0
171
+
172
+ @style = DrawioDsl::Schema::CommonStyle.new(**args) do
173
+ # 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
180
+ end
181
+
182
+ @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |page|
183
+ theme_palette = KConfig.configuration.drawio.palette(page.theme)
184
+
185
+ # Inherit from theme when specific palette options are not specified.
186
+ @fill_color ||= theme_palette.fill_color
187
+ @stroke_color ||= theme_palette.stroke_color
188
+ @font_color ||= theme_palette.font_color
189
+ @gradient ||= theme_palette.gradient
190
+ end
191
+
192
+ @elements = args[:elements] || []
193
+ end
194
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
195
+
196
+ def to_h
197
+ {
198
+ id: id,
199
+ name: name,
200
+ theme: theme,
201
+ palette: palette.to_h,
202
+ style: style.to_h,
203
+ elements: elements.map(&:to_h)
204
+ }
205
+ end
206
+ end
207
+
208
+ # Element is a graphical element, it can be a shape, a text, or a group (todo)
209
+ class Element
210
+ attr_accessor :page
211
+
212
+ attr_accessor :theme
213
+ attr_accessor :title
214
+
215
+ # The style of the element, these will derive from the page style if not provided
216
+ attr_accessor :white_space
217
+ attr_accessor :html
218
+ attr_accessor :rounded
219
+ attr_accessor :shadow
220
+ attr_accessor :glass
221
+ attr_accessor :sketch
222
+
223
+ attr_accessor :fill_color
224
+ attr_accessor :stroke_color
225
+ attr_accessor :font_color
226
+ attr_accessor :gradient
227
+
228
+ attr_accessor :id
229
+ attr_accessor :type
230
+ attr_accessor :x
231
+ attr_accessor :y
232
+ attr_accessor :w
233
+ attr_accessor :h
234
+ attr_accessor :style_modifiers
235
+
236
+ def initialize(page, **args)
237
+ @page = page
238
+ @id = args[:id]
239
+
240
+ apply_defaults(args, KConfig.configuration.drawio.element)
241
+ end
242
+
243
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
244
+ def apply_defaults(args, element_defaults)
245
+ @theme = args[:theme] || page.theme # KConfig.configuration.drawio.themes.sample
246
+ theme_palette = KConfig.configuration.drawio.palette(page.theme)
247
+ @title = args[:title] || ''
248
+
249
+ @white_space = args[:white_space] || page.style.white_space # wrap or nil
250
+ @html = args[:html] || page.style.html
251
+ @rounded = args[:rounded] || page.style.rounded
252
+ @shadow = args[:shadow] || page.style.shadow
253
+ @sketch = args[:sketch] || page.style.sketch
254
+ @glass = args[:glass] || page.style.glass
255
+
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
262
+
263
+ @fill_color = args[:fill_color] || theme_palette.fill_color
264
+ @stroke_color = args[:stroke_color] || theme_palette.stroke_color
265
+ @font_color = args[:font_color] || theme_palette.font_color
266
+ @gradient = args[:gradient] || theme_palette.gradient
267
+ end
268
+
269
+ def style
270
+ key_values = []
271
+ key_values << style_modifiers unless style_modifiers.empty?
272
+ key_values << "whiteSpace=#{white_space}" if white_space
273
+ key_values << "html=#{html}" if html
274
+ key_values << "rounded=#{rounded}" if rounded
275
+ key_values << "shadow=#{shadow}" if shadow
276
+ key_values << "sketch=#{sketch}" if sketch
277
+ key_values << "glass=#{glass}" if glass
278
+ key_values << "fillColor=#{fill_color}" if fill_color
279
+ key_values << "strokeColor=#{stroke_color}" if stroke_color
280
+ key_values << "fontColor=#{font_color}" if font_color
281
+ key_values << "gradient=#{gradient}" if gradient
282
+
283
+ key_values.join(';')
284
+ end
285
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
286
+
287
+ def to_h
288
+ {
289
+ id: id,
290
+ type: type,
291
+ x: x,
292
+ y: y,
293
+ w: w,
294
+ h: h,
295
+ style: style
296
+ }
297
+ end
298
+ end
299
+
300
+ # Graphical element in the shape of a square
301
+ class Square < Element
302
+ def initialize(page, **args)
303
+ super(page, **args)
304
+
305
+ apply_defaults(args, KConfig.configuration.drawio.square)
306
+ end
307
+ end
308
+
309
+ # Graphical element in the shape of a rectangle
310
+ class Rectangle < Element
311
+ def initialize(page, **args)
312
+ super(page, **args)
313
+
314
+ apply_defaults(args, KConfig.configuration.drawio.rectangle)
315
+ end
316
+ end
317
+
318
+ # Graphical element in the shape of a circle
319
+ class Circle < Element
320
+ def initialize(page, **args)
321
+ super(page, **args)
322
+
323
+ apply_defaults(args, KConfig.configuration.drawio.circle)
324
+ end
325
+ end
326
+
327
+ # Graphical element in the shape of an rectangle with to vertical lines
328
+ class Process < Element
329
+ def initialize(page, **args)
330
+ super(page, **args)
331
+
332
+ apply_defaults(args, KConfig.configuration.drawio.process)
333
+ end
334
+ end
335
+
336
+ # Graphical element in the shape of an ellipse
337
+ class Ellipse < Element
338
+ def initialize(page, **args)
339
+ super(page, **args)
340
+
341
+ apply_defaults(args, KConfig.configuration.drawio.ellipse)
342
+ end
343
+ end
344
+ end
345
+ end
346
+ # dx="800" dy="583" background="#FFFACD" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ # DrawioDsl is a DSL for draw-io diagrams.
5
+ class XmlBuilder
6
+ include KLog::Logging
7
+
8
+ attr_reader :diagram
9
+
10
+ def initialize(diagram)
11
+ @diagram = diagram
12
+ end
13
+
14
+ def build
15
+ xml_builder = Nokogiri::XML::Builder.new do |xml|
16
+ build_diagram(xml)
17
+ end
18
+ xml_builder.to_xml.sub('<?xml version="1.0"?>', '').strip
19
+ end
20
+
21
+ private
22
+
23
+ def build_diagram(xml)
24
+ xml.mxfile(host: diagram.host) do
25
+ diagram.pages.each do |page|
26
+ build_page(xml, page)
27
+ end
28
+ end
29
+ end
30
+
31
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/BlockLength
32
+ def build_page(xml, page)
33
+ xml.diagram(id: page.id, name: page.name) do
34
+ xml.mxGraphModel(
35
+ dx: 0,
36
+ dy: 0,
37
+ background: page.background,
38
+ grid: page.grid,
39
+ gridSize: page.grid_size,
40
+ guides: page.guides,
41
+ tooltips: page.tooltips,
42
+ connect: page.connect,
43
+ arrows: page.arrows,
44
+ fold: page.fold,
45
+ page: page.page_no,
46
+ pageScale: page.page_scale,
47
+ pageWidth: page.page_width,
48
+ pageHeight: page.page_height,
49
+ math: page.math,
50
+ shadow: page.page_shadow
51
+ ) do
52
+ xml.root do
53
+ xml.mxCell(id: "#{page.id}-A")
54
+ xml.mxCell(id: "#{page.id}-B", parent: "#{page.id}-A")
55
+ page.elements.each do |element|
56
+ build_element(xml, element)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/BlockLength
63
+
64
+ def build_element(xml, element)
65
+ # puts "opts: #{opts}"
66
+ puts element.x
67
+ puts element.y
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')
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/drawio_dsl.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'nokogiri'
5
+
6
+ require 'k_config'
7
+ require 'k_log'
8
+ require 'k_director'
9
+
10
+ require_relative 'drawio_dsl/configuration'
11
+ require_relative 'drawio_dsl/version'
12
+ require_relative 'drawio_dsl/schema'
13
+ require_relative 'drawio_dsl/dom_builder'
14
+ require_relative 'drawio_dsl/xml_builder'
15
+ require_relative 'drawio_dsl/layout_container'
16
+ require_relative 'drawio_dsl/layout_engine'
17
+ require_relative 'drawio_dsl/drawio'
18
+
19
+ module DrawioDsl
20
+ # raise DrawioDsl::Error, 'Sample message'
21
+ Error = Class.new(StandardError)
22
+
23
+ # Your code goes here...
24
+ end
25
+
26
+ if ENV['KLUE_DEBUG']&.to_s&.downcase == 'true'
27
+ namespace = 'DrawioDsl::Version'
28
+ file_path = $LOADED_FEATURES.find { |f| f.include?('drawio_dsl/version') }
29
+ version = DrawioDsl::VERSION.ljust(9)
30
+ puts "#{namespace.ljust(35)} : #{version.ljust(9)} : #{file_path}"
31
+ end