primer_view_components 0.43.5 → 0.44.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view.d.ts +39 -0
  4. data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  5. data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  6. data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  7. data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.d.ts +42 -0
  8. data/app/assets/javascripts/components/primer/primer.d.ts +4 -0
  9. data/app/assets/javascripts/components/primer/shared_events.d.ts +15 -0
  10. data/app/assets/javascripts/primer_view_components.js +1 -1
  11. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  12. data/app/assets/styles/primer_view_components.css +1 -1
  13. data/app/assets/styles/primer_view_components.css.map +1 -1
  14. data/app/components/primer/alpha/file_tree_view/directory_node.html.erb +5 -0
  15. data/app/components/primer/alpha/file_tree_view/directory_node.rb +24 -0
  16. data/app/components/primer/alpha/file_tree_view/file_node.html.erb +2 -0
  17. data/app/components/primer/alpha/file_tree_view/file_node.rb +14 -0
  18. data/app/components/primer/alpha/file_tree_view.rb +15 -0
  19. data/app/components/primer/alpha/skeleton_box.css +1 -0
  20. data/app/components/primer/alpha/skeleton_box.css.json +6 -0
  21. data/app/components/primer/alpha/skeleton_box.css.map +1 -0
  22. data/app/components/primer/alpha/skeleton_box.html.erb +1 -0
  23. data/app/components/primer/alpha/skeleton_box.pcss +30 -0
  24. data/app/components/primer/alpha/skeleton_box.rb +29 -0
  25. data/app/components/primer/alpha/tree_view/icon.html.erb +1 -0
  26. data/app/components/primer/alpha/tree_view/icon.rb +22 -0
  27. data/app/components/primer/alpha/tree_view/icon_pair.html.erb +13 -0
  28. data/app/components/primer/alpha/tree_view/icon_pair.rb +42 -0
  29. data/app/components/primer/alpha/tree_view/leading_action.html.erb +3 -0
  30. data/app/components/primer/alpha/tree_view/leading_action.rb +18 -0
  31. data/app/components/primer/alpha/tree_view/leaf_node.html.erb +18 -0
  32. data/app/components/primer/alpha/tree_view/leaf_node.rb +96 -0
  33. data/app/components/primer/alpha/tree_view/loading_failure_message.html.erb +13 -0
  34. data/app/components/primer/alpha/tree_view/loading_failure_message.rb +31 -0
  35. data/app/components/primer/alpha/tree_view/node.html.erb +32 -0
  36. data/app/components/primer/alpha/tree_view/node.rb +194 -0
  37. data/app/components/primer/alpha/tree_view/skeleton_loader.html.erb +23 -0
  38. data/app/components/primer/alpha/tree_view/skeleton_loader.rb +36 -0
  39. data/app/components/primer/alpha/tree_view/spinner_loader.html.erb +20 -0
  40. data/app/components/primer/alpha/tree_view/spinner_loader.rb +33 -0
  41. data/app/components/primer/alpha/tree_view/sub_tree.html.erb +21 -0
  42. data/app/components/primer/alpha/tree_view/sub_tree.rb +113 -0
  43. data/app/components/primer/alpha/tree_view/sub_tree_container.html.erb +3 -0
  44. data/app/components/primer/alpha/tree_view/sub_tree_container.rb +39 -0
  45. data/app/components/primer/alpha/tree_view/sub_tree_node.html.erb +49 -0
  46. data/app/components/primer/alpha/tree_view/sub_tree_node.rb +188 -0
  47. data/app/components/primer/alpha/tree_view/tree_view.d.ts +39 -0
  48. data/app/components/primer/alpha/tree_view/tree_view.js +363 -0
  49. data/app/components/primer/alpha/tree_view/tree_view.ts +396 -0
  50. data/app/components/primer/alpha/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  51. data/app/components/primer/alpha/tree_view/tree_view_icon_pair_element.js +62 -0
  52. data/app/components/primer/alpha/tree_view/tree_view_icon_pair_element.ts +56 -0
  53. data/app/components/primer/alpha/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  54. data/app/components/primer/alpha/tree_view/tree_view_include_fragment_element.js +28 -0
  55. data/app/components/primer/alpha/tree_view/tree_view_include_fragment_element.ts +28 -0
  56. data/app/components/primer/alpha/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  57. data/app/components/primer/alpha/tree_view/tree_view_roving_tab_index.js +130 -0
  58. data/app/components/primer/alpha/tree_view/tree_view_roving_tab_index.ts +161 -0
  59. data/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.d.ts +42 -0
  60. data/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.js +418 -0
  61. data/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.ts +470 -0
  62. data/app/components/primer/alpha/tree_view/visual.html.erb +14 -0
  63. data/app/components/primer/alpha/tree_view/visual.rb +27 -0
  64. data/app/components/primer/alpha/tree_view.css +1 -0
  65. data/app/components/primer/alpha/tree_view.css.json +52 -0
  66. data/app/components/primer/alpha/tree_view.css.map +1 -0
  67. data/app/components/primer/alpha/tree_view.html.erb +12 -0
  68. data/app/components/primer/alpha/tree_view.pcss +373 -0
  69. data/app/components/primer/alpha/tree_view.rb +439 -0
  70. data/app/components/primer/beta/breadcrumbs.css +1 -1
  71. data/app/components/primer/beta/breadcrumbs.css.json +0 -1
  72. data/app/components/primer/beta/breadcrumbs.css.map +1 -1
  73. data/app/components/primer/beta/breadcrumbs.pcss +2 -8
  74. data/app/components/primer/beta/progress_bar.css +1 -1
  75. data/app/components/primer/beta/progress_bar.css.map +1 -1
  76. data/app/components/primer/beta/progress_bar.pcss +3 -2
  77. data/app/components/primer/beta/relative_time.rb +3 -0
  78. data/app/components/primer/beta/spinner.html.erb +1 -1
  79. data/app/components/primer/beta/spinner.rb +2 -0
  80. data/app/components/primer/primer.d.ts +4 -0
  81. data/app/components/primer/primer.js +4 -0
  82. data/app/components/primer/primer.pcss +2 -0
  83. data/app/components/primer/primer.ts +4 -0
  84. data/app/components/primer/shared_events.d.ts +15 -0
  85. data/app/components/primer/shared_events.ts +19 -0
  86. data/app/controllers/primer/view_components/tree_view_items.json +293 -0
  87. data/app/controllers/primer/view_components/tree_view_items_controller.rb +55 -0
  88. data/app/forms/check_box_with_nested_form.rb +10 -10
  89. data/app/forms/radio_button_with_nested_form.rb +16 -16
  90. data/app/views/primer/view_components/tree_view_items/async_alpha.html_fragment.erb +23 -0
  91. data/app/views/primer/view_components/tree_view_items/index.html_fragment.erb +24 -0
  92. data/config/routes.rb +2 -0
  93. data/lib/primer/view_components/version.rb +2 -2
  94. data/previews/primer/alpha/file_tree_view_preview/default.html.erb +16 -0
  95. data/previews/primer/alpha/file_tree_view_preview/playground.html.erb +4 -0
  96. data/previews/primer/alpha/file_tree_view_preview.rb +69 -0
  97. data/previews/primer/alpha/skeleton_box_preview.rb +20 -0
  98. data/previews/primer/alpha/tree_view_preview/async_alpha.html.erb +12 -0
  99. data/previews/primer/alpha/tree_view_preview/buttons.html.erb +10 -0
  100. data/previews/primer/alpha/tree_view_preview/default.html.erb +24 -0
  101. data/previews/primer/alpha/tree_view_preview/empty.html.erb +10 -0
  102. data/previews/primer/alpha/tree_view_preview/form_input.html.erb +14 -0
  103. data/previews/primer/alpha/tree_view_preview/leaf_node_playground.html.erb +15 -0
  104. data/previews/primer/alpha/tree_view_preview/links.html.erb +17 -0
  105. data/previews/primer/alpha/tree_view_preview/loading_failure.html.erb +36 -0
  106. data/previews/primer/alpha/tree_view_preview/loading_skeleton.html.erb +12 -0
  107. data/previews/primer/alpha/tree_view_preview/loading_spinner.html.erb +12 -0
  108. data/previews/primer/alpha/tree_view_preview/playground.html.erb +4 -0
  109. data/previews/primer/alpha/tree_view_preview.rb +208 -0
  110. data/static/arguments.json +456 -0
  111. data/static/audited_at.json +17 -0
  112. data/static/classes.json +15 -0
  113. data/static/constants.json +101 -0
  114. data/static/info_arch.json +1410 -56
  115. data/static/previews.json +232 -0
  116. data/static/statuses.json +17 -0
  117. metadata +89 -8
