drawio_dsl 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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