primer_view_components 0.0.104 → 0.0.105
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -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/action_list/item.rb +2 -2
- data/app/components/primer/alpha/action_list.rb +2 -0
- data/app/components/primer/alpha/image.rb +50 -0
- data/app/components/primer/{image_crop.d.ts → alpha/image_crop.d.ts} +0 -0
- data/app/components/primer/{image_crop.html.erb → alpha/image_crop.html.erb} +0 -0
- data/app/components/primer/{image_crop.js → alpha/image_crop.js} +0 -0
- data/app/components/primer/alpha/image_crop.rb +39 -0
- data/app/components/primer/{image_crop.ts → alpha/image_crop.ts} +0 -0
- data/app/components/primer/alpha/nav_list/item.rb +2 -0
- data/app/components/primer/alpha/nav_list/section.rb +2 -0
- data/app/components/primer/alpha/segmented_control/item.html.erb +13 -0
- data/app/components/primer/alpha/segmented_control/item.rb +29 -0
- data/app/components/primer/alpha/segmented_control.css +1 -0
- data/app/components/primer/alpha/segmented_control.css.map +1 -0
- data/app/components/primer/alpha/segmented_control.d.ts +11 -0
- data/app/components/primer/alpha/segmented_control.html.erb +7 -0
- data/app/components/primer/alpha/segmented_control.js +48 -0
- data/app/components/primer/alpha/segmented_control.pcss +121 -0
- data/app/components/primer/alpha/segmented_control.rb +92 -0
- data/app/components/primer/alpha/segmented_control.ts +41 -0
- data/app/components/primer/alpha/tooltip.rb +2 -2
- data/app/components/primer/beta/auto_complete/item.rb +2 -2
- data/app/components/primer/beta/avatar.rb +1 -1
- data/app/components/primer/beta/blankslate.rb +4 -4
- data/app/components/primer/beta/breadcrumbs.rb +1 -1
- data/app/components/primer/beta/button.rb +1 -1
- data/app/components/primer/beta/label.rb +100 -0
- data/app/components/primer/beta/link.rb +99 -0
- data/app/components/primer/component.rb +1 -0
- data/app/components/primer/image.rb +2 -41
- data/app/components/primer/image_crop.rb +2 -32
- data/app/components/primer/label_component.rb +2 -93
- data/app/components/primer/link_component.rb +2 -92
- data/app/components/primer/primer.d.ts +2 -1
- data/app/components/primer/primer.js +2 -1
- data/app/components/primer/primer.pcss +1 -0
- data/app/components/primer/primer.ts +2 -1
- data/app/components/primer/subhead_component.rb +1 -1
- data/app/components/primer/time_ago_component.rb +2 -0
- data/app/helpers/primer/form_helper.rb +1 -0
- data/app/lib/primer/view_helper.rb +1 -1
- data/lib/primer/deprecations.rb +4 -0
- data/lib/primer/form_components.rb +1 -0
- data/lib/primer/view_components/linters/argument_mappers/label.rb +4 -4
- data/lib/primer/view_components/linters/label_component_migration_counter.rb +3 -3
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/rubocop/config/default.yml +0 -3
- data/lib/rubocop/cop/primer/deprecated_label_schemes.rb +3 -3
- data/lib/rubocop/cop/primer/deprecated_label_variants.rb +5 -5
- data/lib/tasks/docs.rake +12 -7
- data/lib/tasks/test.rake +23 -26
- data/previews/primer/alpha/action_list_preview.rb +29 -0
- data/previews/primer/alpha/auto_complete_preview.rb +10 -0
- data/previews/primer/alpha/button_marketing_preview.rb +9 -0
- data/previews/primer/alpha/dialog_preview.rb +18 -1
- data/previews/primer/alpha/hidden_text_expander_preview.rb +7 -0
- data/previews/primer/alpha/image_crop_preview.rb +31 -0
- data/previews/primer/alpha/layout_preview.rb +15 -0
- data/previews/primer/alpha/nav_list_preview.rb +29 -0
- data/previews/primer/alpha/segmented_control_preview.rb +75 -0
- data/previews/primer/alpha/tab_panels_preview.rb +15 -0
- data/previews/primer/alpha/text_field_preview.rb +58 -0
- data/previews/primer/alpha/toggle_switch_preview.rb +4 -0
- data/previews/primer/alpha/tooltip_preview.rb +13 -3
- data/previews/primer/alpha/underline_nav_preview/playground.html.erb +8 -0
- data/previews/primer/alpha/underline_nav_preview.rb +15 -0
- data/previews/primer/alpha/underline_panels_preview.rb +15 -0
- data/previews/primer/beta/auto_complete_item_preview/playground.html.erb +9 -0
- data/previews/primer/beta/auto_complete_item_preview.rb +14 -0
- data/previews/primer/beta/auto_complete_preview.rb +20 -0
- data/previews/primer/beta/avatar_preview.rb +9 -0
- data/previews/primer/beta/avatar_stack_preview.rb +15 -0
- data/previews/primer/beta/base_button_preview.rb +9 -0
- data/previews/primer/beta/blankslate_preview.rb +12 -0
- data/previews/primer/beta/border_box_preview.rb +14 -0
- data/previews/primer/beta/breadcrumbs_preview.rb +11 -0
- data/previews/primer/beta/button_group_preview.rb +12 -0
- data/previews/primer/beta/close_button_preview.rb +7 -0
- data/previews/primer/beta/counter_preview.rb +11 -0
- data/previews/primer/beta/details_preview.rb +15 -0
- data/previews/primer/beta/flash_preview.rb +12 -0
- data/previews/primer/beta/heading_preview.rb +8 -0
- data/previews/primer/beta/label_preview.rb +26 -0
- data/previews/primer/beta/link_preview.rb +41 -0
- data/previews/primer/beta/text_preview.rb +8 -0
- data/previews/primer/beta/truncate_preview.rb +7 -0
- data/previews/primer/clipboard_copy_preview.rb +8 -0
- data/previews/primer/dropdown_preview.rb +18 -0
- data/previews/primer/hellip_button_preview.rb +8 -0
- data/previews/primer/layout_component_preview.rb +13 -1
- data/previews/primer/local_time_component_preview.rb +13 -0
- data/previews/primer/markdown_preview.rb +277 -266
- data/previews/primer/menu_component_preview/playground.html.erb +17 -0
- data/previews/primer/menu_component_preview.rb +4 -1
- data/previews/primer/octicon_component_preview.rb +8 -0
- data/previews/primer/popover_component_preview.rb +15 -1
- data/previews/primer/progress_bar_component_preview.rb +12 -1
- data/previews/primer/spinner_component_preview.rb +7 -0
- data/previews/primer/state_component_preview.rb +11 -1
- data/previews/primer/subhead_component_preview.rb +17 -0
- data/previews/primer/time_ago_component_preview.rb +8 -0
- data/previews/primer/timeline_item_component_preview.rb +12 -1
- data/static/arguments.json +300 -138
- data/static/audited_at.json +6 -0
- data/static/constants.json +86 -71
- data/static/statuses.json +10 -4
- metadata +27 -9
- data/previews/primer/image_crop_preview.rb +0 -22
- data/previews/primer/label_component_preview.rb +0 -15
- data/previews/primer/link_component_preview.rb +0 -29
@@ -0,0 +1,121 @@
|
|
1
|
+
/* SegmentedControl */
|
2
|
+
|
3
|
+
.SegmentedControl {
|
4
|
+
display: inline-flex;
|
5
|
+
list-style: none;
|
6
|
+
background-color: var(--color-segmented-control-bg);
|
7
|
+
border-radius: var(--primer-borderRadius-medium, 6px);
|
8
|
+
box-shadow: var(--primer-borderInset-thin, inset 0 0 0 max(1px, 0.0625rem)) var(--color-border-default);
|
9
|
+
}
|
10
|
+
|
11
|
+
.SegmentedControl-item {
|
12
|
+
position: relative;
|
13
|
+
display: inline-flex;
|
14
|
+
border: var(--primer-borderWidth-thin, max(1px, 0.0625rem)) solid transparent;
|
15
|
+
border-radius: var(--primer-borderRadius-medium, 6px);
|
16
|
+
padding: var(--primer-control-xsmall-paddingInline-condensed, 4px);
|
17
|
+
|
18
|
+
/* Selected ---------------------------------------- */
|
19
|
+
&.SegmentedControl-item--selected {
|
20
|
+
background-color: var(--color-btn-bg);
|
21
|
+
border-color: var(--color-segmented-control-button-selected-border);
|
22
|
+
|
23
|
+
& .Button {
|
24
|
+
font-weight: var(--base-text-weight-semibold, 600);
|
25
|
+
|
26
|
+
&:hover {
|
27
|
+
background-color: transparent;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
&::before {
|
32
|
+
border-color: transparent;
|
33
|
+
}
|
34
|
+
|
35
|
+
& + .SegmentedControl-item::before {
|
36
|
+
border-color: transparent;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/* renders a visibly hidden "copy" of the text in bold, reserving box space for when text becomes bold on selected */
|
41
|
+
& .Button-label[data-content]::before {
|
42
|
+
display: block;
|
43
|
+
height: 0;
|
44
|
+
font-weight: var(--base-text-weight-semibold, 600);
|
45
|
+
visibility: hidden;
|
46
|
+
content: attr(data-content);
|
47
|
+
}
|
48
|
+
|
49
|
+
/* Separator lines */
|
50
|
+
&::before {
|
51
|
+
position: absolute;
|
52
|
+
inset: 0 0 0 -1px;
|
53
|
+
margin-top: var(--primer-control-medium-paddingBlock, 6px);
|
54
|
+
margin-bottom: var(--primer-control-medium-paddingBlock, 6px);
|
55
|
+
content: '';
|
56
|
+
border-left: var(--primer-borderWidth-thin, max(1px, 0.0625rem)) solid var(--color-border-default);
|
57
|
+
}
|
58
|
+
|
59
|
+
/* Button ----------------------------------------- */
|
60
|
+
& .Button {
|
61
|
+
border: 0;
|
62
|
+
font-weight: var(--base-text-weight-normal, 400);
|
63
|
+
transition: none;
|
64
|
+
color: var(--color-btn-text);
|
65
|
+
|
66
|
+
&:focus-visible {
|
67
|
+
outline-offset: calc(var(--primer-control-xsmall-paddingInline-condensed, 4px) - var(--primer-borderWidth-thin, 1px));
|
68
|
+
border-radius: calc(var(--primer-borderRadius-medium, 6px) - 5px);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
& .Button--small {
|
73
|
+
height: calc(var(--primer-control-small-size, 28px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
|
74
|
+
padding: 0 calc(var(--primer-control-small-paddingInline-condensed, 8px) - var(--primer-control-xsmall-paddingInline-condensed, 4px));
|
75
|
+
|
76
|
+
&.Button--iconOnly {
|
77
|
+
width: calc(var(--primer-control-medium-size, 32px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
& .Button--medium {
|
82
|
+
height: calc(var(--primer-control-medium-size, 32px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
|
83
|
+
padding: 0 calc(var(--primer-control-medium-paddingInline-normal, 12px) - var(--primer-control-xsmall-paddingInline-condensed, 4px));
|
84
|
+
|
85
|
+
&.Button--iconOnly {
|
86
|
+
width: calc(var(--primer-control-medium-size, 32px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
& .Button--large {
|
91
|
+
height: calc(var(--primer-control-large-size, 40px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
|
92
|
+
padding: 0 calc(var(--primer-control-large-paddingInline-spacious, 16px) - var(--primer-control-xsmall-paddingInline-condensed, 4px));
|
93
|
+
|
94
|
+
&.Button--iconOnly {
|
95
|
+
width: calc(var(--primer-control-large-size, 40px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
& .Button--iconOnly {
|
100
|
+
padding: initial;
|
101
|
+
}
|
102
|
+
|
103
|
+
& .Button--invisible.Button--invisible-noVisuals .Button-label {
|
104
|
+
color: var(--color-btn-text);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
/* fullWidth */
|
109
|
+
.SegmentedControl--fullWidth {
|
110
|
+
display: flex;
|
111
|
+
|
112
|
+
& .SegmentedControl-item {
|
113
|
+
flex: 1;
|
114
|
+
justify-content: center;
|
115
|
+
}
|
116
|
+
|
117
|
+
& .Button--iconOnly,
|
118
|
+
& .Button-withTooltip {
|
119
|
+
width: 100%;
|
120
|
+
}
|
121
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
# Use a segmented control to let users select an option from a short list and immediately apply the selection
|
6
|
+
class SegmentedControl < Primer::Component
|
7
|
+
status :alpha
|
8
|
+
|
9
|
+
FULL_WIDTH_DEFAULT = false
|
10
|
+
HIDE_LABELS_DEFAULT = false
|
11
|
+
|
12
|
+
# Use to render an item in the segmented control
|
13
|
+
#
|
14
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
15
|
+
renders_many :items, lambda { |label:, selected: false, icon: nil, **system_arguments|
|
16
|
+
Primer::Alpha::SegmentedControl::Item.new(
|
17
|
+
label: label,
|
18
|
+
selected: selected,
|
19
|
+
icon: icon,
|
20
|
+
hide_labels: @hide_labels,
|
21
|
+
size: @size,
|
22
|
+
block: @full_width,
|
23
|
+
**system_arguments
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
27
|
+
# @example Basic usage
|
28
|
+
#
|
29
|
+
# <%= render(Primer::Alpha::SegmentedControl.new) do |c| %>
|
30
|
+
# <%= c.with_item(label: "Preview", selected: true) %>
|
31
|
+
# <%= c.with_item(label: "Raw") %>
|
32
|
+
# <%= c.with_item(label: "Blame") %>
|
33
|
+
# <% end %>
|
34
|
+
#
|
35
|
+
# @example Small
|
36
|
+
#
|
37
|
+
# <%= render(Primer::Alpha::SegmentedControl.new(size: :small)) do |c| %>
|
38
|
+
# <%= c.with_item(label: "Preview", selected: true) %>
|
39
|
+
# <%= c.with_item(label: "Raw") %>
|
40
|
+
# <%= c.with_item(label: "Blame") %>
|
41
|
+
# <% end %>
|
42
|
+
#
|
43
|
+
# @example With icons
|
44
|
+
# <%= render(Primer::Alpha::SegmentedControl.new) do |c| %>
|
45
|
+
# <%= c.with_item(label: "Preview", icon: :eye, selected: true) %>
|
46
|
+
# <%= c.with_item(label: "Raw", icon: :"file-code") %>
|
47
|
+
# <%= c.with_item(label: "Blame", icon: :people) %>
|
48
|
+
# <% end %>
|
49
|
+
#
|
50
|
+
# @example With icons only
|
51
|
+
# <%= render(Primer::Alpha::SegmentedControl.new(hide_labels: true)) do |c| %>
|
52
|
+
# <%= c.with_item(label: "Preview", icon: :eye, selected: true) %>
|
53
|
+
# <%= c.with_item(label: "Raw", icon: :"file-code") %>
|
54
|
+
# <%= c.with_item(label: "Blame", icon: :people) %>
|
55
|
+
# <% end %>
|
56
|
+
#
|
57
|
+
# @example Fill width of parent
|
58
|
+
# <%= render(Primer::Alpha::SegmentedControl.new(full_width: true)) do |c| %>
|
59
|
+
# <%= c.with_item(label: "Preview", icon: :eye, selected: true) %>
|
60
|
+
# <%= c.with_item(label: "Raw", icon: :"file-code") %>
|
61
|
+
# <%= c.with_item(label: "Blame", icon: :people) %>
|
62
|
+
# <% end %>
|
63
|
+
#
|
64
|
+
# @param hide_labels [Boolean] Whether to hide the labels and only show the icons
|
65
|
+
# @param full_width [Boolean] If the component should be full width
|
66
|
+
# @param size [Symbol] <%= one_of(Primer::Beta::Button::SIZE_OPTIONS) %>
|
67
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
68
|
+
def initialize(hide_labels: HIDE_LABELS_DEFAULT, full_width: FULL_WIDTH_DEFAULT, size: Primer::Beta::Button::DEFAULT_SIZE, **system_arguments)
|
69
|
+
@full_width = full_width
|
70
|
+
@size = size
|
71
|
+
@hide_labels = hide_labels
|
72
|
+
|
73
|
+
@system_arguments = system_arguments
|
74
|
+
@system_arguments[:tag] = :ul
|
75
|
+
@system_arguments[:role] = "list"
|
76
|
+
@system_arguments[:classes] = class_names(
|
77
|
+
system_arguments[:classes],
|
78
|
+
"SegmentedControl",
|
79
|
+
"SegmentedControl--iconOnly": hide_labels,
|
80
|
+
"SegmentedControl--fullWidth": full_width
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def render?
|
85
|
+
valid_items_count = items.count <= (@hide_labels ? 6 : 5) && items.count >= 2
|
86
|
+
raise ArgumentError, "A segmented control should have 2–5 choices with text labels, or up to 6 icon-only buttons." if !valid_items_count && !Rails.env.production?
|
87
|
+
|
88
|
+
valid_items_count
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import {controller, targets} from '@github/catalyst'
|
2
|
+
|
3
|
+
@controller
|
4
|
+
export class SegmentedControlElement extends HTMLElement {
|
5
|
+
@targets items: HTMLElement[]
|
6
|
+
|
7
|
+
connectedCallback() {
|
8
|
+
this.#updateButtonLabels()
|
9
|
+
}
|
10
|
+
|
11
|
+
select(event: Event) {
|
12
|
+
const button = event.currentTarget as HTMLButtonElement
|
13
|
+
for (const item of this.items) {
|
14
|
+
item.classList.remove('SegmentedControl-item--selected')
|
15
|
+
item.querySelector('[aria-current]')?.setAttribute('aria-current', 'false')
|
16
|
+
}
|
17
|
+
|
18
|
+
button.closest('li.SegmentedControl-item')?.classList.add('SegmentedControl-item--selected')
|
19
|
+
button.setAttribute('aria-current', 'true')
|
20
|
+
}
|
21
|
+
|
22
|
+
// Updates the button labels to have a data-content attribute with the text
|
23
|
+
// This is for selection styling to avoid the text jumping. It only needs to be
|
24
|
+
// setup when the component is first loaded.
|
25
|
+
#updateButtonLabels() {
|
26
|
+
for (const label of this.querySelectorAll('.Button-label')) {
|
27
|
+
label.setAttribute('data-content', label.textContent || '')
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
declare global {
|
33
|
+
interface Window {
|
34
|
+
SegmentedControlElement: typeof SegmentedControlElement
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
if (!window.customElements.get('segmented-control')) {
|
39
|
+
window.SegmentedControlElement = SegmentedControlElement
|
40
|
+
window.customElements.define('segmented-control', SegmentedControlElement)
|
41
|
+
}
|
@@ -7,12 +7,12 @@ module Primer
|
|
7
7
|
#
|
8
8
|
# When using a tooltip, follow the provided guidelines to avoid accessibility issues:
|
9
9
|
# - Tooltips should contain only **non-essential text**. Tooltips can easily be missed and are not accessible on touch devices so never use tooltips to convey critical information.
|
10
|
-
# - `Tooltip` should be rendered through the API of <%= link_to_component(Primer::ButtonComponent)%>, <%= link_to_component(Primer::
|
10
|
+
# - `Tooltip` should be rendered through the API of <%= link_to_component(Primer::ButtonComponent)%>, <%= link_to_component(Primer::Beta::Link)%>, or <%= link_to_component(Primer::IconButton)%>. Avoid using `Tooltip` a standalone component unless absolutely necessary (and **only** on interactive elements).
|
11
11
|
# @accessibility
|
12
12
|
# - Tooltip text must be brief and concise whether it is a label or a description.
|
13
13
|
# - Tooltip can only hold string content.
|
14
14
|
# - **Never set tooltips on static, non-interactive elements** like `span` or `div`. Tooltips should only be used on interactive elements like buttons or links to avoid excluding keyboard-only users
|
15
|
-
# and screen reader users. Use of tooltip through <%= link_to_component(Primer::ButtonComponent) %>, <%= link_to_component(Primer::
|
15
|
+
# and screen reader users. Use of tooltip through <%= link_to_component(Primer::ButtonComponent) %>, <%= link_to_component(Primer::Beta::Link) %>, or <%= link_to_component(Primer::IconButton) %> will guarantee this.
|
16
16
|
# - If you must use `Tooltip` as a standalone component, place it adjacent after the trigger element in the DOM. This allows screen reader users to navigate to and copy the tooltip
|
17
17
|
# content.
|
18
18
|
# ### Which type should I set?
|
@@ -22,10 +22,10 @@ module Primer
|
|
22
22
|
|
23
23
|
# The trailing visual rendered after the link.
|
24
24
|
#
|
25
|
-
# @param kwargs [Hash] The arguments accepted by <%= link_to_component(Primer::OcticonComponent) %>, <%= link_to_component(Primer::
|
25
|
+
# @param kwargs [Hash] The arguments accepted by <%= link_to_component(Primer::OcticonComponent) %>, <%= link_to_component(Primer::Beta::Label) %>, or <%= link_to_component(Primer::Beta::Counter) %>
|
26
26
|
renders_one :trailing_visual, types: {
|
27
27
|
icon: Primer::OcticonComponent,
|
28
|
-
label: Primer::
|
28
|
+
label: Primer::Beta::Label,
|
29
29
|
counter: Primer::Beta::Counter
|
30
30
|
}
|
31
31
|
|
@@ -72,7 +72,7 @@ module Primer
|
|
72
72
|
|
73
73
|
def call
|
74
74
|
if @href
|
75
|
-
render(Primer::
|
75
|
+
render(Primer::Beta::Link.new(href: @href, classes: @system_arguments[:classes])) do
|
76
76
|
render(Primer::BaseComponent.new(**@system_arguments.except(:classes))) { content }
|
77
77
|
end
|
78
78
|
else
|
@@ -20,7 +20,7 @@ module Primer
|
|
20
20
|
# Use:
|
21
21
|
#
|
22
22
|
# - `visual_icon` for an <%= link_to_component(Primer::OcticonComponent) %>.
|
23
|
-
# - `visual_image` for an <%= link_to_component(Primer::Image) %>.
|
23
|
+
# - `visual_image` for an <%= link_to_component(Primer::Alpha::Image) %>.
|
24
24
|
# - `visual_spinner` for a <%= link_to_component(Primer::SpinnerComponent) %>.
|
25
25
|
#
|
26
26
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
@@ -40,7 +40,7 @@ module Primer
|
|
40
40
|
system_arguments[:size] = "56x56"
|
41
41
|
system_arguments[:classes] = class_names("blankslate-image", system_arguments[:classes])
|
42
42
|
|
43
|
-
Primer::Image.new(**system_arguments)
|
43
|
+
Primer::Alpha::Image.new(**system_arguments)
|
44
44
|
}
|
45
45
|
}
|
46
46
|
|
@@ -83,7 +83,7 @@ module Primer
|
|
83
83
|
system_arguments[:size] = :medium
|
84
84
|
system_arguments[:scheme] ||= :primary
|
85
85
|
|
86
|
-
Primer::ButtonComponent.new(**system_arguments)
|
86
|
+
Primer::ButtonComponent.new(**system_arguments)
|
87
87
|
}
|
88
88
|
|
89
89
|
# Optional secondary action
|
@@ -96,7 +96,7 @@ module Primer
|
|
96
96
|
renders_one :secondary_action, lambda { |href:, **system_arguments|
|
97
97
|
system_arguments[:href] = href
|
98
98
|
|
99
|
-
Primer::
|
99
|
+
Primer::Beta::Link.new(**system_arguments)
|
100
100
|
}
|
101
101
|
|
102
102
|
# @example Basic
|
@@ -57,7 +57,7 @@ module Primer
|
|
57
57
|
# @param system_arguments [Hash] Same arguments as <%= link_to_component(Primer::Beta::Counter) %>.
|
58
58
|
renders_one :trailing_visual, types: {
|
59
59
|
icon: Primer::OcticonComponent,
|
60
|
-
label: Primer::
|
60
|
+
label: Primer::Beta::Label,
|
61
61
|
counter: Primer::CounterComponent
|
62
62
|
}
|
63
63
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Beta
|
5
|
+
# Use `Label` to add contextual metadata to a design.
|
6
|
+
#
|
7
|
+
# @accessibility
|
8
|
+
# Use `aria-label` if the `Label` or the context around it don't explain the label.
|
9
|
+
class Label < Primer::Component
|
10
|
+
status :beta
|
11
|
+
|
12
|
+
DEFAULT_TAG = :span
|
13
|
+
TAG_OPTIONS = [DEFAULT_TAG, :summary, :a, :div].freeze
|
14
|
+
|
15
|
+
DEFAULT_SCHEME = :default
|
16
|
+
SCHEME_MAPPINGS = {
|
17
|
+
DEFAULT_SCHEME => "",
|
18
|
+
:primary => "Label--primary",
|
19
|
+
:secondary => "Label--secondary",
|
20
|
+
:accent => "Label--accent",
|
21
|
+
:success => "Label--success",
|
22
|
+
:attention => "Label--attention",
|
23
|
+
:danger => "Label--danger",
|
24
|
+
:severe => "Label--severe",
|
25
|
+
:done => "Label--done",
|
26
|
+
:sponsors => "Label--sponsors",
|
27
|
+
# deprecated
|
28
|
+
:info => "Label--info",
|
29
|
+
:warning => "Label--warning",
|
30
|
+
:orange => "Label--orange",
|
31
|
+
:purple => "Label--purple"
|
32
|
+
}.freeze
|
33
|
+
DEPRECATED_SCHEME_OPTIONS = [:info, :warning, :orange, :purple].freeze
|
34
|
+
SCHEME_OPTIONS = (SCHEME_MAPPINGS.keys - DEPRECATED_SCHEME_OPTIONS).freeze
|
35
|
+
|
36
|
+
DEFAULT_SIZE = :medium
|
37
|
+
SIZE_MAPPINGS = {
|
38
|
+
DEFAULT_SIZE => nil,
|
39
|
+
:large => "Label--large"
|
40
|
+
}.freeze
|
41
|
+
SIZE_OPTIONS = SIZE_MAPPINGS.keys
|
42
|
+
|
43
|
+
DEFAULT_VARIANT = :none
|
44
|
+
VARIANT_OPTIONS = [DEFAULT_VARIANT].freeze
|
45
|
+
DEPRECATED_VARIANT_OPTIONS = [:large, :inline].freeze
|
46
|
+
|
47
|
+
INLINE_CLASS = "Label--inline"
|
48
|
+
|
49
|
+
# @example Schemes
|
50
|
+
# <%= render(Primer::Beta::Label.new) { "Default" } %>
|
51
|
+
# <%= render(Primer::Beta::Label.new(scheme: :primary)) { "Primary" } %>
|
52
|
+
# <%= render(Primer::Beta::Label.new(scheme: :secondary)) { "Secondary" } %>
|
53
|
+
# <%= render(Primer::Beta::Label.new(scheme: :accent)) { "Accent" } %>
|
54
|
+
# <%= render(Primer::Beta::Label.new(scheme: :success)) { "Success" } %>
|
55
|
+
# <%= render(Primer::Beta::Label.new(scheme: :attention)) { "Attention" } %>
|
56
|
+
# <%= render(Primer::Beta::Label.new(scheme: :danger)) { "Danger" } %>
|
57
|
+
# <%= render(Primer::Beta::Label.new(scheme: :severe)) { "Severe" } %>
|
58
|
+
# <%= render(Primer::Beta::Label.new(scheme: :done)) { "Done" } %>
|
59
|
+
# <%= render(Primer::Beta::Label.new(scheme: :sponsors)) { "Sponsors" } %>
|
60
|
+
#
|
61
|
+
# @example Sizes
|
62
|
+
# <%= render(Primer::Beta::Label.new) { "Medium" } %>
|
63
|
+
# <%= render(Primer::Beta::Label.new(size: :large)) { "Large" } %>
|
64
|
+
#
|
65
|
+
# @example Inline
|
66
|
+
# <%= render(Primer::Beta::Label.new) { "Default" } %>
|
67
|
+
# <%= render(Primer::Beta::Label.new(inline: true)) { "Inline" } %>
|
68
|
+
#
|
69
|
+
# @param tag [Symbol] <%= one_of(Primer::Beta::Label::TAG_OPTIONS) %>
|
70
|
+
# @param scheme [Symbol] <%= one_of(Primer::Beta::Label::SCHEME_MAPPINGS.keys) %>
|
71
|
+
# @param size [Symbol] <%= one_of(Primer::Beta::Label::SIZE_OPTIONS) %>
|
72
|
+
# @param inline [Boolean] Whether or not to render this label inline.
|
73
|
+
# @param variant [Symbol] <%= one_of(Primer::Beta::Label::VARIANT_OPTIONS + Primer::Beta::Label::DEPRECATED_VARIANT_OPTIONS) %>
|
74
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
75
|
+
|
76
|
+
def initialize(tag: DEFAULT_TAG, scheme: DEFAULT_SCHEME, size: DEFAULT_SIZE, inline: false, variant: DEFAULT_VARIANT, **system_arguments)
|
77
|
+
@system_arguments = system_arguments
|
78
|
+
|
79
|
+
@variant = fetch_or_fallback(VARIANT_OPTIONS, variant, nil, deprecated_values: DEPRECATED_VARIANT_OPTIONS)
|
80
|
+
@scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME, deprecated_values: DEPRECATED_SCHEME_OPTIONS)
|
81
|
+
@size = fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)
|
82
|
+
@size = :large if @variant == :large
|
83
|
+
@inline = inline || @variant == :inline
|
84
|
+
|
85
|
+
@system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
|
86
|
+
@system_arguments[:classes] = class_names(
|
87
|
+
"Label",
|
88
|
+
system_arguments[:classes],
|
89
|
+
SCHEME_MAPPINGS[@scheme],
|
90
|
+
SIZE_MAPPINGS[@size],
|
91
|
+
@inline ? INLINE_CLASS : nil
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
def call
|
96
|
+
render(Primer::BaseComponent.new(**@system_arguments)) { content }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Beta
|
5
|
+
# Use `Link` for navigating from one page to another. `Link` styles anchor tags with default blue styling and hover text-decoration.
|
6
|
+
class Link < Primer::Component
|
7
|
+
status :beta
|
8
|
+
|
9
|
+
DEFAULT_SCHEME = :default
|
10
|
+
SCHEME_MAPPINGS = {
|
11
|
+
DEFAULT_SCHEME => "",
|
12
|
+
:primary => "Link--primary",
|
13
|
+
:secondary => "Link--secondary"
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
DEFAULT_TAG = :a
|
17
|
+
TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
|
18
|
+
|
19
|
+
# `Tooltip` that appears on mouse hover or keyboard focus over the link. Use tooltips sparingly and as a last resort.
|
20
|
+
# **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be more appropriate.
|
21
|
+
# The tooltip will appear adjacent to the anchor element. Both the tooltip and the anchor will be nested
|
22
|
+
# under a positioning wrapper.
|
23
|
+
# Consult the <%= link_to_component(Primer::Alpha::Tooltip) %> documentation for more information.
|
24
|
+
#
|
25
|
+
# @param type [Symbol] (:description) <%= one_of(Primer::Alpha::Tooltip::TYPE_OPTIONS) %>
|
26
|
+
# @param system_arguments [Hash] Same arguments as <%= link_to_component(Primer::Alpha::Tooltip) %>.
|
27
|
+
renders_one :tooltip, lambda { |**system_arguments|
|
28
|
+
raise ArgumentError, "Links with a tooltip must have a unique `id` set on the `LinkComponent`." if @id.blank? && !Rails.env.production?
|
29
|
+
|
30
|
+
system_arguments[:for_id] = @id
|
31
|
+
system_arguments[:type] ||= :description
|
32
|
+
|
33
|
+
Primer::Alpha::Tooltip.new(**system_arguments)
|
34
|
+
}
|
35
|
+
|
36
|
+
# @example Default
|
37
|
+
# <%= render(Primer::Beta::Link.new(href: "#")) { "Link" } %>
|
38
|
+
#
|
39
|
+
# @example Muted
|
40
|
+
# <%= render(Primer::Beta::Link.new(href: "#", muted: true)) { "Link" } %>
|
41
|
+
#
|
42
|
+
# @example Schemes
|
43
|
+
# <%= render(Primer::Beta::Link.new(href: "#", scheme: :primary)) { "Primary" } %>
|
44
|
+
# <%= render(Primer::Beta::Link.new(href: "#", scheme: :secondary)) { "Secondary" } %>
|
45
|
+
#
|
46
|
+
# @example Without underline
|
47
|
+
# <%= render(Primer::Beta::Link.new(href: "#", underline: false)) { "Link" } %>
|
48
|
+
#
|
49
|
+
# @example With tooltip
|
50
|
+
# @description
|
51
|
+
# Use tooltips sparingly and as a last resort. Consult the <%= link_to_component(Primer::Alpha::Tooltip) %> documentation for more information.
|
52
|
+
# @code
|
53
|
+
# <%= render(Primer::Beta::Link.new(href: "#", id: "link-with-tooltip")) do |c| %>
|
54
|
+
# <% c.with_tooltip(text: "Tooltip text") %>
|
55
|
+
# Link
|
56
|
+
# <% end %>
|
57
|
+
#
|
58
|
+
# @param tag [String] <%= one_of(Primer::Beta::Link::TAG_OPTIONS) %>
|
59
|
+
# @param href [String] URL to be used for the Link. Required if tag is `:a`. If the requirements are not met an error will be raised in non production environments. In production, an empty link element will be rendered.
|
60
|
+
# @param scheme [Symbol] <%= one_of(Primer::Beta::Link::SCHEME_MAPPINGS.keys) %>
|
61
|
+
# @param muted [Boolean] Uses light gray for Link color, and blue on hover.
|
62
|
+
# @param underline [Boolean] Whether or not to underline the link.
|
63
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
64
|
+
def initialize(href: nil, tag: DEFAULT_TAG, scheme: DEFAULT_SCHEME, muted: false, underline: true, **system_arguments)
|
65
|
+
@system_arguments = system_arguments
|
66
|
+
|
67
|
+
@id = @system_arguments[:id]
|
68
|
+
|
69
|
+
@system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
|
70
|
+
@system_arguments[:href] = href
|
71
|
+
@system_arguments[:classes] = class_names(
|
72
|
+
@system_arguments[:classes],
|
73
|
+
SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme, DEFAULT_SCHEME)],
|
74
|
+
"Link" => tag == :span,
|
75
|
+
"Link--muted" => muted,
|
76
|
+
"no-underline" => !underline
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_render
|
81
|
+
raise ArgumentError, "href is required when using <a> tag" if @system_arguments[:tag] == :a && @system_arguments[:href].nil? && !Rails.env.production?
|
82
|
+
end
|
83
|
+
|
84
|
+
def call
|
85
|
+
if tooltip.present?
|
86
|
+
render Primer::BaseComponent.new(tag: :span, position: :relative) do
|
87
|
+
render(Primer::BaseComponent.new(**@system_arguments)) do
|
88
|
+
content
|
89
|
+
end.to_s + tooltip.to_s
|
90
|
+
end
|
91
|
+
else
|
92
|
+
render(Primer::BaseComponent.new(**@system_arguments)) do
|
93
|
+
content
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -4,6 +4,7 @@ require "view_component/version"
|
|
4
4
|
|
5
5
|
module Primer
|
6
6
|
# @private
|
7
|
+
# :nocov:
|
7
8
|
class Component < ViewComponent::Base
|
8
9
|
include ViewComponent::SlotableV2 unless ViewComponent::Base < ViewComponent::SlotableV2
|
9
10
|
include ViewComponent::PolymorphicSlots unless ViewComponent::Base < ViewComponent::PolymorphicSlots
|
@@ -1,46 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Primer
|
4
|
-
|
5
|
-
|
6
|
-
# @accessibility
|
7
|
-
# Always provide a meaningful `alt`.
|
8
|
-
class Image < Primer::Component
|
9
|
-
# @example Default
|
10
|
-
#
|
11
|
-
# <%= render(Primer::Image.new(src: Primer::ExampleImage::BASE64_SRC, alt: "GitHub")) %>
|
12
|
-
#
|
13
|
-
# @example Helper
|
14
|
-
#
|
15
|
-
# <%= primer_image(src: Primer::ExampleImage::BASE64_SRC, alt: "GitHub") %>
|
16
|
-
#
|
17
|
-
# @example Lazy loading
|
18
|
-
#
|
19
|
-
# <%= render(Primer::Image.new(src: Primer::ExampleImage::BASE64_SRC, alt: "GitHub", lazy: true)) %>
|
20
|
-
#
|
21
|
-
# @example Custom size
|
22
|
-
#
|
23
|
-
# <%= render(Primer::Image.new(src: Primer::ExampleImage::BASE64_SRC, alt: "GitHub", height: 100, width: 100)) %>
|
24
|
-
#
|
25
|
-
# @param src [String] The source url of the image.
|
26
|
-
# @param alt [String] Specifies an alternate text for the image.
|
27
|
-
# @param lazy [Boolean] Whether or not to lazily load the image.
|
28
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
29
|
-
def initialize(src:, alt:, lazy: false, **system_arguments)
|
30
|
-
@system_arguments = deny_tag_argument(**system_arguments)
|
31
|
-
|
32
|
-
@src = src
|
33
|
-
@system_arguments[:tag] = :img
|
34
|
-
@system_arguments[:alt] = alt
|
35
|
-
|
36
|
-
return unless lazy
|
37
|
-
|
38
|
-
@system_arguments[:loading] = :lazy
|
39
|
-
@system_arguments[:decoding] = :async
|
40
|
-
end
|
41
|
-
|
42
|
-
def call
|
43
|
-
render(Primer::BaseComponent.new(src: image_path(@src), **@system_arguments))
|
44
|
-
end
|
4
|
+
class Image < Primer::Alpha::Image
|
5
|
+
status :deprecated
|
45
6
|
end
|
46
7
|
end
|
@@ -1,37 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Primer
|
4
|
-
|
5
|
-
|
6
|
-
# A loading indicator that is shown while the image is loading.
|
7
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
8
|
-
renders_one :loading, lambda { |**system_arguments|
|
9
|
-
deny_tag_argument(**system_arguments)
|
10
|
-
system_arguments[:tag] = :div
|
11
|
-
system_arguments[:"data-loading-slot"] = true
|
12
|
-
|
13
|
-
Primer::BaseComponent.new(**system_arguments)
|
14
|
-
}
|
15
|
-
|
16
|
-
# @example Simple cropper
|
17
|
-
# <%= render(Primer::ImageCrop.new(src: Primer::ExampleImage::BASE64_SRC)) %>
|
18
|
-
#
|
19
|
-
# @example Square cropper
|
20
|
-
# <%= render(Primer::ImageCrop.new(src: Primer::ExampleImage::BASE64_SRC, rounded: false)) %>
|
21
|
-
#
|
22
|
-
# @example Cropper with a custom loader
|
23
|
-
# <%= render(Primer::ImageCrop.new(src: Primer::ExampleImage::BASE64_SRC, rounded: false)) do |cropper| %>
|
24
|
-
# <% cropper.with_loading(style: "width: 120px").with_content("Loading...") %>
|
25
|
-
# <% end %>
|
26
|
-
#
|
27
|
-
# @param src [String] The path of the image to crop.
|
28
|
-
# @param rounded [Boolean] If the crop mask should be a circle. Defaults to true.
|
29
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
30
|
-
def initialize(src:, rounded: true, **system_arguments)
|
31
|
-
@system_arguments = deny_tag_argument(**system_arguments)
|
32
|
-
@system_arguments[:tag] = "image-crop"
|
33
|
-
@system_arguments[:src] = src
|
34
|
-
@system_arguments[:rounded] = true if rounded
|
35
|
-
end
|
4
|
+
class ImageCrop < Primer::Alpha::ImageCrop
|
5
|
+
status :deprecated
|
36
6
|
end
|
37
7
|
end
|