@@ -0,0 +1,439 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The TreeView component is made up of a number of smaller components, quite a few more than have been created
4
+ # to construct complex components in the past. The current architecture was designed to achieve reusability for
5
+ # certain features like loading indicators, different icons for expanded and collapsed sub trees, etc. The
6
+ # following describes how the components fit together at a high level, using React-like syntax. Each element
7
+ # in the diagram corresponds to one of three types of object:
8
+ #
9
+ # 1. Elements with TitleCase tags represent components in the Primer::Alpha::TreeView namespace, eg. <LeafNode>.
10
+ # 2. Elements with dasherized-tags are web components/custom elements, eg. <tree-view>.
11
+ # 3. Elements with lowercase tags are regular 'ol HTML elements, eg. <ul>.
12
+ #
13
+ # ### Overall structure
14
+ #
15
+ # <TreeView>
16
+ # <tree-view>
17
+ # <ul role="tree">
18
+ # <LeafNode>
19
+ # <Node>
20
+ # <li role="none">
21
+ # <div role="treeitem">
22
+ # ...
23
+ # </div>
24
+ # </li>
25
+ # </Node>
26
+ # </LeafNode>
27
+ #
28
+ # <SubTreeNode>
29
+ # <tree-view-sub-tree-node>
30
+ # <li role="none">
31
+ #
32
+ # <SubTreeContainer>
33
+ # <ul role="group">
34
+ # <SubTree>
35
+ # <LeafNode>
36
+ # <Node>
37
+ # <li role="none">
38
+ # <div role="treeitem">
39
+ # ...
40
+ # </div>
41
+ # </li>
42
+ # </Node>
43
+ # </LeafNode>
44
+ #
45
+ # <SubTreeNode>
46
+ # <tree-view-sub-tree-node>
47
+ # <Node>
48
+ # <li role="none">
49
+ # <div role="treeitem">
50
+ # ...
51
+ # </div>
52
+ # </li>
53
+ # </Node>
54
+ # <SubTreeContainer>
55
+ # <ul role="group">
56
+ # ...
57
+ # </ul>
58
+ # </SubTreeContainer>
59
+ # </tree-view-sub-tree-node>
60
+ # </SubTreeNode>
61
+ # </SubTree>
62
+ # </ul>
63
+ # </SubTreeContainer>
64
+ #
65
+ # </li>
66
+ # </tree-view-sub-tree-node>
67
+ # </SubTreeNode>
68
+ # </ul>
69
+ # </tree-view>
70
+ # </TreeView>
71
+ #
72
+ # ### Leading visuals
73
+ #
74
+ # TreeView nodes (i.e. both leaf and sub tree nodes) support leading and trailing visuals. At the time of this
75
+ # writing, only octicons are supported. The single icon case is achieved by using a standard slot, but the
76
+ # component also supports rendering distinct icons for both the expanded and collapsed states. An overview of the
77
+ # markup for this more complicated multi-icon feature is described below.
78
+ #
79
+ # <LeafNode>
80
+ # <Node>
81
+ # <li role="none">
82
+ # <div role="treeitem">
83
+ # <Visual>
84
+ # <IconPair>
85
+ # <tree-view-icon-pair>
86
+ # <Icon slot="expanded_icon">
87
+ # <Primer::Beta::Octicon />
88
+ # </Icon>
89
+ # <Icon slot="collapsed_icon">
90
+ # <Primer::Beta::Octicon />
91
+ # </Icon>
92
+ # </tree-view-icon-pair>
93
+ # </IconPair>
94
+ # </Visual>
95
+ # </div>
96
+ # </li>
97
+ # </Node>
98
+ # </LeafNode>
99
+ #
100
+ # ### Loaders
101
+ #
102
+ # TreeViews support two types of loader: a loading spinner and a loading skeleton.
103
+ #
104
+ # #### Loading spinner
105
+ #
106
+ # <SubTree>
107
+ # <SpinnerLoader>
108
+ # <tree-view-include-fragment>
109
+ # <SubTreeContainer>
110
+ # <Node>
111
+ # <Primer::Beta::Spinner />
112
+ # </Node>
113
+ # <Node>
114
+ # <LoadingFailureMessage />
115
+ # </Node>
116
+ # </SubTreeContainer>
117
+ # </tree-view-include-fragment>
118
+ # </SpinnerLoader>
119
+ # </SubTree>
120
+ #
121
+ # #### Loading skeleton
122
+ #
123
+ # <SubTree>
124
+ # <SkeletonLoader>
125
+ # <tree-view-include-fragment>
126
+ # <SubTreeContainer>
127
+ # <Node>
128
+ # <span>
129
+ # <Primer::Alpha::SkeletonBox width="16px" />
130
+ # <Primer::Alpha::SkeletonBox width="100%" />
131
+ # </span>
132
+ # <span>
133
+ # ...
134
+ # </span>
135
+ # ...
136
+ # </Node>
137
+ # <Node>
138
+ # <LoadingFailureMessage />
139
+ # </Node>
140
+ # </SubTreeContainer>
141
+ # </tree-view-include-fragment>
142
+ # </SkeletonLoader>
143
+ # </SubTree>
144
+
145
+ module Primer
146
+ module Alpha
147
+ # TreeView is a hierarchical list of items that may have a parent-child relationship where children
148
+ # can be toggled into view by expanding or collapsing their parent item.
149
+ #
150
+ # ## Terminology
151
+ #
152
+ # Consider the following tree structure:
153
+ #
154
+ # src
155
+ # ├ button.rb
156
+ # └ action_list
157
+ # ├ item.rb
158
+ # └ header.rb
159
+ #
160
+ # 1. **Node**. A node is an item in the tree. Nodes can either be "leaf" nodes (i.e. have no children), or "sub-tree"
161
+ # nodes, which do have children. In the example above, button.rb, item.rb, and header.rb are all leaf nodes, while
162
+ # action_list is a sub-tree node.
163
+ # 2. **Path**. A node's path is like its ID. It's an array of strings containing the current node's label and all the
164
+ # labels of its ancestors, in order. In the example above, header.rb's path is ["src", "action_list", "header.rb"].
165
+ #
166
+ # ## Static nodes
167
+ #
168
+ # The `TreeView` component allows items to be provided statically or loaded dynamically from the server.
169
+ # Providing items statically is done using the `leaf` and `sub_tree` slots:
170
+ #
171
+ # ```erb
172
+ # <%= render(Primer::Alpha::TreeView.new) do |tree| %>
173
+ # <% tree.with_sub_tree(label: "Directory") do |sub_tree| %>
174
+ # <% sub_tree.with_leaf(label: "File 1")
175
+ # <% end %>
176
+ # <% tree.with_leaf(label: "File 2") %>
177
+ # <% end %>
178
+ # ```
179
+ #
180
+ # ## Dynamic nodes
181
+ #
182
+ # Tree nodes can also be fetched dynamically from the server and will require creating a Rails controller action
183
+ # to respond with the list of nodes. Unlike other Primer components, `TreeView` allows the programmer to specify
184
+ # loading behavior on a per-sub-tree basis, i.e. each sub-tree must specify how its nodes are loaded. To load nodes
185
+ # dynamically for a given sub-tree, configure it with either a loading spinner or a loading skeleton, and provide
186
+ # the URL to fetch nodes from:
187
+ #
188
+ # ```erb
189
+ # <%= render(Primer::Alpha::TreeView.new) do |tree| %>
190
+ # <% tree.with_sub_tree(label: "Directory") do |sub_tree| %>
191
+ # <% sub_tree.with_loading_spinner(src: primer_view_components.tree_view_items_path) %>
192
+ # <% end %>
193
+ # <% end %>
194
+ # ```
195
+ #
196
+ # Define a controller action to serve the list of nodes. The `TreeView` component automatically includes the
197
+ # sub-tree's path as a GET parameter, encoded as a JSON array.
198
+ #
199
+ # ```ruby
200
+ # class TreeViewItemsController < ApplicationController
201
+ # def show
202
+ # @path = JSON.parse(params[:path])
203
+ # @results = get_tree_items(starting_at: path)
204
+ # end
205
+ # end
206
+ # ```
207
+ #
208
+ # Responses must be HTML fragments, eg. have a content type of `text/html+fragment`. This content type isn't
209
+ # available by default in Rails, so you may have to register it eg. in an initializer:
210
+ #
211
+ # ```ruby
212
+ # Mime::Type.register("text/fragment+html", :html_fragment)
213
+ # ```
214
+ #
215
+ # Render a `Primer::Alpha::TreeView::SubTree` in the action's template, tree_view_items/show.html_fragment.erb:
216
+ #
217
+ # ```erb
218
+ # <%= render(Primer::Alpha::TreeView::SubTree.new(path: @path, node_variant: :div)) do |tree| %>
219
+ # <% tree.with_leaf(...) %>
220
+ # <% tree.with_sub_tree(...) do |sub_tree| %>
221
+ # ...
222
+ # <% end %>
223
+ # <% end %>
224
+ # ```
225
+ #
226
+ # ## Multi-select mode
227
+ #
228
+ # Passing `select_variant: :multiple` to both sub-tree and leaf nodes will add a check box to the left of the node's
229
+ # label. These check boxes behave according to the value of a second argument, `select_strategy:`.
230
+ #
231
+ # The default select strategy, `:descendants`, will cause all child nodes to be checked when the node is checked.
232
+ # This includes both sub-tree and leaf nodes. When the node is unchecked, all child nodes will also be unchecked.
233
+ # Unchecking a child node of a checked parent will cause the parent to enter a mixed or indeterminate state, which
234
+ # is represented by a horizontal line icon instead of a check mark. This icon indicates that some children are
235
+ # checked, but not all.
236
+ #
237
+ # A secondary select strategy, `:self`, is provided to allow disabling the automatic checking of child nodes. When
238
+ # `select_strategy: :self` is specified, checking sub-tree nodes does not check child nodes, and sub-tree nodes
239
+ # cannot enter a mixed or indeterminate state.
240
+ #
241
+ # Nodes can be checked via the keyboard by pressing the space key.
242
+ #
243
+ # ## Node tags
244
+ #
245
+ # `TreeView`s support three different node variants, `:anchor`, `:button`, and `:div` (the default), which controls
246
+ # which HTML tag is used to construct the nodes. The `:anchor` and `:button` variants correspond to `<a>` and
247
+ # `<button>` tags respectively, which are browser-native elements. Anchors and buttons can be activated (i.e.
248
+ # "clicked") using the mouse or keyboard via the enter or space keys. The node variant must be the same for all
249
+ # nodes in the tree, and is therefore specified at the root level, eg. `TreeView.new(node_variant: :anchor)`.
250
+ #
251
+ # Trees with node variants other than `:div` cannot have check boxes, i.e. cannot be put into multi-select mode.
252
+ #
253
+ # Trees with node variants other than `:div` do not emit the `treeViewNodeActivated` or `treeViewBeforeNodeActivated`
254
+ # events, since it is assumed any behavior associated with these variants is user- or browser-defined.
255
+ #
256
+ # ## Interaction behavior matrix
257
+ #
258
+ # |Interaction |Select variant|Tag |Result |
259
+ # |:---------------|:-------------|:------------|:--------------------------|
260
+ # |Enter/space |none |div |Expands/collapses |
261
+ # |Enter/space |none |anchor/button|Activates anchor/button |
262
+ # |Enter/space |multiple |div |Checks or unchecks |
263
+ # |Enter/space |multiple |anchor/button|N/A (not allowed) |
264
+ # |Left/right arrow|none |div |Expands/collapses |
265
+ # |Left/right arrow|none |anchor/button|Expands/collapses |
266
+ # |Left/right arrow|multiple |div |Expands/collapses |
267
+ # |Left/right arrow|multiple |anchor/button|N/A (not allowed) |
268
+ # |Click |none |div |Expands/collapses |
269
+ # |Click |multiple |div |Checks or unchecks |
270
+ # |Click |multiple |anchor/button|N/A (not allowed) |
271
+ #
272
+ # ## JavaScript API
273
+ #
274
+ # `TreeView`s render a `<tree-view>` custom element that exposes behavior to the client.
275
+ #
276
+ # |Name |Notes |
277
+ # |:-----------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
278
+ # |`getNodePath(node: Element): string[]` |Returns the path to the given node. |
279
+ # |`getNodeType(node: Element): TreeViewNodeType | null` |Returns either `"leaf"` or `"sub-tree"`. |
280
+ # |`markCurrentAtPath(path: string[])` |Marks the node as the "current" node, which appears visually distinct from other nodes. |
281
+ # |`get currentNode(): HTMLLIElement | null` |Returns the current node. |
282
+ # |`expandAtPath(path: string[])` |Expands the sub-tree at `path`. |
283
+ # |`collapseAtPath(path: string[])` |Collapses the sub-tree at `path`. |
284
+ # |`toggleAtPath(path: string[])` |If the sub-tree at `path` is collapsed, this function expands it, and vice-versa. |
285
+ # |`checkAtPath(path: string[])` |If the node at `path` has a checkbox, this function checks it. |
286
+ # |`uncheckAtPath(path: string[])` |If the node at `path` has a checkbox, this function unchecks it. |
287
+ # |`toggleCheckedAtPath(path: string[])` |If the sub-tree at `path` is checked, this function unchecks it, and vice-versa. |
288
+ # |`checkedValueAtPath(path: string[]): TreeViewCheckedValue` |Returns `"true"` (all child nodes are checked), `"false"` (no child nodes are checked), or `"mixed"` (some child nodes are checked, some are not).|
289
+ # |`nodeAtPath(path: string[], selector?: string): Element | null` |Returns the node for the given `path`, either a leaf node or sub-tree node. |
290
+ # |`subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null`|Returns the sub-tree at the given `path`, if it exists. |
291
+ # |`leafAtPath(path: string[]): HTMLLIElement | null` |Returns the leaf node at the given `path`, if it exists. |
292
+ # |`getNodeCheckedValue(node: Element): TreeViewCheckedValue` |The same as `checkedValueAtPath`, but accepts a node instead of a path. |
293
+ #
294
+ # ### Events
295
+ #
296
+ # The events enumerated below include node information by way of the `TreeViewNodeInfo` object, which has the
297
+ # following signature:
298
+ #
299
+ # ```typescript
300
+ # type TreeViewNodeType = 'leaf' | 'sub-tree'
301
+ # type TreeViewCheckedValue = 'true' | 'false' | 'mixed'
302
+ #
303
+ # type TreeViewNodeInfo = {
304
+ # node: Element
305
+ # type: TreeViewNodeType
306
+ # path: string[]
307
+ # checkedValue: TreeViewCheckedValue
308
+ # previousCheckedValue: TreeViewCheckedValue
309
+ # }
310
+ # ```
311
+ #
312
+ # |Name |Type |Bubbles |Cancelable |
313
+ # |:----------------------------|:------------------------------------------|:-------|:----------|
314
+ # |`treeViewNodeActivated` |`CustomEvent<TreeViewNodeInfo>` |Yes |No |
315
+ # |`treeViewBeforeNodeActivated`|`CustomEvent<TreeViewNodeInfo>` |Yes |Yes |
316
+ # |`treeViewNodeExpanded` |`CustomEvent<TreeViewNodeInfo>>` |Yes |No |
317
+ # |`treeViewNodeCollapsed` |`CustomEvent<TreeViewNodeInfo>>` |Yes |No |
318
+ # |`treeViewNodeChecked` |`CustomEvent<TreeViewNodeInfo[]>` |Yes |Yes |
319
+ # |`treeViewBeforeNodeChecked` |`CustomEvent<TreeViewNodeInfo[]>` |Yes |No |
320
+ #
321
+ # _Item activation_
322
+ #
323
+ # The `<tree-view>` element fires an `treeViewNodeActivated` event whenever a node is activated (eg. clicked)
324
+ # via the mouse or keyboard.
325
+ #
326
+ # The `treeViewBeforeNodeActivated` event fires before a node is activated. Canceling this event will prevent the
327
+ # node from being activated.
328
+ #
329
+ # ```typescript
330
+ # document.querySelector("select-panel").addEventListener(
331
+ # "treeViewBeforeNodeActivated",
332
+ # (event: CustomEvent<TreeViewNodeInfo>) => {
333
+ # event.preventDefault() // Cancel the event to prevent activation (eg. expanding/collapsing)
334
+ # }
335
+ # )
336
+ # ```
337
+ #
338
+ # _Item checking/unchecking_
339
+ #
340
+ # The `tree-view` element fires a `treeViewNodeChecked` event whenever a node is checked or unchecked.
341
+ #
342
+ # The `treeViewBeforeNodeChecked` event fires before a node is checked or unchecked. Canceling this event will
343
+ # prevent the check/uncheck operation.
344
+ #
345
+ # ```typescript
346
+ # document.querySelector("select-panel").addEventListener(
347
+ # "treeViewBeforeNodeChecked",
348
+ # (event: CustomEvent<TreeViewNodeInfo[]>) => {
349
+ # event.preventDefault() // Cancel the event to prevent activation (eg. expanding/collapsing)
350
+ # }
351
+ # )
352
+ # ```
353
+ #
354
+ # Because checking or unchecking a sub-tree results in the checking or unchecking of all its children recursively,
355
+ # both the `treeViewNodeChecked` and `treeViewBeforeNodeChecked` events provide an array of `TreeViewNodeInfo`
356
+ # objects, which contain entries for every modified node in the tree.
357
+ class TreeView < Primer::Component
358
+ status :alpha
359
+
360
+ DEFAULT_NODE_VARIANT = :div
361
+ NODE_VARIANT_OPTIONS = [DEFAULT_NODE_VARIANT, :anchor, :button].freeze
362
+
363
+ # @!parse
364
+ # # Adds an leaf node to the tree. Leaf nodes are nodes that do not have children.
365
+ # #
366
+ # # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Primer::Alpha::TreeView::LeafNode) %>
367
+ # # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Primer::Alpha::TreeView::LeafNode) %>, or whatever class is passed as the `component_klass` argument.
368
+ # def with_leaf(**system_arguments, &block)
369
+ # end
370
+
371
+ # @!parse
372
+ # # Adds a sub-tree node to the tree. Sub-trees are nodes that have children, which can be both leaf nodes and other sub-trees.
373
+ # #
374
+ # # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Primer::Alpha::TreeView::SubTreeNode) %>
375
+ # # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Primer::Alpha::TreeView::SubTreeNode) %>, or whatever class is passed as the `component_klass` argument.
376
+ # def with_sub_tree(**system_arguments, &block)
377
+ # end
378
+
379
+ renders_many :nodes, types: {
380
+ leaf: {
381
+ renders: lambda { |component_klass: LeafNode, label:, **system_arguments|
382
+ component_klass.new(
383
+ **system_arguments,
384
+ node_variant: node_variant,
385
+ path: [label],
386
+ label: label
387
+ )
388
+ },
389
+
390
+ as: :leaf
391
+ },
392
+
393
+ sub_tree: {
394
+ renders: lambda { |component_klass: SubTreeNode, label:, **system_arguments|
395
+ component_klass.new(
396
+ **system_arguments,
397
+ node_variant: node_variant,
398
+ path: [label],
399
+ label: label
400
+ )
401
+ },
402
+
403
+ as: :sub_tree
404
+ }
405
+ }
406
+
407
+ attr_reader :node_variant
408
+
409
+ # @param node_variant [Symbol] The variant to use for this node. <%= one_of(Primer::Alpha::TreeView::NODE_VARIANT_OPTIONS) %>
410
+ # @param form_arguments [Hash] These arguments allow the selections made within a `TreeView` to be submitted to the server as part of a Rails form. Pass the `builder:` and `name:` options to this hash. `builder:` should be an instance of `ActionView::Helpers::FormBuilder`, which are created by the standard Rails `#form_with` and `#form_for` helpers. The `name:` option is the desired name of the field that will be included in the params sent to the server on form submission.
411
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>.
412
+ def initialize(node_variant: DEFAULT_NODE_VARIANT, form_arguments: {}, **system_arguments)
413
+ @system_arguments = deny_tag_argument(**system_arguments)
414
+ @form_arguments = form_arguments
415
+
416
+ @node_variant = fetch_or_fallback(NODE_VARIANT_OPTIONS, node_variant, DEFAULT_NODE_VARIANT)
417
+
418
+ @system_arguments[:tag] = :ul
419
+ @system_arguments[:role] = :tree
420
+ @system_arguments[:classes] = class_names(
421
+ @system_arguments.delete(:classes),
422
+ "TreeViewRootUlStyles"
423
+ )
424
+ end
425
+
426
+ def acts_as_form_input?
427
+ @form_arguments[:builder] && @form_arguments[:name]
428
+ end
429
+
430
+ private
431
+
432
+ def before_render
433
+ if (first_node = nodes.first)
434
+ first_node.merge_system_arguments!(tabindex: 0)
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end
@@ -1 +1 @@
1
- .breadcrumb-item{display:inline-block;list-style:none;margin-left:-.35em;max-width:100%}.breadcrumb-item:after{border-right:.1em solid var(--borderColor-neutral-emphasis);content:"";display:inline-block;height:.8em;margin:0 .5em;transform:rotate(15deg) translateY(.0625em)}.breadcrumb-item:first-child{margin-left:0}:is(.breadcrumb-item-selected,.breadcrumb-item[aria-current]:not([aria-current=false])):after{content:none}.breadcrumb-item-selected a{color:var(--fgColor-default);cursor:default!important;-webkit-text-decoration:none!important;text-decoration:none!important}
1
+ .breadcrumb-item{display:inline-block;list-style:none;max-width:100%}.breadcrumb-item:after{border-right:.1em solid var(--borderColor-neutral-emphasis);content:"";display:inline-block;height:.8em;margin:0 .15em 0 .5em;transform:rotate(15deg) translateY(.0625em)}:is(.breadcrumb-item-selected,.breadcrumb-item[aria-current]:not([aria-current=false])):after{content:none}.breadcrumb-item-selected a{color:var(--fgColor-default);cursor:default!important;-webkit-text-decoration:none!important;text-decoration:none!important}
@@ -3,7 +3,6 @@
3
3
  "selectors": [
4
4
  ".breadcrumb-item",
5
5
  ".breadcrumb-item:after",
6
- ".breadcrumb-item:first-child",
7
6
  ":is(.breadcrumb-item-selected,.breadcrumb-item[aria-current]:not([aria-current=false])):after",
8
7
  ".breadcrumb-item-selected a"
9
8
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["breadcrumbs.pcss"],"names":[],"mappings":"AAAA,iBACE,oBAAqB,CAGrB,eAAgB,CADhB,kBAAoB,CAEpB,cAgBF,CAdE,uBAOE,2DAA6D,CAF7D,UAAW,CAJX,oBAAqB,CACrB,WAAa,CAEb,aAAe,CAIf,2CACF,CAEA,6BACE,aACF,CAKA,8FACE,YACF,CAIF,4BACE,4BAA6B,CAE7B,wBAA0B,CAD1B,sCAAgC,CAAhC,8BAEF","file":"breadcrumbs.css","sourcesContent":[".breadcrumb-item {\n display: inline-block;\n /* stylelint-disable-next-line primer/spacing */\n margin-left: -0.35em;\n list-style: none;\n max-width: 100%;\n\n &::after {\n display: inline-block;\n height: 0.8em;\n /* stylelint-disable-next-line primer/spacing */\n margin: 0 0.5em;\n content: '';\n /* stylelint-disable-next-line primer/borders */\n border-right: 0.1em solid var(--borderColor-neutral-emphasis);\n transform: rotate(15deg) translateY(0.0625em);\n }\n\n &:first-child {\n margin-left: 0;\n }\n}\n\n.breadcrumb-item-selected,\n.breadcrumb-item[aria-current]:not([aria-current='false']) {\n &::after {\n content: none;\n }\n}\n\n/* stylelint-disable-next-line selector-max-type */\n.breadcrumb-item-selected a {\n color: var(--fgColor-default);\n text-decoration: none !important;\n cursor: default !important;\n}\n"]}
1
+ {"version":3,"sources":["breadcrumbs.pcss"],"names":[],"mappings":"AAAA,iBACE,oBAAqB,CACrB,eAAgB,CAChB,cAYF,CAVE,uBAOE,2DAA6D,CAF7D,UAAW,CAJX,oBAAqB,CACrB,WAAa,CAEb,qBAAwB,CAIxB,2CACF,CAKA,8FACE,YACF,CAIF,4BACE,4BAA6B,CAE7B,wBAA0B,CAD1B,sCAAgC,CAAhC,8BAEF","file":"breadcrumbs.css","sourcesContent":[".breadcrumb-item {\n display: inline-block;\n list-style: none;\n max-width: 100%;\n\n &::after {\n display: inline-block;\n height: 0.8em;\n /* stylelint-disable-next-line primer/spacing */\n margin: 0 0.15em 0 0.5em;\n content: '';\n /* stylelint-disable-next-line primer/borders */\n border-right: 0.1em solid var(--borderColor-neutral-emphasis);\n transform: rotate(15deg) translateY(0.0625em);\n }\n}\n\n.breadcrumb-item-selected,\n.breadcrumb-item[aria-current]:not([aria-current='false']) {\n &::after {\n content: none;\n }\n}\n\n/* stylelint-disable-next-line selector-max-type */\n.breadcrumb-item-selected a {\n color: var(--fgColor-default);\n text-decoration: none !important;\n cursor: default !important;\n}"]}
@@ -1,7 +1,5 @@
1
1
  .breadcrumb-item {
2
2
  display: inline-block;
3
- /* stylelint-disable-next-line primer/spacing */
4
- margin-left: -0.35em;
5
3
  list-style: none;
6
4
  max-width: 100%;
7
5
 
@@ -9,16 +7,12 @@
9
7
  display: inline-block;
10
8
  height: 0.8em;
11
9
  /* stylelint-disable-next-line primer/spacing */
12
- margin: 0 0.5em;
10
+ margin: 0 0.15em 0 0.5em;
13
11
  content: '';
14
12
  /* stylelint-disable-next-line primer/borders */
15
13
  border-right: 0.1em solid var(--borderColor-neutral-emphasis);
16
14
  transform: rotate(15deg) translateY(0.0625em);
17
15
  }
