openproject-primer_view_components 0.87.0 → 0.89.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/tree_view/tree_view.js +27 -11
- data/app/components/primer/alpha/tree_view/tree_view.ts +24 -9
- data/app/components/primer/alpha/tree_view.css +1 -1
- data/app/components/primer/alpha/tree_view.css.json +1 -2
- data/app/components/primer/alpha/tree_view.css.map +1 -1
- data/app/components/primer/alpha/tree_view.pcss +11 -7
- data/app/components/primer/open_project/avatar_fallback.js +1 -1
- data/app/components/primer/open_project/avatar_fallback.ts +1 -1
- data/app/components/primer/open_project/border_box/collapsible_header.js +1 -1
- data/app/components/primer/open_project/border_box/collapsible_header.ts +1 -1
- data/app/components/primer/open_project/collapsible_section.js +1 -1
- data/app/components/primer/open_project/collapsible_section.ts +1 -1
- data/app/components/primer/open_project/danger_dialog_form_helper.js +1 -1
- data/app/components/primer/open_project/danger_dialog_form_helper.ts +1 -1
- data/app/components/primer/open_project/fieldset.rb +2 -2
- data/app/components/primer/open_project/filterable_tree_view.css +1 -1
- data/app/components/primer/open_project/filterable_tree_view.css.map +1 -1
- data/app/components/primer/open_project/filterable_tree_view.js +12 -5
- data/app/components/primer/open_project/filterable_tree_view.pcss +2 -2
- data/app/components/primer/open_project/filterable_tree_view.rb +7 -0
- data/app/components/primer/open_project/filterable_tree_view.ts +10 -5
- data/app/components/primer/open_project/flex_layout.rb +2 -2
- data/app/components/primer/open_project/inline_message.rb +1 -1
- data/app/components/primer/open_project/input_group.html.erb +9 -0
- data/app/components/primer/open_project/input_group.rb +13 -0
- data/app/components/primer/open_project/page_header.rb +1 -1
- data/app/components/primer/open_project/page_header_element.js +1 -1
- data/app/components/primer/open_project/page_header_element.ts +1 -1
- data/app/components/primer/open_project/pagination.rb +1 -3
- data/app/components/primer/open_project/sub_header/button.rb +1 -1
- data/app/components/primer/open_project/sub_header.html.erb +8 -8
- data/app/components/primer/open_project/sub_header.rb +19 -18
- data/app/components/primer/open_project/sub_header_element.js +1 -1
- data/app/components/primer/open_project/sub_header_element.ts +1 -1
- data/app/components/primer/open_project/zen_mode_button.js +1 -1
- data/app/components/primer/open_project/zen_mode_button.ts +1 -1
- data/app/controllers/primer/view_components/filterable_tree_view_items_controller.rb +22 -5
- data/config/locales/en.yml +1 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/previews/primer/alpha/tree_view_preview/doubled_path.html.erb +12 -0
- data/previews/primer/alpha/tree_view_preview/form_input.html.erb +7 -7
- data/previews/primer/alpha/tree_view_preview.rb +6 -0
- data/previews/primer/open_project/filterable_tree_view_preview/async_server_highlights.html.erb +4 -0
- data/previews/primer/open_project/filterable_tree_view_preview.rb +12 -0
- data/previews/primer/open_project/input_group_preview.rb +9 -0
- data/previews/primer/open_project/sub_header_preview/quick_filters.html.erb +2 -2
- data/previews/primer/open_project/sub_header_preview.rb +12 -12
- data/static/arguments.json +8 -8
- data/static/info_arch.json +32 -9
- data/static/previews.json +23 -10
- metadata +3 -1
|
@@ -18,7 +18,7 @@ type NodeState = {
|
|
|
18
18
|
|
|
19
19
|
const ASYNC_DEBOUNCE_MS = 300
|
|
20
20
|
|
|
21
|
-
@controller
|
|
21
|
+
@controller('filterable-tree-view')
|
|
22
22
|
export class FilterableTreeViewElement extends HTMLElement {
|
|
23
23
|
@target filterInput: HTMLInputElement
|
|
24
24
|
@target filterModeControlList: HTMLElement
|
|
@@ -69,6 +69,10 @@ export class FilterableTreeViewElement extends HTMLElement {
|
|
|
69
69
|
return !!this.#src
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
get #clientHighlightsEnabled(): boolean {
|
|
73
|
+
return this.getAttribute('data-show-search-highlighting') !== 'false'
|
|
74
|
+
}
|
|
75
|
+
|
|
72
76
|
handleEvent(event: Event) {
|
|
73
77
|
if (event.target === this.filterModeControl) {
|
|
74
78
|
this.#handleFilterModeEvent(event)
|
|
@@ -119,7 +123,8 @@ export class FilterableTreeViewElement extends HTMLElement {
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
this.#checkedNodeIds.set(nodeId, nodeInfo.checkedValue)
|
|
122
|
-
const payload: {path: string[]; value?: string} = {path: nodeInfo.path}
|
|
126
|
+
const payload: {path: string[]; nodeId?: string; value?: string} = {path: nodeInfo.path}
|
|
127
|
+
payload.nodeId = nodeId
|
|
123
128
|
const dataValue = node.getAttribute('data-value')
|
|
124
129
|
if (dataValue) payload.value = dataValue
|
|
125
130
|
this.#checkedNodeFormPayloads.set(nodeId, payload)
|
|
@@ -371,12 +376,12 @@ export class FilterableTreeViewElement extends HTMLElement {
|
|
|
371
376
|
|
|
372
377
|
if (requestWasFiltered) {
|
|
373
378
|
this.#expandAllSubTrees()
|
|
374
|
-
this.#applyAsyncHighlights(query)
|
|
379
|
+
if (this.#clientHighlightsEnabled) this.#applyAsyncHighlights(query)
|
|
375
380
|
const hasResults = !!this.treeViewList?.querySelector('[role=treeitem]')
|
|
376
381
|
this.noResultsMessage.toggleAttribute('hidden', hasResults)
|
|
377
382
|
this.treeViewList?.toggleAttribute('hidden', !hasResults)
|
|
378
383
|
} else {
|
|
379
|
-
this.#removeHighlights()
|
|
384
|
+
if (this.#clientHighlightsEnabled) this.#removeHighlights()
|
|
380
385
|
this.#restoreExpansionState()
|
|
381
386
|
this.noResultsMessage.setAttribute('hidden', 'hidden')
|
|
382
387
|
this.treeViewList?.removeAttribute('hidden')
|
|
@@ -699,7 +704,7 @@ export class FilterableTreeViewElement extends HTMLElement {
|
|
|
699
704
|
this.treeViewList.removeAttribute('hidden')
|
|
700
705
|
this.noResultsMessage.setAttribute('hidden', 'hidden')
|
|
701
706
|
|
|
702
|
-
this.#applyHighlights(allRanges)
|
|
707
|
+
if (this.#clientHighlightsEnabled) this.#applyHighlights(allRanges)
|
|
703
708
|
}
|
|
704
709
|
}
|
|
705
710
|
|
|
@@ -29,10 +29,10 @@ module Primer
|
|
|
29
29
|
# no slot provided
|
|
30
30
|
return false if rows.empty? && columns.empty? && boxes.empty?
|
|
31
31
|
|
|
32
|
-
if [rows, columns, boxes].
|
|
32
|
+
if [rows, columns, boxes].one? { |arr| !arr.empty? }
|
|
33
33
|
# only rows or columns or boxes are used
|
|
34
34
|
true
|
|
35
|
-
elsif [rows, columns, boxes].
|
|
35
|
+
elsif [rows, columns, boxes].many? { |arr| !arr.empty? }
|
|
36
36
|
# rows, columns and boxes are used together, which is not allowed
|
|
37
37
|
raise ArgumentError, "You can't mix row, column and box slots"
|
|
38
38
|
end
|
|
@@ -33,7 +33,7 @@ module Primer
|
|
|
33
33
|
# @param scheme [Symbol] <%= one_of(Primer::OpenProject::InlineMessage::SCHEME_OPTIONS) %>
|
|
34
34
|
# @param size [Symbol] <%= one_of(Primer::OpenProject::InlineMessage::SIZE_OPTIONS) %>
|
|
35
35
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
36
|
-
def initialize(scheme:, size: DEFAULT_SIZE, **system_arguments)
|
|
36
|
+
def initialize(scheme:, size: DEFAULT_SIZE, **system_arguments)
|
|
37
37
|
resolved_scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme)
|
|
38
38
|
resolved_size = fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)
|
|
39
39
|
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
<%= trailing_action %>
|
|
5
5
|
<% end %>
|
|
6
6
|
|
|
7
|
+
<% if @text_input_validation_message %>
|
|
8
|
+
<div class="FormControl-inlineValidation mt-1" id="<%= @text_input_validation_message_id %>">
|
|
9
|
+
<span class="FormControl-inlineValidation--visual">
|
|
10
|
+
<%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %>
|
|
11
|
+
</span>
|
|
12
|
+
<span><%= @text_input_validation_message %></span>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
7
16
|
<% if caption %>
|
|
8
17
|
<%= caption %>
|
|
9
18
|
<% end %>
|
|
@@ -49,6 +49,19 @@ module Primer
|
|
|
49
49
|
renders_one :text_input, lambda { |readonly: true, **system_arguments|
|
|
50
50
|
deny_single_argument(:input_width, "Set the `input_width` on the `InputGroup`", **system_arguments)
|
|
51
51
|
deny_single_argument(:caption, "Set the `caption` on the `InputGroup`", **system_arguments)
|
|
52
|
+
|
|
53
|
+
# Extract validation_message so it can be rendered outside the flex row.
|
|
54
|
+
# This prevents the trailing action from misaligning when validation is present.
|
|
55
|
+
@text_input_validation_message = system_arguments.delete(:validation_message)
|
|
56
|
+
if @text_input_validation_message
|
|
57
|
+
@text_input_validation_message_id = self.class.generate_id(base_name: "input-group-validation")
|
|
58
|
+
system_arguments[:invalid] = true
|
|
59
|
+
system_arguments[:aria] = merge_aria(
|
|
60
|
+
system_arguments,
|
|
61
|
+
{ aria: { describedby: @text_input_validation_message_id } }
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
52
65
|
system_arguments[:input_width] = @system_arguments[:input_width]
|
|
53
66
|
|
|
54
67
|
system_arguments[:classes] = class_names(
|
|
@@ -15,7 +15,7 @@ let PageHeaderElement = class PageHeaderElement extends HTMLElement {
|
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
17
|
PageHeaderElement = __decorate([
|
|
18
|
-
controller
|
|
18
|
+
controller('page-header')
|
|
19
19
|
], PageHeaderElement);
|
|
20
20
|
if (!window.customElements.get('page-header')) {
|
|
21
21
|
window.PageHeaderElement = PageHeaderElement;
|
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
<%= render @collapsed_filter_trigger if @collapsed_filter_trigger.present? %>
|
|
9
9
|
|
|
10
|
+
<%= segmented_control %>
|
|
11
|
+
|
|
12
|
+
<% if @segmented_control_block.present? %>
|
|
13
|
+
<%= render(@mobile_segmented_control) do |control| %>
|
|
14
|
+
<% @segmented_control_block.call(control) %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
10
18
|
<%= quick_sort %>
|
|
11
19
|
<%= quick_group %>
|
|
12
20
|
<% quick_filters.each do |quick_filter| %>
|
|
@@ -15,14 +23,6 @@
|
|
|
15
23
|
|
|
16
24
|
<%= render(@mobile_filter_button) if @mobile_filter_button.present? %>
|
|
17
25
|
<%= filter_button %>
|
|
18
|
-
|
|
19
|
-
<%= segmented_control %>
|
|
20
|
-
|
|
21
|
-
<% if @segmented_control_block.present? %>
|
|
22
|
-
<%= render(@mobile_segmented_control) do |control| %>
|
|
23
|
-
<% @segmented_control_block.call(control) %>
|
|
24
|
-
<% end %>
|
|
25
|
-
<% end %>
|
|
26
26
|
</div>
|
|
27
27
|
|
|
28
28
|
<div class="SubHeader-middlePane SubHeader-hiddenOnExpand">
|
|
@@ -40,7 +40,7 @@ module Primer
|
|
|
40
40
|
@mobile_actions ||= []
|
|
41
41
|
mobile_component = Primer::Beta::IconButton.new(display: MOBILE_ACTIONS_DISPLAY,
|
|
42
42
|
**icon_args)
|
|
43
|
-
@mobile_actions.push({ component: mobile_component, block: block})
|
|
43
|
+
@mobile_actions.push({ component: mobile_component, block: block })
|
|
44
44
|
|
|
45
45
|
Primer::OpenProject::SubHeader::Button.new(display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
|
46
46
|
end
|
|
@@ -65,9 +65,9 @@ module Primer
|
|
|
65
65
|
mobile_component = Primer::OpenProject::SubHeader::Menu.new(icon_only: true,
|
|
66
66
|
display: MOBILE_ACTIONS_DISPLAY,
|
|
67
67
|
**kwargs)
|
|
68
|
-
@mobile_actions.push({ component: mobile_component, block: block})
|
|
68
|
+
@mobile_actions.push({ component: mobile_component, block: block })
|
|
69
69
|
|
|
70
|
-
Primer::OpenProject::SubHeader::Menu.new(icon_only: icon_only,display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
|
70
|
+
Primer::OpenProject::SubHeader::Menu.new(icon_only: icon_only, display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
|
71
71
|
},
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -131,9 +131,10 @@ module Primer
|
|
|
131
131
|
# To render custom content, call the `with_filter_component` method and pass a block that returns HTML.
|
|
132
132
|
renders_one :filter_button, types: {
|
|
133
133
|
button: {
|
|
134
|
-
renders: lambda { |icon_only: false, leading_icon: :filter, mobile_label: I18n.t("
|
|
134
|
+
renders: lambda { |icon_only: false, leading_icon: :filter, mobile_label: I18n.t("button_all_filters"), **kwargs|
|
|
135
135
|
kwargs[:mr] ||= 2
|
|
136
136
|
kwargs[:icon] = leading_icon
|
|
137
|
+
kwargs[:scheme] ||= :invisible
|
|
137
138
|
|
|
138
139
|
kwargs[:aria] ||= merge_aria(
|
|
139
140
|
kwargs,
|
|
@@ -146,7 +147,7 @@ module Primer
|
|
|
146
147
|
if icon_only
|
|
147
148
|
Primer::Beta::IconButton.new(**icon_args)
|
|
148
149
|
else
|
|
149
|
-
@mobile_filter_button =
|
|
150
|
+
@mobile_filter_button = Primer::Beta::IconButton.new(display: MOBILE_ACTIONS_DISPLAY,
|
|
150
151
|
**icon_args)
|
|
151
152
|
|
|
152
153
|
Primer::OpenProject::SubHeader::Button.new(display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
|
@@ -194,18 +195,18 @@ module Primer
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
renders_one :segmented_control, lambda { |**system_arguments, &block|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
198
|
+
deny_tag_argument(**system_arguments)
|
|
199
|
+
system_arguments[:mr] ||= 2
|
|
200
|
+
system_arguments = set_as_hidden_filter_target(system_arguments)
|
|
201
|
+
|
|
202
|
+
@segmented_control_block = block
|
|
203
|
+
@mobile_segmented_control = Primer::OpenProject::SubHeader::SegmentedControl.new(
|
|
204
|
+
hide_labels: true,
|
|
205
|
+
display: MOBILE_ACTIONS_DISPLAY,
|
|
206
|
+
**system_arguments
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
Primer::OpenProject::SubHeader::SegmentedControl.new(display: DESKTOP_ACTIONS_DISPLAY, **system_arguments)
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
renders_one :text, lambda { |**system_arguments|
|
|
@@ -226,7 +227,7 @@ module Primer
|
|
|
226
227
|
|
|
227
228
|
# @param collapsed_search [Boolean] When true, the search bar starts collapsed as an icon button on all screen sizes. Clicking expands it.
|
|
228
229
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
|
229
|
-
def initialize(collapsed_search:
|
|
230
|
+
def initialize(collapsed_search: true, **system_arguments)
|
|
230
231
|
@collapsed_search = collapsed_search
|
|
231
232
|
@system_arguments = system_arguments
|
|
232
233
|
@system_arguments[:tag] = :"sub-header"
|
|
@@ -69,7 +69,7 @@ __decorate([
|
|
|
69
69
|
targets
|
|
70
70
|
], SubHeaderElement.prototype, "filterExpandButton", void 0);
|
|
71
71
|
SubHeaderElement = __decorate([
|
|
72
|
-
controller
|
|
72
|
+
controller('sub-header')
|
|
73
73
|
], SubHeaderElement);
|
|
74
74
|
if (!window.customElements.get('sub-header')) {
|
|
75
75
|
window.SubHeaderElement = SubHeaderElement;
|
|
@@ -56,7 +56,7 @@ __decorate([
|
|
|
56
56
|
target
|
|
57
57
|
], ZenModeButtonElement.prototype, "button", void 0);
|
|
58
58
|
ZenModeButtonElement = __decorate([
|
|
59
|
-
controller
|
|
59
|
+
controller('zen-mode-button')
|
|
60
60
|
], ZenModeButtonElement);
|
|
61
61
|
if (!window.customElements.get('zen-mode-button')) {
|
|
62
62
|
window.ZenModeButtonElement = ZenModeButtonElement;
|
|
@@ -165,15 +165,32 @@ module Primer
|
|
|
165
165
|
def index
|
|
166
166
|
query = params[:query].to_s.strip
|
|
167
167
|
select_variant = (params[:select_variant].presence || "multiple").to_sym
|
|
168
|
+
server_highlights = params[:server_highlights] == "true"
|
|
168
169
|
nodes = TREE.filter_map { |node| node.filter(query) }
|
|
169
170
|
|
|
170
|
-
|
|
171
|
-
nodes:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
if server_highlights && query.present?
|
|
172
|
+
html = render_to_string locals: { nodes:, query:, select_variant: }
|
|
173
|
+
render html: inject_highlights(html, query)
|
|
174
|
+
else
|
|
175
|
+
render locals: { nodes:, query:, select_variant: }
|
|
176
|
+
end
|
|
175
177
|
end
|
|
176
178
|
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
# Post-processes the rendered tree HTML to wrap matching label text in <mark> tags.
|
|
182
|
+
# This avoids html_safety issues with passing markup through ViewComponent's label slot.
|
|
183
|
+
# Only called in the demo endpoint when server_highlights=true is requested.
|
|
184
|
+
def inject_highlights(html, query)
|
|
185
|
+
escaped_query = Regexp.escape(CGI.escapeHTML(query))
|
|
186
|
+
html.gsub(
|
|
187
|
+
/(<span class="TreeViewItemContentText">)([^<]*)(#{escaped_query})([^<]*)(<\/span>)/i,
|
|
188
|
+
'\1\2<mark>\3</mark>\4\5'
|
|
189
|
+
).html_safe
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
public
|
|
193
|
+
|
|
177
194
|
def async_form_tree
|
|
178
195
|
query = params[:query].to_s.strip
|
|
179
196
|
name = params[:name].to_s.presence || "characters"
|
data/config/locales/en.yml
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div style="max-width: 400px">
|
|
2
|
+
<%= render(Primer::Alpha::TreeView.new) do |tree_view| %>
|
|
3
|
+
<% tree_view.with_sub_tree(label: "src", expanded: true, select_variant: :single) do |sub_tree| %>
|
|
4
|
+
<% sub_tree.with_leaf(label: "button.rb", select_variant: :single) %>
|
|
5
|
+
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant: :single) %>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<% tree_view.with_leaf(label: "action_menu.rb", select_variant: :single) %>
|
|
9
|
+
<% tree_view.with_leaf(label: "action_menu.rb", select_variant: :single) %>
|
|
10
|
+
<% tree_view.with_leaf(label: "action_menu.rb", select_variant: :single) %>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<%= form_with(url: primer_view_components.generic_form_submission_path(format: :json)) do |f| %>
|
|
2
2
|
<%= render(Primer::Alpha::Stack.new) do %>
|
|
3
3
|
<%= render(Primer::Alpha::TreeView.new(form_arguments: { builder: f, name: "folder_structure" })) do |tree| %>
|
|
4
|
-
<% tree.with_sub_tree(label: "src", expanded: expanded, select_variant: select_variant, value: 0) do |sub_tree| %>
|
|
5
|
-
<% sub_tree.with_leaf(label: "button.rb", select_variant: select_variant, value: 1, checked: true) %>
|
|
6
|
-
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant: select_variant, value: 2) %>
|
|
4
|
+
<% tree.with_sub_tree(label: "src", expanded: expanded, select_variant: select_variant, value: 0, data: { node_id: "src" }) do |sub_tree| %>
|
|
5
|
+
<% sub_tree.with_leaf(label: "button.rb", select_variant: select_variant, value: 1, checked: true, data: { node_id: "src-button-rb" }) %>
|
|
6
|
+
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant: select_variant, value: 2, data: { node_id: "src-icon-button-rb" }) %>
|
|
7
7
|
<% end %>
|
|
8
8
|
|
|
9
|
-
<% tree.with_leaf(label: "action_menu.rb", select_variant: select_variant, value: 3) %>
|
|
10
|
-
<% tree.with_sub_tree(label: "Docs & legal requirements", select_variant: select_variant, value: 4) do |sub_tree| %>
|
|
11
|
-
<% sub_tree.with_leaf(label: "Readme.md", select_variant: select_variant, value: 5) %>
|
|
12
|
-
<% sub_tree.with_leaf(label: "Copyright.md", select_variant: select_variant, value: 6) %>
|
|
9
|
+
<% tree.with_leaf(label: "action_menu.rb", select_variant: select_variant, value: 3, data: { node_id: "action-menu-rb" }) %>
|
|
10
|
+
<% tree.with_sub_tree(label: "Docs & legal requirements", select_variant: select_variant, value: 4, data: { node_id: "docs" }) do |sub_tree| %>
|
|
11
|
+
<% sub_tree.with_leaf(label: "Readme.md", select_variant: select_variant, value: 5, data: { node_id: "docs-readme-md" }) %>
|
|
12
|
+
<% sub_tree.with_leaf(label: "Copyright.md", select_variant: select_variant, value: 6, data: { node_id: "docs-copyright-md" }) %>
|
|
13
13
|
<% end %>
|
|
14
14
|
<% end %>
|
|
15
15
|
|
|
@@ -123,6 +123,18 @@ module Primer
|
|
|
123
123
|
})
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
+
# @label Async (with server-side highlighting)
|
|
127
|
+
#
|
|
128
|
+
# Demonstrates `show_search_highlighting: false`: the client skips its own highlight logic
|
|
129
|
+
# and the server is responsible for wrapping matching text in `<mark>` (or other HTML) elements.
|
|
130
|
+
#
|
|
131
|
+
# @param select_variant [Symbol] select [multiple, single, none]
|
|
132
|
+
def async_server_highlights(select_variant: :single)
|
|
133
|
+
render_with_template(locals: {
|
|
134
|
+
select_variant: select_variant.to_sym
|
|
135
|
+
})
|
|
136
|
+
end
|
|
137
|
+
|
|
126
138
|
# @label Async form input
|
|
127
139
|
def async_form_input
|
|
128
140
|
render_with_template
|
|
@@ -71,6 +71,15 @@ module Primer
|
|
|
71
71
|
menu.with_caption { "Some caption" }
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
|
+
|
|
75
|
+
# @label With error message
|
|
76
|
+
# @snapshot
|
|
77
|
+
def with_error_message
|
|
78
|
+
render(Primer::OpenProject::InputGroup.new) do |menu|
|
|
79
|
+
menu.with_text_input(name: "a name", label: "My input group", validation_message: "Custom validation message", value: "Copyable value")
|
|
80
|
+
menu.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" })
|
|
81
|
+
end
|
|
82
|
+
end
|
|
74
83
|
end
|
|
75
84
|
end
|
|
76
85
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<%= render(Primer::OpenProject::SubHeader.new
|
|
1
|
+
<%= render(Primer::OpenProject::SubHeader.new) do |component|
|
|
2
2
|
component.with_filter_input(name: "filter", label: "Filter")
|
|
3
3
|
|
|
4
|
-
component.with_filter_button
|
|
4
|
+
component.with_filter_button do
|
|
5
5
|
"All filters"
|
|
6
6
|
end
|
|
7
7
|
|
|
@@ -22,16 +22,17 @@ module Primer
|
|
|
22
22
|
show_filter_button: true,
|
|
23
23
|
show_quick_filter: true,
|
|
24
24
|
show_action_button: true,
|
|
25
|
-
collapsed_search:
|
|
25
|
+
collapsed_search: true,
|
|
26
26
|
text: nil,
|
|
27
27
|
value: nil
|
|
28
28
|
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
bool = ActiveModel::Type::Boolean.new
|
|
30
|
+
render_with_template(locals: {show_filter_input: bool.cast(show_filter_input),
|
|
31
|
+
show_clear_button: bool.cast(show_clear_button),
|
|
32
|
+
show_filter_button: bool.cast(show_filter_button),
|
|
33
|
+
show_quick_filter: bool.cast(show_quick_filter),
|
|
34
|
+
show_action_button: bool.cast(show_action_button),
|
|
35
|
+
collapsed_search: bool.cast(collapsed_search),
|
|
35
36
|
text: text,
|
|
36
37
|
value: value})
|
|
37
38
|
end
|
|
@@ -42,7 +43,6 @@ module Primer
|
|
|
42
43
|
component.with_filter_input(name: "filter", label: "Filter")
|
|
43
44
|
component.with_filter_button do |button|
|
|
44
45
|
button.with_trailing_visual_counter(count: "15")
|
|
45
|
-
"Filter"
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
|
|
@@ -93,11 +93,11 @@ module Primer
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
# @label With
|
|
97
|
-
def
|
|
98
|
-
render(Primer::OpenProject::SubHeader.new(collapsed_search:
|
|
96
|
+
# @label With expanded search
|
|
97
|
+
def expanded_search
|
|
98
|
+
render(Primer::OpenProject::SubHeader.new(collapsed_search: false)) do |component|
|
|
99
99
|
component.with_filter_input(name: "filter", label: "Filter")
|
|
100
|
-
component.with_filter_button do |button|
|
|
100
|
+
component.with_filter_button(scheme: :default) do |button|
|
|
101
101
|
button.with_trailing_visual_counter(count: "15")
|
|
102
102
|
"Filter"
|
|
103
103
|
end
|
data/static/arguments.json
CHANGED
|
@@ -6188,25 +6188,25 @@
|
|
|
6188
6188
|
{
|
|
6189
6189
|
"name": "filter_input_arguments",
|
|
6190
6190
|
"type": "Hash",
|
|
6191
|
-
"default": "`
|
|
6191
|
+
"default": "`{}`",
|
|
6192
6192
|
"description": "Arguments that will be passed to the [TextField](/components/alpha/textfield) component."
|
|
6193
6193
|
},
|
|
6194
6194
|
{
|
|
6195
6195
|
"name": "filter_mode_control_arguments",
|
|
6196
6196
|
"type": "Hash",
|
|
6197
|
-
"default": "`
|
|
6197
|
+
"default": "`{}`",
|
|
6198
6198
|
"description": "Arguments that will be passed to the [SegmentedControl](/components/alpha/segmentedcontrol) component."
|
|
6199
6199
|
},
|
|
6200
6200
|
{
|
|
6201
6201
|
"name": "include_sub_items_check_box_arguments",
|
|
6202
6202
|
"type": "Hash",
|
|
6203
|
-
"default": "`
|
|
6203
|
+
"default": "`{}`",
|
|
6204
6204
|
"description": "Arguments that will be passed to the [CheckBox](/components/alpha/checkbox) component."
|
|
6205
6205
|
},
|
|
6206
6206
|
{
|
|
6207
6207
|
"name": "no_results_node_arguments",
|
|
6208
6208
|
"type": "Hash",
|
|
6209
|
-
"default": "`
|
|
6209
|
+
"default": "`{}`",
|
|
6210
6210
|
"description": "Arguments that will be passed to a [TreeView::LeafNode](/components/alpha/treeviewleafnode) component that appears when no items match the filter criteria."
|
|
6211
6211
|
}
|
|
6212
6212
|
]
|
|
@@ -6600,12 +6600,12 @@
|
|
|
6600
6600
|
]
|
|
6601
6601
|
},
|
|
6602
6602
|
{
|
|
6603
|
-
"component": "OpenProject::SubHeader::
|
|
6603
|
+
"component": "OpenProject::SubHeader::QuickAction",
|
|
6604
6604
|
"status": "open_project",
|
|
6605
6605
|
"a11y_reviewed": false,
|
|
6606
|
-
"short_name": "
|
|
6607
|
-
"source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/sub_header/
|
|
6608
|
-
"lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/sub_header/
|
|
6606
|
+
"short_name": "OpenProjectSubHeaderQuickAction",
|
|
6607
|
+
"source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/sub_header/quick_action_component.rb",
|
|
6608
|
+
"lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/sub_header/quick_action/default/",
|
|
6609
6609
|
"parameters": []
|
|
6610
6610
|
},
|
|
6611
6611
|
{
|