protos 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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +12 -0
  4. data/CHANGELOG.md +5 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +150 -0
  7. data/Rakefile +12 -0
  8. data/examples/navbar.rb +43 -0
  9. data/lib/protos/accordion/item.rb +33 -0
  10. data/lib/protos/accordion.rb +34 -0
  11. data/lib/protos/alert/actions.rb +19 -0
  12. data/lib/protos/alert/icon.rb +19 -0
  13. data/lib/protos/alert.rb +46 -0
  14. data/lib/protos/attributes.rb +42 -0
  15. data/lib/protos/avatar.rb +71 -0
  16. data/lib/protos/card/actions.rb +19 -0
  17. data/lib/protos/card/body.rb +19 -0
  18. data/lib/protos/card/image.rb +11 -0
  19. data/lib/protos/card/title.rb +19 -0
  20. data/lib/protos/card.rb +55 -0
  21. data/lib/protos/collapse/content.rb +19 -0
  22. data/lib/protos/collapse/title.rb +19 -0
  23. data/lib/protos/collapse.rb +31 -0
  24. data/lib/protos/combobox.rb +53 -0
  25. data/lib/protos/command/dialog.rb +32 -0
  26. data/lib/protos/command/empty.rb +25 -0
  27. data/lib/protos/command/group.rb +13 -0
  28. data/lib/protos/command/input.rb +44 -0
  29. data/lib/protos/command/item.rb +19 -0
  30. data/lib/protos/command/list.rb +19 -0
  31. data/lib/protos/command/title.rb +19 -0
  32. data/lib/protos/command/trigger.rb +19 -0
  33. data/lib/protos/command.rb +49 -0
  34. data/lib/protos/component.rb +79 -0
  35. data/lib/protos/drawer/content.rb +19 -0
  36. data/lib/protos/drawer/side.rb +25 -0
  37. data/lib/protos/drawer/trigger.rb +21 -0
  38. data/lib/protos/drawer.rb +35 -0
  39. data/lib/protos/dropdown/item.rb +11 -0
  40. data/lib/protos/dropdown/menu.rb +25 -0
  41. data/lib/protos/dropdown/trigger.rb +11 -0
  42. data/lib/protos/dropdown.rb +59 -0
  43. data/lib/protos/engine.rb +7 -0
  44. data/lib/protos/list/item.rb +22 -0
  45. data/lib/protos/list.rb +30 -0
  46. data/lib/protos/modal/close_button.rb +16 -0
  47. data/lib/protos/modal/dialog.rb +30 -0
  48. data/lib/protos/modal/trigger.rb +19 -0
  49. data/lib/protos/modal.rb +31 -0
  50. data/lib/protos/popover/content.rb +19 -0
  51. data/lib/protos/popover/trigger.rb +11 -0
  52. data/lib/protos/popover.rb +54 -0
  53. data/lib/protos/table/body.rb +11 -0
  54. data/lib/protos/table/caption.rb +19 -0
  55. data/lib/protos/table/cell.rb +11 -0
  56. data/lib/protos/table/footer.rb +11 -0
  57. data/lib/protos/table/head.rb +11 -0
  58. data/lib/protos/table/header.rb +11 -0
  59. data/lib/protos/table/row.rb +11 -0
  60. data/lib/protos/table.rb +48 -0
  61. data/lib/protos/theme.rb +76 -0
  62. data/lib/protos/timeline/center.rb +19 -0
  63. data/lib/protos/timeline/item.rb +11 -0
  64. data/lib/protos/timeline/left.rb +19 -0
  65. data/lib/protos/timeline/right.rb +19 -0
  66. data/lib/protos/timeline.rb +38 -0
  67. data/lib/protos/token_list.rb +41 -0
  68. data/lib/protos/types.rb +32 -0
  69. data/lib/protos/typography/heading.rb +44 -0
  70. data/lib/protos/typography/inline_link.rb +24 -0
  71. data/lib/protos/typography/paragraph.rb +22 -0
  72. data/lib/protos/typography.rb +29 -0
  73. data/lib/protos/version.rb +5 -0
  74. data/lib/protos.rb +94 -0
  75. data/protos.gemspec +45 -0
  76. metadata +180 -0
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class List < Component
5
+ option :ordered, Types::Bool, default: -> { false }, reader: false
6
+
7
+ def template(&block)
8
+ send(element, **attrs, &block)
9
+ end
10
+
11
+ def item(...)
12
+ Item.new(...)
13
+ end
14
+
15
+ private
16
+
17
+ def css
18
+ {
19
+ container: tokens(
20
+ "join",
21
+ "join-vertical"
22
+ )
23
+ }
24
+ end
25
+
26
+ def element
27
+ @ordered ? :ol : :ul
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Modal
5
+ class CloseButton < Component
6
+ def template(&block)
7
+ form(method: :dialog, class: css[:form]) do
8
+ button(
9
+ **attrs,
10
+ &block
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Modal
5
+ class Dialog < Component
6
+ def template(&block)
7
+ dialog(**attrs) do
8
+ div(class: css[:modal], &block)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def theme
15
+ {
16
+ container: tokens("modal", "modal-bottom", "sm:modal-middle"),
17
+ modal: tokens("modal-box", "flex", "flex-col", "gap-xs")
18
+ }
19
+ end
20
+
21
+ def attrs
22
+ @attrs ||= build_attrs(
23
+ {
24
+ data: { "protos--modal-target": "modal" }
25
+ }
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Modal
5
+ class Trigger < Component
6
+ def template(&block)
7
+ button(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def default_attrs
13
+ {
14
+ data: { action: "protos--modal#open" }
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Modal < Protos::Component
5
+ def template(&block)
6
+ div(**attrs, class: css[:container], &block)
7
+ end
8
+
9
+ def close_button(...)
10
+ CloseButton.new(...)
11
+ end
12
+
13
+ def dialog(...)
14
+ Dialog.new(...)
15
+ end
16
+
17
+ def trigger(...)
18
+ Trigger.new(...)
19
+ end
20
+
21
+ private
22
+
23
+ def default_attrs
24
+ {
25
+ data: {
26
+ controller: "protos--modal"
27
+ }
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Popover
5
+ class Content < Component
6
+ def template(&block)
7
+ div(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def theme
13
+ {
14
+ container: tokens("dropdown-content", "z-10")
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Popover
5
+ class Trigger < Component
6
+ def template(&block)
7
+ summary(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Popover < Component
5
+ # DOCS: Like a dropdown, but instead of a list of menu items, its just
6
+ # a rendered block
7
+
8
+ PositionTypes = Types::Coercible::Symbol.enum(
9
+ :top,
10
+ :bottom,
11
+ :left,
12
+ :right,
13
+ :start,
14
+ :end
15
+ )
16
+
17
+ option :position,
18
+ type: PositionTypes,
19
+ default: -> { :bottom },
20
+ reader: false
21
+ option :hover, type: Types::Bool, default: -> { false }
22
+
23
+ def template(&block)
24
+ details(**attrs, &block)
25
+ end
26
+
27
+ def content(...)
28
+ Content.new(...)
29
+ end
30
+
31
+ def trigger(...)
32
+ Trigger.new(...)
33
+ end
34
+
35
+ private
36
+
37
+ def position
38
+ {
39
+ bottom: "dropdown-bottom",
40
+ top: "dropdown-top",
41
+ left: "dropdown-left",
42
+ right: "dropdown-right",
43
+ end: "dropdown-end",
44
+ start: "dropdown-start"
45
+ }.fetch(@position)
46
+ end
47
+
48
+ def theme
49
+ {
50
+ container: tokens("dropdown", position)
51
+ }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Body < Component
6
+ def template(&block)
7
+ tbody(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Caption < Component
6
+ def template(&block)
7
+ caption(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def theme
13
+ {
14
+ container: tokens("caption-bottom", "p-sm")
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Cell < Component
6
+ def template(&block)
7
+ td(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Footer < Component
6
+ def template(&block)
7
+ tfoot(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Head < Component
6
+ def template(&block)
7
+ th(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Header < Component
6
+ def template(&block)
7
+ thead(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table
5
+ class Row < Component
6
+ def template(&block)
7
+ tr(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Table < Component
5
+ def template(&block)
6
+ div(**attrs) do
7
+ table(class: css[:table], &block)
8
+ end
9
+ end
10
+
11
+ def body(...)
12
+ Body.new(...)
13
+ end
14
+
15
+ def caption(...)
16
+ Caption.new(...)
17
+ end
18
+
19
+ def cell(...)
20
+ Cell.new(...)
21
+ end
22
+
23
+ def footer(...)
24
+ Footer.new(...)
25
+ end
26
+
27
+ def head(...)
28
+ Head.new(...)
29
+ end
30
+
31
+ def header(...)
32
+ Header.new(...)
33
+ end
34
+
35
+ def row(...)
36
+ Row.new(...)
37
+ end
38
+
39
+ private
40
+
41
+ def theme
42
+ {
43
+ container: tokens("w-full", "overflow-x-auto"),
44
+ table: tokens("table")
45
+ }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Theme
5
+ def initialize(theme = {}, **kwargs)
6
+ @theme = theme.merge(kwargs)
7
+ end
8
+
9
+ def [](key)
10
+ @theme[key]
11
+ end
12
+
13
+ def key?(key)
14
+ @theme.key?(key)
15
+ end
16
+
17
+ def add(key, value)
18
+ current_tokens = parse(@theme.fetch(key, ""))
19
+ new_tokens = parse(value)
20
+ tokens = current_tokens + new_tokens
21
+
22
+ @theme[key] = tokens.to_s
23
+ end
24
+
25
+ def remove(key, value)
26
+ current_tokens = parse(@theme.fetch(key, ""))
27
+ removable_tokens = parse(value)
28
+ tokens = current_tokens - removable_tokens
29
+
30
+ @theme[key] = tokens.to_s
31
+ end
32
+
33
+ def set(key, value)
34
+ if value.is_a?(Hash)
35
+ @theme[key] = value
36
+ else
37
+ tokens = parse(value)
38
+ @theme[key] = tokens.to_s
39
+ end
40
+ end
41
+
42
+ def merge(hash)
43
+ hash ||= {}
44
+
45
+ tap do
46
+ hash.each_key do |key|
47
+ if key?(key)
48
+ add(key, hash[key])
49
+ elsif negation?(key)
50
+ no_bang = key.to_s[1..].to_sym
51
+ remove(no_bang, hash[key])
52
+ elsif override?(key)
53
+ no_bang = key.to_s.chomp("!").to_sym
54
+ set(no_bang, hash[key])
55
+ else
56
+ set(key, hash[key])
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def parse(value)
65
+ TokenList.parse(value)
66
+ end
67
+
68
+ def negation?(key)
69
+ key.to_s.start_with?("!")
70
+ end
71
+
72
+ def override?(key)
73
+ key.to_s.end_with?("!")
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Timeline
5
+ class Center < Component
6
+ def template(&block)
7
+ div(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def theme
13
+ {
14
+ container: tokens("timeline-middle")
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Timeline
5
+ class Item < Component
6
+ def template(&block)
7
+ li(**attrs, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Timeline
5
+ class Left < Component
6
+ def template(&block)
7
+ div(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def theme
13
+ {
14
+ container: tokens("timeline-start")
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Timeline
5
+ class Right < Component
6
+ def template(&block)
7
+ div(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def theme
13
+ {
14
+ container: tokens("timeline-end")
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class Timeline < Component
5
+ option :vertical, type: Types::Bool, default: -> { false }
6
+
7
+ def template(&block)
8
+ ul(**attrs, &block)
9
+ end
10
+
11
+ def item(...)
12
+ Item.new(...)
13
+ end
14
+
15
+ def left(...)
16
+ Left.new(...)
17
+ end
18
+
19
+ def center(...)
20
+ Center.new(...)
21
+ end
22
+
23
+ def right(...)
24
+ Right.new(...)
25
+ end
26
+
27
+ private
28
+
29
+ def css
30
+ {
31
+ container: tokens(
32
+ "timeline",
33
+ vertical: "timeline-vertical"
34
+ )
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ class TokenList
5
+ def self.parse(input)
6
+ case input
7
+ when String then new(input.split)
8
+ when Array then new(input)
9
+ else raise ArgumentError, "Invalid input: #{input.inspect}"
10
+ end
11
+ end
12
+
13
+ attr_reader :tokens
14
+
15
+ def initialize(tokens)
16
+ @tokens = Set.new(tokens)
17
+ end
18
+
19
+ def to_s
20
+ @tokens.to_a.join(" ")
21
+ end
22
+
23
+ def -(other)
24
+ other = TokenList.parse(other) unless other.is_a?(TokenList)
25
+ self.class.new(@tokens - other.tokens)
26
+ end
27
+
28
+ def +(other)
29
+ other = TokenList.parse(other) unless other.is_a?(TokenList)
30
+ self.class.new(@tokens + other.tokens)
31
+ end
32
+
33
+ def remove(token)
34
+ @tokens.delete(token)
35
+ end
36
+
37
+ def add(token)
38
+ @tokens.add(token)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ module Types
5
+ include Dry.Types()
6
+
7
+ MaskShapes = Types::Coercible::Symbol.enum(
8
+ :none,
9
+ :squircle,
10
+ :heart,
11
+ :hexagon,
12
+ :hexagon2,
13
+ :decagon,
14
+ :pentagon,
15
+ :diamond,
16
+ :square,
17
+ :circle,
18
+ :parallelogram,
19
+ :parallelogram2,
20
+ :parallelogram3,
21
+ :parallelogram4,
22
+ :star,
23
+ :star2,
24
+ :triangle,
25
+ :triangle2,
26
+ :triangle3,
27
+ :triangle4,
28
+ :half1,
29
+ :half2
30
+ )
31
+ end
32
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ module Typography
5
+ class Heading < Protos::Component
6
+ SizeTypes = Types::Coercible::Symbol.enum(:xs, :sm, :md, :lg, :xl)
7
+ LevelTypes = Types::Integer.enum(1, 2, 3, 4, 5, 6)
8
+
9
+ option :size, type: SizeTypes, default: -> { "md" }, reader: false
10
+ option :level, type: LevelTypes, default: -> { 1 }, reader: false
11
+
12
+ def template(&block)
13
+ send(element, **attrs, &block)
14
+ end
15
+
16
+ private
17
+
18
+ def theme
19
+ {
20
+ container: tokens(
21
+ size,
22
+ "scroll-m-20",
23
+ "tracking-tight",
24
+ "transition-colors"
25
+ )
26
+ }
27
+ end
28
+
29
+ def size
30
+ {
31
+ xs: "text-lg font-medium",
32
+ sm: "text-xl font-medium",
33
+ md: "text-2xl font-semibold",
34
+ lg: "text-3xl font-semibold md:text-4xl",
35
+ xl: "text-4xl font-bold md:text-5xl"
36
+ }.fetch(@size.to_sym)
37
+ end
38
+
39
+ def element
40
+ %i[h1 h2 h3 h4 h5 h6][@level - 1]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protos
4
+ module Typography
5
+ class InlineLink < Component
6
+ def template(&block)
7
+ a(**attrs, &block)
8
+ end
9
+
10
+ private
11
+
12
+ def theme
13
+ {
14
+ container: tokens(
15
+ "font-medium",
16
+ "hover:underline",
17
+ "underline-offset-4",
18
+ "cursor-pointer"
19
+ )
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end