18
-
19
- &:first-child {
20
- margin-left: 0;
21
- }
22
16
  }
23
17
 
24
18
  .breadcrumb-item-selected,
@@ -33,4 +27,4 @@
33
27
  color: var(--fgColor-default);
34
28
  text-decoration: none !important;
35
29
  cursor: default !important;
36
- }
30
+ }
@@ -1 +1 @@
1
- .Progress{background-color:var(--bgColor-neutral-muted);border-radius:var(--borderRadius-medium);display:flex;height:8px;outline:1px solid #0000;overflow:hidden}.Progress--large{height:10px}.Progress--small{height:5px}.Progress-item{outline:2px solid #0000}.Progress-item+.Progress-item{margin-left:var(--base-size-2)}
1
+ .Progress{background-color:var(--progressBar-track-bgColor);border-radius:var(--borderRadius-medium);display:flex;height:8px;outline:solid 1px var(--progressBar-track-borderColor);outline-offset:-1px;overflow:hidden}.Progress--large{height:10px}.Progress--small{height:5px}.Progress-item{outline:2px solid #0000}.Progress-item+.Progress-item{margin-left:var(--base-size-2)}
@@ -1 +1 @@
1
- {"version":3,"sources":["progress_bar.pcss"],"names":[],"mappings":"AAEA,UAIE,6CAA8C,CAC9C,wCAAyC,CAJzC,YAAa,CACb,UAAW,CAIX,uBAA8B,CAH9B,eAIF,CAEA,iBACE,WACF,CAEA,iBACE,UACF,CAEA,eACE,uBACF,CAEA,8BACE,8BACF","file":"progress_bar.css","sourcesContent":["/* Progress */\n\n.Progress {\n display: flex;\n height: 8px;\n overflow: hidden;\n background-color: var(--bgColor-neutral-muted);\n border-radius: var(--borderRadius-medium);\n outline: 1px solid transparent; /* Support Firefox custom colors */\n}\n\n.Progress--large {\n height: 10px;\n}\n\n.Progress--small {\n height: 5px;\n}\n\n.Progress-item {\n outline: 2px solid transparent; /* Support Firefox custom colors */\n}\n\n.Progress-item + .Progress-item {\n margin-left: var(--base-size-2);\n}\n"]}
1
+ {"version":3,"sources":["progress_bar.pcss"],"names":[],"mappings":"AAEA,UAIE,iDAAkD,CAClD,wCAAyC,CAJzC,YAAa,CACb,UAAW,CAIX,sDAAuD,CACvD,mBAAoB,CAJpB,eAKF,CAEA,iBACE,WACF,CAEA,iBACE,UACF,CAEA,eACE,uBACF,CAEA,8BACE,8BACF","file":"progress_bar.css","sourcesContent":["/* Progress */\n\n.Progress {\n display: flex;\n height: 8px;\n overflow: hidden;\n background-color: var(--progressBar-track-bgColor);\n border-radius: var(--borderRadius-medium);\n outline: solid 1px var(--progressBar-track-borderColor);\n outline-offset: -1px;\n}\n\n.Progress--large {\n height: 10px;\n}\n\n.Progress--small {\n height: 5px;\n}\n\n.Progress-item {\n outline: 2px solid transparent; /* Support Firefox custom colors */\n}\n\n.Progress-item + .Progress-item {\n margin-left: var(--base-size-2);\n}\n"]}
@@ -4,9 +4,10 @@
4
4
  display: flex;
5
5
  height: 8px;
6
6
  overflow: hidden;
7
- background-color: var(--bgColor-neutral-muted);
7
+ background-color: var(--progressBar-track-bgColor);
8
8
  border-radius: var(--borderRadius-medium);
9
- outline: 1px solid transparent; /* Support Firefox custom colors */
9
+ outline: solid 1px var(--progressBar-track-borderColor);
10
+ outline-offset: -1px;
10
11
  }
11
12
 
12
13
  .Progress--large {
@@ -102,6 +102,7 @@ module Primer
102
102
  # @param lang [String] The language to use.
103
103
  # @param title [String] Provide a custom title to the element.
104
104
  # @param no_title [Boolean] Removes the `title` attribute provided on the element by default.
105
+ # @param aria_hidden [Boolean] Set if the element is hidden or not.
105
106
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
106
107
  def initialize(
107
108
  datetime:,
@@ -122,6 +123,7 @@ module Primer
122
123
  lang: nil,
123
124
  title: nil,
124
125
  no_title: false,
126
+ aria_hidden: nil,
125
127
  **system_arguments
126
128
  )
127
129
  @system_arguments = deny_tag_argument(**system_arguments)
@@ -143,6 +145,7 @@ module Primer
143
145
  @system_arguments[:lang] = lang if lang.present?
144
146
  @system_arguments[:format] = fetch_or_fallback(FORMAT_OPTIONS, format, FORMAT_DEFAULT) if format.present?
145
147
  @system_arguments[:"format-style"] = format_style if format_style.present?
148
+ @system_arguments[:"aria-hidden"] = aria_hidden if aria_hidden.present?
146
149
  if datetime.present? && datetime.respond_to?(:iso8601)
147
150
  @datetime = datetime
148
151
  @system_arguments[:datetime] = datetime.iso8601
@@ -1,4 +1,4 @@
1
- <%= render(Primer::BaseComponent.new(tag: :span, hidden: @hidden, data: { target: @target })) do %>
1
+ <%= render(Primer::BaseComponent.new(tag: :span, hidden: @hidden, data: { target: @target }, **@wrapper_arguments)) do %>
2
2
  <%= render Primer::BaseComponent.new(**@system_arguments) do %>
3
3
  <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-opacity="0.25" stroke-width="2" vector-effect="non-scaling-stroke" fill="none" />
4
4
  <path d="M15 8a7.002 7.002 0 00-7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" vector-effect="non-scaling-stroke" />
@@ -43,6 +43,8 @@ module Primer
43
43
 
44
44
  @target = extract_data(:target, @system_arguments)
45
45
  @hidden = @system_arguments.delete(:hidden)
46
+
47
+ @wrapper_arguments = @system_arguments.delete(:wrapper_arguments) || {}
46
48
  end
47
49
 
48
50
  def no_aria_label?
@@ -26,3 +26,7 @@ import '../../lib/primer/forms/toggle_switch_input';
26
26
  import './alpha/action_menu/action_menu_element';
27
27
  import './alpha/select_panel_element';
28
28
  import './beta/details_toggle_element';
29
+ import './alpha/tree_view/tree_view';
30
+ import './alpha/tree_view/tree_view_icon_pair_element';
31
+ import './alpha/tree_view/tree_view_sub_tree_node_element';
32
+ import './alpha/tree_view/tree_view_include_fragment_element';
@@ -26,3 +26,7 @@ import '../../lib/primer/forms/toggle_switch_input';
26
26
  import './alpha/action_menu/action_menu_element';
27
27
  import './alpha/select_panel_element';
28
28
  import './beta/details_toggle_element';
29
+ import './alpha/tree_view/tree_view';
30
+ import './alpha/tree_view/tree_view_icon_pair_element';
31
+ import './alpha/tree_view/tree_view_sub_tree_node_element';
32
+ import './alpha/tree_view/tree_view_include_fragment_element';
@@ -17,6 +17,8 @@
17
17
  @import "./alpha/text_field.pcss";
18
18
  @import "./alpha/toggle_switch.pcss";
19
19
  @import "./alpha/underline_nav.pcss";
20
+ @import "./alpha/skeleton_box.pcss";
21
+ @import "./alpha/tree_view.pcss";
20
22
 
21
23
  /* beta */
22
24
  @import "./beta/avatar.pcss";
@@ -26,3 +26,7 @@ import '../../lib/primer/forms/toggle_switch_input'
26
26
  import './alpha/action_menu/action_menu_element'
27
27
  import './alpha/select_panel_element'
28
28
  import './beta/details_toggle_element'
29
+ import './alpha/tree_view/tree_view'
30
+ import './alpha/tree_view/tree_view_icon_pair_element'
31
+ import './alpha/tree_view/tree_view_sub_tree_node_element'
32
+ import './alpha/tree_view/tree_view_include_fragment_element'
@@ -3,9 +3,24 @@ export type ItemActivatedEvent = {
3
3
  checked: boolean;
4
4
  value: string | null;
5
5
  };
6
+ export type TreeViewNodeType = 'leaf' | 'sub-tree';
7
+ export type TreeViewCheckedValue = 'true' | 'false' | 'mixed';
8
+ export type TreeViewNodeInfo = {
9
+ node: Element;
10
+ type: TreeViewNodeType;
11
+ path: string[];
12
+ checkedValue: TreeViewCheckedValue;
13
+ previousCheckedValue: TreeViewCheckedValue;
14
+ };
6
15
  declare global {
7
16
  interface HTMLElementEventMap {
8
17
  itemActivated: CustomEvent<ItemActivatedEvent>;
9
18
  beforeItemActivated: CustomEvent<ItemActivatedEvent>;
19
+ treeViewNodeActivated: CustomEvent<TreeViewNodeInfo>;
20
+ treeViewBeforeNodeActivated: CustomEvent<TreeViewNodeInfo>;
21
+ treeViewNodeExpanded: CustomEvent<TreeViewNodeInfo>;
22
+ treeViewNodeCollapsed: CustomEvent<TreeViewNodeInfo>;
23
+ treeViewNodeChecked: CustomEvent<TreeViewNodeInfo[]>;
24
+ treeViewBeforeNodeChecked: CustomEvent<TreeViewNodeInfo[]>;
10
25
  }
11
26
  }
@@ -4,9 +4,28 @@ export type ItemActivatedEvent = {
4
4
  value: string | null
5
5
  }
6
6
 
7
+ export type TreeViewNodeType = 'leaf' | 'sub-tree'
8
+ export type TreeViewCheckedValue = 'true' | 'false' | 'mixed'
9
+
10
+ export type TreeViewNodeInfo = {
11
+ node: Element
12
+ type: TreeViewNodeType
13
+ path: string[]
14
+ checkedValue: TreeViewCheckedValue
15
+ previousCheckedValue: TreeViewCheckedValue
16
+ }
17
+
7
18
  declare global {
8
19
  interface HTMLElementEventMap {
9
20
  itemActivated: CustomEvent<ItemActivatedEvent>
10
21
  beforeItemActivated: CustomEvent<ItemActivatedEvent>
22
+
23
+ treeViewNodeActivated: CustomEvent<TreeViewNodeInfo>
24
+ treeViewBeforeNodeActivated: CustomEvent<TreeViewNodeInfo>
25
+ treeViewNodeExpanded: CustomEvent<TreeViewNodeInfo>
26
+ treeViewNodeCollapsed: CustomEvent<TreeViewNodeInfo>
27
+
28
+ treeViewNodeChecked: CustomEvent<TreeViewNodeInfo[]>
29
+ treeViewBeforeNodeChecked: CustomEvent<TreeViewNodeInfo[]>
11
30
  }
12
31
  }