drawio_dsl 0.1.0 → 0.4.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.builders/.templates/command.rb +8 -0
  3. data/.builders/.templates/schema_shape.rb +9 -0
  4. data/.builders/blueprint/shapes.rb +19 -0
  5. data/.builders/boot.rb +1 -0
  6. data/.builders/generators/10-page-margin.rb +46 -0
  7. data/.builders/generators/15-grid-direction.rb +40 -0
  8. data/.builders/generators/16-grid-alignment.rb +73 -0
  9. data/.builders/generators/20-styles.rb +64 -0
  10. data/.builders/generators/25-themes.rb +24 -0
  11. data/.rubocop.yml +6 -1
  12. data/CHANGELOG.md +24 -0
  13. data/lib/drawio_dsl/configuration.rb +58 -45
  14. data/lib/drawio_dsl/dom_builder.rb +66 -23
  15. data/lib/drawio_dsl/drawio.rb +65 -36
  16. data/lib/drawio_dsl/layout_engine.rb +24 -28
  17. data/lib/drawio_dsl/schema/_.rb +23 -0
  18. data/lib/drawio_dsl/schema/common_style.rb +42 -0
  19. data/lib/drawio_dsl/schema/default_palette.rb +31 -0
  20. data/lib/drawio_dsl/schema/diagram.rb +57 -0
  21. data/lib/drawio_dsl/schema/layouts/flex_layout.rb +87 -0
  22. data/lib/drawio_dsl/schema/layouts/grid_layout.rb +102 -0
  23. data/lib/drawio_dsl/schema/layouts/layout.rb +41 -0
  24. data/lib/drawio_dsl/schema/node.rb +53 -0
  25. data/lib/drawio_dsl/schema/page.rb +135 -0
  26. data/lib/drawio_dsl/schema/shapes/callout.rb +9 -0
  27. data/lib/drawio_dsl/schema/shapes/circle.rb +9 -0
  28. data/lib/drawio_dsl/schema/shapes/cloud.rb +9 -0
  29. data/lib/drawio_dsl/schema/shapes/diamond.rb +9 -0
  30. data/lib/drawio_dsl/schema/shapes/ellipse.rb +9 -0
  31. data/lib/drawio_dsl/schema/shapes/hexagon.rb +9 -0
  32. data/lib/drawio_dsl/schema/shapes/note.rb +9 -0
  33. data/lib/drawio_dsl/schema/shapes/process.rb +9 -0
  34. data/lib/drawio_dsl/schema/shapes/rectangle.rb +9 -0
  35. data/lib/drawio_dsl/schema/shapes/shape.rb +125 -0
  36. data/lib/drawio_dsl/schema/shapes/square.rb +9 -0
  37. data/lib/drawio_dsl/version.rb +1 -1
  38. data/lib/drawio_dsl/xml_builder.rb +7 -7
  39. data/lib/drawio_dsl.rb +1 -2
  40. data/package-lock.json +2 -2
  41. data/package.json +1 -1
  42. metadata +30 -6
  43. data/.builders/generators/10-layout.rb +0 -21
  44. data/lib/drawio_dsl/drawio_dsl-OLD.x +0 -335
  45. data/lib/drawio_dsl/layout_container.rb +0 -54
  46. data/lib/drawio_dsl/schema.rb +0 -346
@@ -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
 
@@ -21,6 +15,15 @@ module DrawioDsl
21
15
  page = DrawioDsl::DrawioPage.new(self, **opts.merge(name: name))
22
16
  page.instance_eval(&block) if block_given?
23
17
 
18
+ layout = DrawioDsl::LayoutEngine.new(builder.current_page)
19
+ layout.call
20
+ self
21
+ end
22
+
23
+ def apply_layout
24
+ engine = DrawioDsl::LayoutEngine.new(builder.diagram)
25
+ engine.call
26
+
24
27
  self
25
28
  end
26
29
  end
@@ -41,63 +44,89 @@ module DrawioDsl
41
44
  builder.add_page(**opts)
42
45
  end
43
46
 
