drawio_dsl 0.2.0 → 0.4.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.builders/.data/shapes.json +82 -0
  3. data/.builders/.templates/basic/schema_require.rb +16 -0
  4. data/.builders/.templates/schema_shape.rb +9 -0
  5. data/.builders/.templates/schema_shape_spec.rb +13 -0
  6. data/.builders/blueprint/shapes.rb +29 -0
  7. data/.builders/boot.rb +5 -0
  8. data/.builders/generators/90-requires.rb +35 -0
  9. data/.builders/generators/sample_diagrams/10-page-margin.rb +46 -0
  10. data/.builders/generators/sample_diagrams/15-grid-direction.rb +40 -0
  11. data/.builders/generators/sample_diagrams/16-grid-alignment.rb +73 -0
  12. data/.builders/generators/sample_diagrams/20-styles.rb +64 -0
  13. data/.builders/generators/sample_diagrams/25-themes.rb +24 -0
  14. data/.rubocop.yml +2 -1
  15. data/CHANGELOG.md +21 -0
  16. data/lib/drawio_dsl/configuration.rb +56 -43
  17. data/lib/drawio_dsl/dom_builder.rb +25 -0
  18. data/lib/drawio_dsl/drawio.rb +49 -0
  19. data/lib/drawio_dsl/layout_engine.rb +12 -28
  20. data/lib/drawio_dsl/schema/_.rb +23 -0
  21. data/lib/drawio_dsl/schema/common_style.rb +42 -0
  22. data/lib/drawio_dsl/schema/default_palette.rb +31 -0
  23. data/lib/drawio_dsl/schema/diagram.rb +57 -0
  24. data/lib/drawio_dsl/schema/layouts/flex_layout.rb +87 -0
  25. data/lib/drawio_dsl/schema/layouts/grid_layout.rb +102 -0
  26. data/lib/drawio_dsl/schema/layouts/layout.rb +41 -0
  27. data/lib/drawio_dsl/schema/node.rb +53 -0
  28. data/lib/drawio_dsl/schema/page.rb +135 -0
  29. data/lib/drawio_dsl/schema/shapes/callout.rb +9 -0
  30. data/lib/drawio_dsl/schema/shapes/circle.rb +9 -0
  31. data/lib/drawio_dsl/schema/shapes/cloud.rb +9 -0
  32. data/lib/drawio_dsl/schema/shapes/diamond.rb +9 -0
  33. data/lib/drawio_dsl/schema/shapes/ellipse.rb +9 -0
  34. data/lib/drawio_dsl/schema/shapes/hexagon.rb +9 -0
  35. data/lib/drawio_dsl/schema/shapes/note.rb +9 -0
  36. data/lib/drawio_dsl/schema/shapes/process.rb +9 -0
  37. data/lib/drawio_dsl/schema/shapes/rectangle.rb +9 -0
  38. data/lib/drawio_dsl/schema/shapes/shape.rb +125 -0
  39. data/lib/drawio_dsl/schema/shapes/square.rb +9 -0
  40. data/lib/drawio_dsl/version.rb +1 -1
  41. data/lib/drawio_dsl.rb +1 -1
  42. data/package-lock.json +2 -2
  43. data/package.json +1 -1
  44. metadata +33 -5
  45. data/.builders/generators/10-layout.rb +0 -34
  46. data/lib/drawio_dsl/drawio_dsl-OLD.x +0 -335
  47. data/lib/drawio_dsl/schema.rb +0 -604
@@ -109,6 +109,31 @@ module DrawioDsl
109
109
  add_shape(ellipse)
110
110
  end
111
111
 
112
+ def add_diamond(**opts)
113
+ diamond = DrawioDsl::Schema::Diamond.new(current_page, **opts)
114
+ add_shape(diamond)
115
+ end
116
+
117
+ def add_hexagon(**opts)
118
+ add_hexagon = DrawioDsl::Schema::Hexagon.new(current_page, **opts)
119
+ add_shape(add_hexagon)
120
+ end
121
+
122
+ def add_cloud(**opts)
123
+ cloud = DrawioDsl::Schema::Cloud.new(current_page, **opts)
124
+ add_shape(cloud)
125
+ end
126
+
127
+ def add_note(**opts)
128
+ note = DrawioDsl::Schema::Note.new(current_page, **opts)
129
+ add_shape(note)
130
+ end
131
+
132
+ def add_callout(**opts)
133
+ callout = DrawioDsl::Schema::Callout.new(current_page, **opts)
134
+ add_shape(callout)
135
+ end
136
+
112
137
  def debug
113
138
  puts JSON.pretty_generate(actions)
114
139
  puts JSON.pretty_generate(diagram.to_h)
@@ -15,6 +15,8 @@ module DrawioDsl
15
15
  page = DrawioDsl::DrawioPage.new(self, **opts.merge(name: name))
16
16
  page.instance_eval(&block) if block_given?
17
17
 
18
+ layout = DrawioDsl::LayoutEngine.new(builder.current_page)
19
+ layout.call
18
20
  self
19
21
  end
20
22
 
@@ -54,6 +56,33 @@ module DrawioDsl
54
56
  builder.add_flex_layout(**opts)
55
57
  end
56
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
+
57
86
  def square(**opts)
58
87
  builder.add_square(**opts)
59
88
  end
@@ -74,6 +103,26 @@ module DrawioDsl
74
103
  builder.add_ellipse(**opts)
75
104
  end
76
105
 
106
+ def diamond(**opts)
107
+ builder.add_diamond(**opts)
108
+ end
109
+
110
+ def hexagon(**opts)
111
+ builder.add_hexagon(**opts)
112
+ end
113
+
114
+ def cloud(**opts)
115
+ builder.add_cloud(**opts)
116
+ end
117
+
118
+ def note(**opts)
119
+ builder.add_note(**opts)
120
+ end
121
+
122
+ def callout(**opts)
123
+ builder.add_callout(**opts)
124
+ end
125
+
77
126
  private
78
127
 
79
128
  def current_shape
@@ -9,51 +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_reader :diagram
12
+ attr_reader :page
13
13
 
14
14
  attr_reader :current_page
15
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
16
 
21
- def initialize(diagram)
22
- @diagram = diagram
23
- @x = 0
24
- @y = 0
25
- end
26
-
27
- def call
28
- diagram.pages.each do |page|
29
- focus_on_page(page)
17
+ # attr_reader :page_margin_top
18
+ # attr_reader :page_margin_left
19
+ # attr_reader :x
20
+ # attr_reader :y
30
21
 
31
- apply_layout_to_page(page)
32
- end
22
+ def initialize(page)
23
+ @page = page
24
+ # @x = 0
25
+ # @y = 0
33
26
  end
34
27
 
35
- private
36
-
37
- def focus_on_page(page)
38
- @current_page = page
28
+ def call
39
29
  page.position_x = page.margin_left
40
30
  page.position_y = page.margin_top
41
- end
42
31
 
43
- def apply_layout_to_page(page)
44
32
  page.nodes.each do |node|
45
33
  case node.classification
46
34
  when :layout_rule
47
35
  @current_layout = node
48
36
  when :shape
49
- current_layout.position_shape(node)
37
+ current_layout&.position_shape(node)
50
38
  end
51
- node.debug(format: :row)
39
+ # node.debug(format: :row)
52
40
  end
53
41
  end
54
-
55
- def define_layout_rule(layout_rule)
56
- layout_rule.debug
57
- end
58
42
  end
59
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