47
+ def layout_rule(**opts)
48
+ builder.add_layout_rule(**opts)
49
+ end
50
+
51
+ def grid_layout(**opts)
52
+ builder.add_grid_layout(**opts)
53
+ end
54
+
55
+ def flex_layout(**opts)
56
+ builder.add_flex_layout(**opts)
57
+ end
58
+
59
+ # rubocop:disable Metrics/CyclomaticComplexity
60
+ def random(**opts)
61
+ case rand(10)
62
+ when 0
63
+ square(**opts)
64
+ when 1
65
+ rectangle(**opts)
66
+ when 2
67
+ circle(**opts)
68
+ when 3
69
+ process(**opts)
70
+ when 4
71
+ ellipse(**opts)
72
+ when 5
73
+ diamond(**opts)
74
+ when 6
75
+ hexagon(**opts)
76
+ when 7
77
+ cloud(**opts)
78
+ when 8
79
+ note(**opts)
80
+ when 9
81
+ callout(**opts)
82
+ end
83
+ end
84
+ # rubocop:enable Metrics/CyclomaticComplexity
85
+
44
86
  def square(**opts)
45
- opts = attach_xy(**opts)
46
87
  builder.add_square(**opts)
47
- update_layout
48
88
  end
49
89
 
50
90
  def rectangle(**opts)
51
- opts = attach_xy(**opts)
52
91
  builder.add_rectangle(**opts)
53
- update_layout
54
92
  end
55
93
 
56
94
  def circle(**opts)
57
- opts = attach_xy(**opts)
58
95
  builder.add_circle(**opts)
59
- update_layout
60
96
  end
61
97
 
62
98
  def process(**opts)
63
- opts = attach_xy(**opts)
64
99
  builder.add_process(**opts)
65
- update_layout
66
100
  end
67
101
 
68
102
  def ellipse(**opts)
69
- opts = attach_xy(**opts)
70
103
  builder.add_ellipse(**opts)
71
- update_layout
72
104
  end
73
105
 
74
- private
75
-
76
- def current_element
77
- builder.current_element
106
+ def diamond(**opts)
107
+ builder.add_diamond(**opts)
78
108
  end
79
109
 
80
- # attr_reader :layout_engine
81
- # set_layout
82
-
83
- # layout.container.place_element(element)
110
+ def hexagon(**opts)
111
+ builder.add_hexagon(**opts)
112
+ end
84
113
 
85
- def update_layout
86
- return unless auto_layout
114
+ def cloud(**opts)
115
+ builder.add_cloud(**opts)
116
+ end
87
117
 
88
- # puts current_element.page.x
89
- # puts current_element.page.y
90
- # puts current_element.page.w
91
- # puts current_element.page.h
118
+ def note(**opts)
119
+ builder.add_note(**opts)
120
+ end
92
121
 
93
- @current_x += current_element.w + 20
94
- @current_y += current_element.h + 20
122
+ def callout(**opts)
123
+ builder.add_callout(**opts)
95
124
  end
96
125
 
97
- def attach_xy(**opts)
98
- opts[:x] ||= current_x
99
- opts[:y] ||= current_y
100
- opts
126
+ private
127
+
128
+ def current_shape
129
+ builder.current_shape
101
130
  end
102
131
  end
103
132
  end
@@ -9,39 +9,35 @@ 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
12
+ attr_reader :page
16
13
 
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
14
+ attr_reader :current_page
15
+ attr_reader :current_layout
23
16
 
24
- def container
25
- @container ||= LayoutContainer.new(self)
26
- end
17
+ # attr_reader :page_margin_top
18
+ # attr_reader :page_margin_left
19
+ # attr_reader :x
20
+ # attr_reader :y
27
21
 
28
- def go_vertical(**opts)
29
- @container = LayoutContainer.new(self, direction: :vertical, **opts)
22
+ def initialize(page)
23
+ @page = page
24
+ # @x = 0
25
+ # @y = 0
30
26
  end
31
27
 
32
- def go_horizontal(**opts)
33
- @container = LayoutContainer.new(self, direction: :horizontal, **opts)
28
+ def call
29
+ page.position_x = page.margin_left
30
+ page.position_y = page.margin_top
31
+
32
+ page.nodes.each do |node|
33
+ case node.classification
34
+ when :layout_rule
35
+ @current_layout = node
36
+ when :shape
37
+ current_layout&.position_shape(node)
38
+ end
39
+ # node.debug(format: :row)
40
+ end
34
41
  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
42
  end
47
43
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'common_style'
4
+ require_relative 'default_palette'
5
+ require_relative 'diagram'
6
+ require_relative 'node'
7
+ require_relative 'page'
8
+
9
+ require_relative 'layouts/layout'
10
+ require_relative 'layouts/flex_layout'
11
+ require_relative 'layouts/grid_layout'
12
+
13
+ require_relative 'shapes/shape'
14
+ require_relative 'shapes/callout'
15
+ require_relative 'shapes/circle'
16
+ require_relative 'shapes/cloud'
17
+ require_relative 'shapes/diamond'
18
+ require_relative 'shapes/ellipse'
19
+ require_relative 'shapes/hexagon'
20
+ require_relative 'shapes/note'
21
+ require_relative 'shapes/process'
22
+ require_relative 'shapes/rectangle'
23
+ require_relative 'shapes/square'
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Common Style is the reused on Diagram, Page and Shape
6
+ #
7
+ # When styles are not provided at each level, then they will inherit from
8
+ # the parent common style.
9
+ #
10
+ # Shapes will use the common style of their page
11
+ class CommonStyle
12
+ attr_accessor :white_space
13
+ attr_accessor :html
14
+ attr_accessor :rounded
15
+ attr_accessor :shadow
16
+ attr_accessor :glass
17
+ attr_accessor :sketch
18
+
19
+ def initialize(**args, &block)
20
+ @white_space = args[:white_space]
21
+ @html = args[:html]
22
+ @rounded = args[:rounded]
23
+ @shadow = args[:shadow]
24
+ @sketch = args[:sketch]
25
+ @glass = args[:glass]
26
+
27
+ instance_eval(&block) if block_given?
28
+ end
29
+
30
+ def to_h
31
+ {
32
+ white_space: white_space,
33
+ html: html,
34
+ rounded: rounded,
35
+ shadow: shadow,
36
+ sketch: sketch,
37
+ glass: glass
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Default Palette contains palette information that can be inherited at each level
6
+ class DefaultPalette
7
+ attr_accessor :fill_color
8
+ attr_accessor :stroke_color
9
+ attr_accessor :font_color
10
+ attr_accessor :gradient
11
+
12
+ def initialize(owner, **args, &block)
13
+ @fill_color = args[:fill_color]
14
+ @stroke_color = args[:stroke_color]
15
+ @font_color = args[:font_color]
16
+ @gradient = args[:gradient]
17
+
18
+ instance_exec(owner, &block) if block_given?
19
+ end
20
+
21
+ def to_h
22
+ {
23
+ fill_color: fill_color,
24
+ stroke_color: stroke_color,
25
+ font_color: font_color,
26
+ gradient: gradient
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Diagram is the root of the schema, it contains pages
6
+ class Diagram
7
+ attr_accessor :host
8
+ attr_accessor :theme
9
+ attr_accessor :style
10
+ attr_accessor :palette
11
+ attr_accessor :pages
12
+
13
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
14
+ def initialize(**args)
15
+ @host = args[:host] || SecureRandom.alphanumeric(3)
16
+
17
+ # Apply a random theme to the diagram if none is specified.
18
+ @theme = args[:theme] || KConfig.configuration.drawio.random_theme
19
+
20
+ @style = DrawioDsl::Schema::CommonStyle.new(**args) do
21
+ default_style = KConfig.configuration.drawio.base_style
22
+
23
+ # Inherit from configured style when specific style not specified.
24
+ @white_space ||= default_style.white_space
25
+ @html ||= default_style.html
26
+ @rounded ||= default_style.rounded
27
+ @shadow ||= default_style.shadow
28
+ @sketch ||= default_style.sketch
29
+ @glass ||= default_style.glass
30
+ end
31
+
32
+ @palette = DrawioDsl::Schema::DefaultPalette.new(self, **args) do |diagram|
33
+ theme_palette = KConfig.configuration.drawio.palette(diagram.theme)
34
+
35
+ # Inherit from theme when specific palette options are not specified.
36
+ @fill_color ||= theme_palette.fill_color
37
+ @stroke_color ||= theme_palette.stroke_color
38
+ @font_color ||= theme_palette.font_color
39
+ @gradient ||= theme_palette.gradient
40
+ end
41
+
42
+ @pages = args[:pages] || []
43
+ end
44
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
45
+
46
+ def to_h
47
+ {
48
+ host: host,
49
+ theme: theme,
50
+ palette: palette.to_h,
51
+ style: style.to_h,
52
+ pages: pages.map(&:to_h)
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Provides flex style layouts
6
+ class FlexLayout < Layout
7
+ attr_accessor :direction
8
+ attr_accessor :wrap_at
9
+ attr_reader :gap
10
+ attr_reader :perpendicular_max
11
+
12
+ def initialize(page, **args)
13
+ @type = :flex_layout
14
+ @direction = args[:direction] || :horizontal
15
+ @wrap_at = args[:wrap_at] || (direction == :horizontal ? 1000 : 800)
16
+ @gap = args[:gap] || 20
17
+ @perpendicular_max = 0
18
+
19
+ super(page, **args)
20
+ end
21
+
22
+ def position_shape(shape)
23
+ fire_after_init
24
+
25
+ position_shape_horizontally(shape) if direction == :horizontal
26
+ position_shape_vertically(shape) if direction == :vertical
27
+ end
28
+
29
+ def to_h
30
+ fire_after_init
31
+
32
+ super.merge(
33
+ direction: direction,
34
+ wrap_at: wrap_at,
35
+ boundary: boundary,
36
+ perpendicular_max: perpendicular_max
37
+ )
38
+ end
39
+
40
+ private
41
+
42
+ # rubocop:disable Metrics/AbcSize
43
+ def position_shape_horizontally(shape)
44
+ boundary_violation = page.position_x + shape.w + gap > boundary
45
+
46
+ if boundary_violation
47
+ page.position_x = anchor_x
48
+ page.position_y += perpendicular_max
49
+ end
50
+
51
+ shape.x = page.position_x
52
+ shape.y = page.position_y
53
+
54
+ return if boundary_violation
55
+
56
+ page.position_x += shape.w + gap
57
+ @perpendicular_max = [perpendicular_max, shape.h].max
58
+ end
59
+ # rubocop:enable Metrics/AbcSize
60
+
61
+ # rubocop:disable Metrics/AbcSize
62
+ def position_shape_vertically(shape)
63
+ boundary_violation = page.position_y + shape.h + gap > boundary
64
+
65
+ if boundary_violation
66
+ page.position_y = anchor_y
67
+ page.position_x += perpendicular_max
68
+ end
69
+
70
+ shape.x = page.position_x
71
+ shape.y = page.position_y
72
+
73
+ return if boundary_violation
74
+
75
+ page.position_y += shape.h + gap
76
+ @perpendicular_max = [perpendicular_max, shape.w].max
77
+ end
78
+ # rubocop:enable Metrics/AbcSize
79
+
80
+ def boundary
81
+ return @boundary if defined? @boundary
82
+
83
+ @boundary = wrap_at + (direction == :horizontal ? anchor_x : anchor_y)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Provides grid style layouts
6
+ class GridLayout < Layout
7
+ attr_accessor :direction
8
+ attr_accessor :wrap_at
9
+ attr_accessor :grid_size
10
+ attr_accessor :cell_no
11
+ attr_accessor :h_align
12
+ attr_accessor :v_align
13
+
14
+ def initialize(page, **args)
15
+ @type = :grid_layout
16
+ @direction = args[:direction] || :horizontal
17
+ @wrap_at = args[:wrap_at] || 5
18
+ @grid_size = args[:grid_size] || 220
19
+ @h_align = args[:h_align] || :center
20
+ @v_align = args[:v_align] || :center
21
+ @cell_no = 1
22
+
23
+ super(page, **args)
24
+ end
25
+
26
+ def position_shape(shape)
27
+ fire_after_init
28
+
29
+ shape.x = horizontal_shape_alignment(shape)
30
+ shape.y = vertical_shape_alignment(shape)
31
+
32
+ # puts '------------------'
33
+ # puts "cell: #{cell_no}"
34
+ # puts "wrap_at: #{wrap_at}"
35
+ # puts "shape-x: #{shape.x}"
36
+ # puts "shape-y: #{shape.y}"
37
+ # puts "page-x: #{page.position_x}"
38
+ # puts "page-y: #{page.position_y}"
39
+ # puts "anchor-x: #{anchor_x}"
40
+ # puts "anchor-y: #{anchor_y}"
41
+
42
+ move_cell_horizontally if direction == :horizontal
43
+ move_cell_vertically if direction == :vertical
44
+ end
45
+
46
+ def to_h
47
+ super.merge(
48
+ direction: direction,
49
+ wrap_at: wrap_at,
50
+ grid_size: grid_size,
51
+ cell_no: cell_no
52
+ )
53
+ end
54
+
55
+ private
56
+
57
+ # rubocop:disable Metrics/AbcSize
58
+ def horizontal_shape_alignment(shape)
59
+ return page.position_x + ((grid_size - shape.w) / 2) if h_align == :center
60
+ return page.position_x + (grid_size - shape.w) if h_align == :right
61
+
62
+ page.position_x
63
+ end
64
+ # rubocop:enable Metrics/AbcSize
65
+
66
+ # rubocop:disable Metrics/AbcSize
67
+ def vertical_shape_alignment(shape)
68
+ return page.position_y + ((grid_size - shape.h) / 2) if v_align == :center || v_align == :middle
69
+ return page.position_y + (grid_size - shape.h) if v_align == :bottom
70
+
71
+ page.position_y
72
+ end
73
+ # rubocop:enable Metrics/AbcSize
74
+
75
+ def move_cell_horizontally
76
+ if cell_no < wrap_at
77
+ page.position_x += grid_size
78
+ @cell_no += 1
79
+ return
80
+ end
81
+
82
+ # Flow down to the next row
83
+ page.position_x = anchor_x
84
+ page.position_y += grid_size
85
+ @cell_no = 1
86
+ end
87
+
88
+ def move_cell_vertically
89
+ if cell_no < wrap_at
90
+ page.position_y += grid_size
91
+ @cell_no += 1
92
+ return
93
+ end
94
+
95
+ # Flow right to the next column
96
+ page.position_y = anchor_y
97
+ page.position_x += grid_size
98
+ @cell_no = 1
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Provides base configuration for automatic layouts
6
+ class Layout < Node
7
+ attr_accessor :type
8
+
9
+ # represents the x coordinate of the top left corner layout area
10
+ # this coordinate is based on the current location of the page
11
+ attr_accessor :anchor_x
12
+ attr_accessor :anchor_y
13
+
14
+ def initialize(page, **args)
15
+ @after_init_fired = false
16
+
17
+ super(page, **args.merge(classification: :layout_rule))
18
+ end
19
+
20
+ def fire_after_init
21
+ return if @after_init_fired
22
+
23
+ @after_init_fired = true
24
+ after_init
25
+ end
26
+
27
+ def after_init
28
+ @anchor_x ||= page.position_x
29
+ @anchor_y ||= page.position_y
30
+ end
31
+
32
+ def to_h
33
+ super.merge(
34
+ type: type,
35
+ anchor_x: anchor_x,
36
+ anchor_y: anchor_y
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrawioDsl
4
+ module Schema
5
+ # Node is a base class for shapes, connections, positioners and layout rules
6
+ #
7
+ class Node
8
+ attr_accessor :id
9
+ attr_accessor :page
10
+ attr_accessor :classification
11
+
12
+ def initialize(page, **args)
13
+ @page = page
14
+ @id = args[:id]
15
+ @classification = args[:classification] || :unknown
16
+ end
17
+
18
+ def to_h
19
+ {
20
+ id: id,
21
+ classification: classification
22
+ }
23
+ end
24
+
25
+ # def debug(format: :detail)
26
+ # if format == :detail
27
+ # debug_detail(to_h)
28
+ # else
29
+ # debug_row(classification, id)
30
+ # end
31
+ # end
32
+
33
+ # def debug_detail(**key_values)
34
+ # key_values.each do |key, value|
35
+ # puts "#{key.to_s.ljust(15)}: #{value}"
36
+ # end
37
+ # end
38
+
39
+ #
40
+ # def debug_row(classification, id, type = nil, x = nil, y = nil, width = nil, height = nil)
41
+ # row = []
42
+ # row << classification.to_s.ljust(11)
43
+ # row << id.to_s.ljust(6)
44
+ # row << (type.nil? ? '' : type).to_s.ljust(15)
45
+ # row << (x.nil? ? '' : x).to_s.rjust(5)
46
+ # row << (y.nil? ? '' : y).to_s.rjust(5)
47
+ # row << (width.nil? ? '' : width).to_s.rjust(5)
48
+ # row << (height.nil? ? '' : height).to_s.rjust(5)
49
+ # puts row.join(' | ')
50
+ # end
51
+ end
52
+ end
53
+ end