@mixtint/primer-view-components 0.75.2 → 0.84.3
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.
- package/README.md +20 -1
- package/app/assets/javascripts/components/primer/alpha/tree_view/tree_view.d.ts +1 -0
- package/app/assets/javascripts/components/primer/open_project/avatar_fallback.d.ts +28 -0
- package/app/assets/javascripts/components/primer/primer.d.ts +2 -0
- package/app/assets/javascripts/lib/primer/forms/character_counter.d.ts +41 -0
- package/app/assets/javascripts/lib/primer/forms/primer_text_area.d.ts +13 -0
- package/app/assets/javascripts/lib/primer/forms/primer_text_field.d.ts +2 -0
- package/app/assets/javascripts/primer_view_components.js +1 -1
- package/app/assets/javascripts/primer_view_components.js.map +1 -1
- package/app/assets/styles/primer_view_components.css +354 -29
- package/app/assets/styles/primer_view_components.css.map +1 -1
- package/app/components/primer/alpha/action_bar_element.js +68 -77
- package/app/components/primer/alpha/action_list.css +1 -1
- package/app/components/primer/alpha/action_list.js +1 -1
- package/app/components/primer/alpha/action_menu/action_menu_element.js +1 -1
- package/app/components/primer/alpha/dialog.css +12 -0
- package/app/components/primer/alpha/dialog.css.json +2 -1
- package/app/components/primer/alpha/segmented_control.js +1 -1
- package/app/components/primer/alpha/select_panel_element.js +4 -2
- package/app/components/primer/alpha/tab_nav.css +8 -1
- package/app/components/primer/alpha/tab_nav.css.json +1 -0
- package/app/components/primer/alpha/toggle_switch.js +1 -1
- package/app/components/primer/alpha/tool_tip.js +13 -6
- package/app/components/primer/alpha/tree_view/tree_view.d.ts +1 -0
- package/app/components/primer/alpha/tree_view/tree_view.js +28 -20
- package/app/components/primer/alpha/tree_view/tree_view_icon_pair_element.js +1 -1
- package/app/components/primer/alpha/tree_view/tree_view_include_fragment_element.js +1 -1
- package/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.js +25 -2
- package/app/components/primer/alpha/x_banner.js +1 -1
- package/app/components/primer/beta/avatar_stack.css +21 -41
- package/app/components/primer/beta/avatar_stack.css.json +7 -5
- package/app/components/primer/beta/blankslate.css +7 -0
- package/app/components/primer/beta/blankslate.css.json +1 -0
- package/app/components/primer/beta/details_toggle_element.js +1 -1
- package/app/components/primer/beta/nav_list.js +1 -1
- package/app/components/primer/beta/nav_list_group_element.js +1 -1
- package/app/components/primer/beta/state.css +1 -2
- package/app/components/primer/beta/timeline_item.css +2 -1
- package/app/components/primer/dialog_helper.js +24 -9
- package/app/components/primer/open_project/avatar_fallback.d.ts +28 -0
- package/app/components/primer/open_project/avatar_fallback.js +105 -0
- package/app/components/primer/open_project/avatar_stack.css +29 -0
- package/app/components/primer/open_project/avatar_stack.css.json +10 -0
- package/app/components/primer/open_project/border_box/collapsible_header.css +72 -10
- package/app/components/primer/open_project/border_box/collapsible_header.css.json +9 -2
- package/app/components/primer/open_project/inline_message.css +42 -0
- package/app/components/primer/open_project/inline_message.css.json +13 -0
- package/app/components/primer/open_project/page_header.css +51 -0
- package/app/components/primer/open_project/page_header.css.json +10 -1
- package/app/components/primer/open_project/pagination.css +87 -0
- package/app/components/primer/open_project/pagination.css.json +24 -0
- package/app/components/primer/primer.d.ts +2 -0
- package/app/components/primer/primer.js +2 -0
- package/app/components/primer/scrollable_region.js +1 -1
- package/app/lib/primer/forms/character_counter.d.ts +41 -0
- package/app/lib/primer/forms/character_counter.js +114 -0
- package/app/lib/primer/forms/primer_multi_input.js +1 -1
- package/app/lib/primer/forms/primer_text_area.d.ts +13 -0
- package/app/lib/primer/forms/primer_text_area.js +53 -0
- package/app/lib/primer/forms/primer_text_field.d.ts +2 -0
- package/app/lib/primer/forms/primer_text_field.js +17 -3
- package/app/lib/primer/forms/toggle_switch_input.js +1 -1
- package/package.json +24 -23
- package/static/arguments.json +235 -9
- package/static/audited_at.json +6 -0
- package/static/classes.json +30 -2
- package/static/constants.json +67 -1
- package/static/form_previews.json +15 -0
- package/static/info_arch.json +1035 -257
- package/static/previews.json +476 -14
- package/static/statuses.json +6 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.InlineMessage {
|
|
2
|
+
display: grid;
|
|
3
|
+
/* stylelint-disable-next-line primer/typography */
|
|
4
|
+
font-size: var(--inline-message-fontSize);
|
|
5
|
+
/* stylelint-disable-next-line primer/typography */
|
|
6
|
+
line-height: var(--inline-message-lineHeight);
|
|
7
|
+
/* stylelint-disable-next-line primer/colors */
|
|
8
|
+
color: var(--inline-message-fgColor);
|
|
9
|
+
column-gap: 0.5rem;
|
|
10
|
+
grid-template-columns: auto 1fr;
|
|
11
|
+
align-items: start;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.InlineMessage[data-size='small'] {
|
|
15
|
+
--inline-message-fontSize: var(--text-body-size-small);
|
|
16
|
+
--inline-message-lineHeight: var(--text-body-lineHeight-small, 1.6666);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.InlineMessage[data-size='medium'] {
|
|
20
|
+
--inline-message-fontSize: var(--text-body-size-medium);
|
|
21
|
+
--inline-message-lineHeight: var(--text-body-lineHeight-medium, 1.4285);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.InlineMessage[data-variant='warning'] {
|
|
25
|
+
--inline-message-fgColor: var(--fgColor-attention);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.InlineMessage[data-variant='critical'] {
|
|
29
|
+
--inline-message-fgColor: var(--fgColor-danger);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.InlineMessage[data-variant='success'] {
|
|
33
|
+
--inline-message-fgColor: var(--fgColor-success);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.InlineMessage[data-variant='unavailable'] {
|
|
37
|
+
--inline-message-fgColor: var(--fgColor-muted);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.InlineMessageIcon {
|
|
41
|
+
min-height: calc(var(--inline-message-lineHeight) * var(--inline-message-fontSize));
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "open_project/inline_message",
|
|
3
|
+
"selectors": [
|
|
4
|
+
".InlineMessage",
|
|
5
|
+
".InlineMessage[data-size='small']",
|
|
6
|
+
".InlineMessage[data-size='medium']",
|
|
7
|
+
".InlineMessage[data-variant='warning']",
|
|
8
|
+
".InlineMessage[data-variant='critical']",
|
|
9
|
+
".InlineMessage[data-variant='success']",
|
|
10
|
+
".InlineMessage[data-variant='unavailable']",
|
|
11
|
+
".InlineMessageIcon"
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -70,6 +70,39 @@
|
|
|
70
70
|
justify-content: flex-end;
|
|
71
71
|
display: flex;
|
|
72
72
|
align-items: center;
|
|
73
|
+
color: var(--fgColor-default);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* OpenProject legacy form markup inside header actions: match Primer / dark theme */
|
|
77
|
+
|
|
78
|
+
.PageHeader-actions .form--label {
|
|
79
|
+
color: var(--fgColor-muted);
|
|
80
|
+
min-width: 0;
|
|
81
|
+
max-width: none;
|
|
82
|
+
flex-grow: 0;
|
|
83
|
+
padding: 0;
|
|
84
|
+
margin: 0;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
font-size: var(--text-body-size-medium);
|
|
87
|
+
line-height: var(--control-medium-lineBoxHeight);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.PageHeader-actions select.form--select {
|
|
91
|
+
color: var(--fgColor-default);
|
|
92
|
+
background-color: var(--control-bgColor-rest);
|
|
93
|
+
border-color: var(--borderColor-default);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.PageHeader-actions select.form--select:focus,
|
|
97
|
+
.PageHeader-actions select.form--select:active {
|
|
98
|
+
border-color: var(--fgColor-accent);
|
|
99
|
+
outline: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.PageHeader-actions select.form--select option,
|
|
103
|
+
.PageHeader-actions select.form--select optgroup {
|
|
104
|
+
background-color: var(--bgColor-default);
|
|
105
|
+
color: var(--fgColor-default);
|
|
73
106
|
}
|
|
74
107
|
|
|
75
108
|
.PageHeader-breadcrumbs {
|
|
@@ -85,3 +118,21 @@
|
|
|
85
118
|
.PageHeader-parentLink {
|
|
86
119
|
flex: 1 1 auto;
|
|
87
120
|
}
|
|
121
|
+
|
|
122
|
+
/* Hide the context bar on desktop when no breadcrumb is visible */
|
|
123
|
+
|
|
124
|
+
@media screen and (min-width: 543.98px) {
|
|
125
|
+
.PageHeader--noBreadcrumb .PageHeader-contextBar {
|
|
126
|
+
display: none;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Match the title bar and actions height with the toggle menu button for proper vertical alignment */
|
|
131
|
+
|
|
132
|
+
.PageHeader--noBreadcrumb .PageHeader-titleBar {
|
|
133
|
+
line-height: var(--control-small-size);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.PageHeader--noBreadcrumb .PageHeader-actions {
|
|
137
|
+
height: var(--control-small-size);
|
|
138
|
+
}
|
|
@@ -13,8 +13,17 @@
|
|
|
13
13
|
".PageHeader-tabNavBar .PageHeader-tabNav",
|
|
14
14
|
".PageHeader--withTabNav .PageHeader-description",
|
|
15
15
|
".PageHeader-actions",
|
|
16
|
+
".PageHeader-actions .form--label",
|
|
17
|
+
".PageHeader-actions select.form--select",
|
|
18
|
+
".PageHeader-actions select.form--select:focus",
|
|
19
|
+
".PageHeader-actions select.form--select:active",
|
|
20
|
+
".PageHeader-actions select.form--select option",
|
|
21
|
+
".PageHeader-actions select.form--select optgroup",
|
|
16
22
|
".PageHeader-breadcrumbs",
|
|
17
23
|
".PageHeader-leadingAction",
|
|
18
|
-
".PageHeader-parentLink"
|
|
24
|
+
".PageHeader-parentLink",
|
|
25
|
+
".PageHeader--noBreadcrumb .PageHeader-contextBar",
|
|
26
|
+
".PageHeader--noBreadcrumb .PageHeader-titleBar",
|
|
27
|
+
".PageHeader--noBreadcrumb .PageHeader-actions"
|
|
19
28
|
]
|
|
20
29
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
.Page {
|
|
2
|
+
min-width: var(--base-size-32);
|
|
3
|
+
height: var(--base-size-32);
|
|
4
|
+
padding: var(--base-size-8) var(--base-size-6);
|
|
5
|
+
margin-right: var(--base-size-4);
|
|
6
|
+
color: var(--fgColor-default);
|
|
7
|
+
white-space: nowrap;
|
|
8
|
+
vertical-align: middle;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
border-radius: var(--borderRadius-medium,.375rem);
|
|
11
|
+
transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.Page[rel='prev'] > svg,
|
|
18
|
+
.Page[rel='next'] > svg {
|
|
19
|
+
height: var(--base-size-16);
|
|
20
|
+
width: var(--base-size-16);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.Page[rel='prev'] > svg {
|
|
24
|
+
margin-inline-end: var(--base-size-4);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.Page[rel='next'] > svg {
|
|
28
|
+
margin-inline-start: var(--base-size-4);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.Page:last-child {
|
|
32
|
+
margin-right: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.Page:hover,
|
|
36
|
+
.Page:focus {
|
|
37
|
+
-webkit-text-decoration: none;
|
|
38
|
+
text-decoration: none;
|
|
39
|
+
background-color: var(--control-transparent-bgColor-hover);
|
|
40
|
+
outline: 0;
|
|
41
|
+
transition-duration: 0.1s;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.Page:focus-visible {
|
|
45
|
+
outline: 2px solid var(--bgColor-accent-emphasis);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.Page:active {
|
|
49
|
+
border-color: var(--borderColor-muted);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.Page[rel='prev'],
|
|
53
|
+
.Page[rel='next'] {
|
|
54
|
+
color: var(--fgColor-accent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.Page[aria-current],
|
|
58
|
+
.Page[aria-current]:hover {
|
|
59
|
+
color: var(--fgColor-onEmphasis);
|
|
60
|
+
background-color: var(--bgColor-accent-emphasis);
|
|
61
|
+
border-color: transparent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.Page[aria-current]:focus-visible {
|
|
65
|
+
outline: 2px solid var(--bgColor-accent-emphasis);
|
|
66
|
+
box-shadow: inset 0 0 0 var(--base-size-2) var(--fgColor-onEmphasis);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.Page[aria-hidden],
|
|
70
|
+
.Page[aria-hidden]:hover,
|
|
71
|
+
.Page[role='presentation'],
|
|
72
|
+
.Page[role='presentation']:hover {
|
|
73
|
+
color: var(--fgColor-disabled);
|
|
74
|
+
cursor: default;
|
|
75
|
+
background-color: transparent;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.PaginationContainer {
|
|
79
|
+
margin: var(--base-size-16) 0;
|
|
80
|
+
text-align: center;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@media (max-width: 543.98px){
|
|
84
|
+
.Page:not([rel='prev']):not([rel='next']) {
|
|
85
|
+
display: none;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "open_project/pagination",
|
|
3
|
+
"selectors": [
|
|
4
|
+
".Page",
|
|
5
|
+
".Page[rel='prev'] > svg",
|
|
6
|
+
".Page[rel='next'] > svg",
|
|
7
|
+
".Page:last-child",
|
|
8
|
+
".Page:hover",
|
|
9
|
+
".Page:focus",
|
|
10
|
+
".Page:focus-visible",
|
|
11
|
+
".Page:active",
|
|
12
|
+
".Page[rel='prev']",
|
|
13
|
+
".Page[rel='next']",
|
|
14
|
+
".Page[aria-current]",
|
|
15
|
+
".Page[aria-current]:hover",
|
|
16
|
+
".Page[aria-current]:focus-visible",
|
|
17
|
+
".Page[aria-hidden]",
|
|
18
|
+
".Page[aria-hidden]:hover",
|
|
19
|
+
".Page[role='presentation']",
|
|
20
|
+
".Page[role='presentation']:hover",
|
|
21
|
+
".PaginationContainer",
|
|
22
|
+
".Page:not([rel='prev']):not([rel='next'])"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -22,6 +22,7 @@ import './beta/relative_time';
|
|
|
22
22
|
import './alpha/tab_container';
|
|
23
23
|
import '../../lib/primer/forms/primer_multi_input';
|
|
24
24
|
import '../../lib/primer/forms/primer_text_field';
|
|
25
|
+
import '../../lib/primer/forms/primer_text_area';
|
|
25
26
|
import '../../lib/primer/forms/toggle_switch_input';
|
|
26
27
|
import './alpha/action_menu/action_menu_element';
|
|
27
28
|
import './alpha/select_panel_element';
|
|
@@ -30,6 +31,7 @@ import './alpha/tree_view/tree_view';
|
|
|
30
31
|
import './alpha/tree_view/tree_view_icon_pair_element';
|
|
31
32
|
import './alpha/tree_view/tree_view_sub_tree_node_element';
|
|
32
33
|
import './alpha/tree_view/tree_view_include_fragment_element';
|
|
34
|
+
import './open_project/avatar_fallback';
|
|
33
35
|
import './open_project/page_header_element';
|
|
34
36
|
import './open_project/zen_mode_button';
|
|
35
37
|
import './open_project/sub_header_element';
|
|
@@ -22,6 +22,7 @@ import './beta/relative_time';
|
|
|
22
22
|
import './alpha/tab_container';
|
|
23
23
|
import '../../lib/primer/forms/primer_multi_input';
|
|
24
24
|
import '../../lib/primer/forms/primer_text_field';
|
|
25
|
+
import '../../lib/primer/forms/primer_text_area';
|
|
25
26
|
import '../../lib/primer/forms/toggle_switch_input';
|
|
26
27
|
import './alpha/action_menu/action_menu_element';
|
|
27
28
|
import './alpha/select_panel_element';
|
|
@@ -30,6 +31,7 @@ import './alpha/tree_view/tree_view';
|
|
|
30
31
|
import './alpha/tree_view/tree_view_icon_pair_element';
|
|
31
32
|
import './alpha/tree_view/tree_view_sub_tree_node_element';
|
|
32
33
|
import './alpha/tree_view/tree_view_include_fragment_element';
|
|
34
|
+
import './open_project/avatar_fallback';
|
|
33
35
|
import './open_project/page_header_element';
|
|
34
36
|
import './open_project/zen_mode_button';
|
|
35
37
|
import './open_project/sub_header_element';
|
|
@@ -46,7 +46,7 @@ __decorate([
|
|
|
46
46
|
attr
|
|
47
47
|
], ScrollableRegionElement.prototype, "labelledBy", void 0);
|
|
48
48
|
ScrollableRegionElement = __decorate([
|
|
49
|
-
controller
|
|
49
|
+
controller('scrollable-region')
|
|
50
50
|
], ScrollableRegionElement);
|
|
51
51
|
export { ScrollableRegionElement };
|
|
52
52
|
window.ScrollableRegionElement = ScrollableRegionElement;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared character counting functionality for text inputs with character limits.
|
|
3
|
+
* Handles real-time character count updates, validation, and aria-live announcements.
|
|
4
|
+
*/
|
|
5
|
+
export declare class CharacterCounter {
|
|
6
|
+
private inputElement;
|
|
7
|
+
private characterLimitElement;
|
|
8
|
+
private characterLimitSrElement;
|
|
9
|
+
private SCREEN_READER_DELAY;
|
|
10
|
+
private announceTimeout;
|
|
11
|
+
private isInitialLoad;
|
|
12
|
+
constructor(inputElement: HTMLInputElement | HTMLTextAreaElement, characterLimitElement: HTMLElement, characterLimitSrElement: HTMLElement);
|
|
13
|
+
/**
|
|
14
|
+
* Initialize character counting by setting up event listener and initial count
|
|
15
|
+
*/
|
|
16
|
+
initialize(signal?: AbortSignal): void;
|
|
17
|
+
/**
|
|
18
|
+
* Clean up any pending timeouts
|
|
19
|
+
*/
|
|
20
|
+
cleanup(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Pluralizes a word based on the count
|
|
23
|
+
*/
|
|
24
|
+
private pluralize;
|
|
25
|
+
/**
|
|
26
|
+
* Update the character count display and validation state
|
|
27
|
+
*/
|
|
28
|
+
private updateCharacterCount;
|
|
29
|
+
/**
|
|
30
|
+
* Announce character count to screen readers with debouncing
|
|
31
|
+
*/
|
|
32
|
+
private announceToScreenReader;
|
|
33
|
+
/**
|
|
34
|
+
* Set error when character limit is exceeded
|
|
35
|
+
*/
|
|
36
|
+
private setError;
|
|
37
|
+
/**
|
|
38
|
+
* Clear error when back under character limit
|
|
39
|
+
*/
|
|
40
|
+
private clearError;
|
|
41
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared character counting functionality for text inputs with character limits.
|
|
3
|
+
* Handles real-time character count updates, validation, and aria-live announcements.
|
|
4
|
+
*/
|
|
5
|
+
export class CharacterCounter {
|
|
6
|
+
constructor(inputElement, characterLimitElement, characterLimitSrElement) {
|
|
7
|
+
this.inputElement = inputElement;
|
|
8
|
+
this.characterLimitElement = characterLimitElement;
|
|
9
|
+
this.characterLimitSrElement = characterLimitSrElement;
|
|
10
|
+
this.SCREEN_READER_DELAY = 500;
|
|
11
|
+
this.announceTimeout = null;
|
|
12
|
+
this.isInitialLoad = true;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Initialize character counting by setting up event listener and initial count
|
|
16
|
+
*/
|
|
17
|
+
initialize(signal) {
|
|
18
|
+
this.inputElement.addEventListener('keyup', () => this.updateCharacterCount(), signal ? { signal } : undefined); // Keyup used over input for better screen reader support
|
|
19
|
+
this.inputElement.addEventListener('paste', () => setTimeout(() => this.updateCharacterCount(), 50), // Gives the pasted content time to register
|
|
20
|
+
signal ? { signal } : undefined);
|
|
21
|
+
this.updateCharacterCount();
|
|
22
|
+
this.isInitialLoad = false;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Clean up any pending timeouts
|
|
26
|
+
*/
|
|
27
|
+
cleanup() {
|
|
28
|
+
if (this.announceTimeout) {
|
|
29
|
+
clearTimeout(this.announceTimeout);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Pluralizes a word based on the count
|
|
34
|
+
*/
|
|
35
|
+
pluralize(count, string) {
|
|
36
|
+
return count === 1 ? string : `${string}s`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Update the character count display and validation state
|
|
40
|
+
*/
|
|
41
|
+
updateCharacterCount() {
|
|
42
|
+
if (!this.characterLimitElement)
|
|
43
|
+
return;
|
|
44
|
+
const maxLengthAttr = this.characterLimitElement.getAttribute('data-max-length');
|
|
45
|
+
if (!maxLengthAttr)
|
|
46
|
+
return;
|
|
47
|
+
const maxLength = parseInt(maxLengthAttr, 10);
|
|
48
|
+
const currentLength = this.inputElement.value.length;
|
|
49
|
+
const charactersRemaining = maxLength - currentLength;
|
|
50
|
+
let message = '';
|
|
51
|
+
if (charactersRemaining >= 0) {
|
|
52
|
+
const characterText = this.pluralize(charactersRemaining, 'character');
|
|
53
|
+
message = `${charactersRemaining} ${characterText} remaining`;
|
|
54
|
+
const textSpan = this.characterLimitElement.querySelector('.FormControl-caption-text');
|
|
55
|
+
if (textSpan) {
|
|
56
|
+
textSpan.textContent = message;
|
|
57
|
+
}
|
|
58
|
+
this.clearError();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const charactersOver = -charactersRemaining;
|
|
62
|
+
const characterText = this.pluralize(charactersOver, 'character');
|
|
63
|
+
message = `${charactersOver} ${characterText} over`;
|
|
64
|
+
const textSpan = this.characterLimitElement.querySelector('.FormControl-caption-text');
|
|
65
|
+
if (textSpan) {
|
|
66
|
+
textSpan.textContent = message;
|
|
67
|
+
}
|
|
68
|
+
this.setError();
|
|
69
|
+
}
|
|
70
|
+
// We don't want this announced on initial load
|
|
71
|
+
if (!this.isInitialLoad) {
|
|
72
|
+
this.announceToScreenReader(message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Announce character count to screen readers with debouncing
|
|
77
|
+
*/
|
|
78
|
+
announceToScreenReader(message) {
|
|
79
|
+
if (this.announceTimeout) {
|
|
80
|
+
clearTimeout(this.announceTimeout);
|
|
81
|
+
}
|
|
82
|
+
this.announceTimeout = window.setTimeout(() => {
|
|
83
|
+
if (this.characterLimitSrElement) {
|
|
84
|
+
this.characterLimitSrElement.textContent = message;
|
|
85
|
+
}
|
|
86
|
+
}, this.SCREEN_READER_DELAY);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Set error when character limit is exceeded
|
|
90
|
+
*/
|
|
91
|
+
setError() {
|
|
92
|
+
this.inputElement.setAttribute('invalid', 'true');
|
|
93
|
+
this.inputElement.setAttribute('aria-invalid', 'true');
|
|
94
|
+
this.characterLimitElement.classList.add('fgColor-danger');
|
|
95
|
+
// Show danger icon
|
|
96
|
+
const icon = this.characterLimitElement.querySelector('.FormControl-caption-icon');
|
|
97
|
+
if (icon) {
|
|
98
|
+
icon.removeAttribute('hidden');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Clear error when back under character limit
|
|
103
|
+
*/
|
|
104
|
+
clearError() {
|
|
105
|
+
this.inputElement.removeAttribute('invalid');
|
|
106
|
+
this.inputElement.removeAttribute('aria-invalid');
|
|
107
|
+
this.characterLimitElement.classList.remove('fgColor-danger');
|
|
108
|
+
// Hide danger icon
|
|
109
|
+
const icon = this.characterLimitElement.querySelector('.FormControl-caption-icon');
|
|
110
|
+
if (icon) {
|
|
111
|
+
icon.setAttribute('hidden', '');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -35,7 +35,7 @@ __decorate([
|
|
|
35
35
|
targets
|
|
36
36
|
], PrimerMultiInputElement.prototype, "fields", void 0);
|
|
37
37
|
PrimerMultiInputElement = __decorate([
|
|
38
|
-
controller
|
|
38
|
+
controller('primer-multi-input')
|
|
39
39
|
], PrimerMultiInputElement);
|
|
40
40
|
export { PrimerMultiInputElement };
|
|
41
41
|
if (!window.customElements.get('primer-multi-input')) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class PrimerTextAreaElement extends HTMLElement {
|
|
2
|
+
#private;
|
|
3
|
+
inputElement: HTMLTextAreaElement;
|
|
4
|
+
characterLimitElement: HTMLElement;
|
|
5
|
+
characterLimitSrElement: HTMLElement;
|
|
6
|
+
connectedCallback(): void;
|
|
7
|
+
disconnectedCallback(): void;
|
|
8
|
+
}
|
|
9
|
+
declare global {
|
|
10
|
+
interface Window {
|
|
11
|
+
PrimerTextAreaElement: typeof PrimerTextAreaElement;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
+
};
|
|
13
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
14
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
15
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
16
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
17
|
+
};
|
|
18
|
+
var _PrimerTextAreaElement_characterCounter;
|
|
19
|
+
import { controller, target } from '@github/catalyst';
|
|
20
|
+
import { CharacterCounter } from './character_counter';
|
|
21
|
+
let PrimerTextAreaElement = class PrimerTextAreaElement extends HTMLElement {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments);
|
|
24
|
+
_PrimerTextAreaElement_characterCounter.set(this, null);
|
|
25
|
+
}
|
|
26
|
+
connectedCallback() {
|
|
27
|
+
if (this.characterLimitElement) {
|
|
28
|
+
__classPrivateFieldSet(this, _PrimerTextAreaElement_characterCounter, new CharacterCounter(this.inputElement, this.characterLimitElement, this.characterLimitSrElement), "f");
|
|
29
|
+
__classPrivateFieldGet(this, _PrimerTextAreaElement_characterCounter, "f").initialize();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
disconnectedCallback() {
|
|
33
|
+
__classPrivateFieldGet(this, _PrimerTextAreaElement_characterCounter, "f")?.cleanup();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
_PrimerTextAreaElement_characterCounter = new WeakMap();
|
|
37
|
+
__decorate([
|
|
38
|
+
target
|
|
39
|
+
], PrimerTextAreaElement.prototype, "inputElement", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
target
|
|
42
|
+
], PrimerTextAreaElement.prototype, "characterLimitElement", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
target
|
|
45
|
+
], PrimerTextAreaElement.prototype, "characterLimitSrElement", void 0);
|
|
46
|
+
PrimerTextAreaElement = __decorate([
|
|
47
|
+
controller('primer-text-area')
|
|
48
|
+
], PrimerTextAreaElement);
|
|
49
|
+
export { PrimerTextAreaElement };
|
|
50
|
+
if (!window.customElements.get('primer-text-area')) {
|
|
51
|
+
Object.assign(window, { PrimerTextAreaElement });
|
|
52
|
+
window.customElements.define('primer-text-area', PrimerTextAreaElement);
|
|
53
|
+
}
|
|
@@ -15,6 +15,8 @@ export declare class PrimerTextFieldElement extends HTMLElement {
|
|
|
15
15
|
validationErrorIcon: HTMLElement;
|
|
16
16
|
leadingVisual: HTMLElement;
|
|
17
17
|
leadingSpinner: HTMLElement;
|
|
18
|
+
characterLimitElement: HTMLElement;
|
|
19
|
+
characterLimitSrElement: HTMLElement;
|
|
18
20
|
connectedCallback(): void;
|
|
19
21
|
disconnectedCallback(): void;
|
|
20
22
|
clearContents(): void;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable custom-elements/expose-class-on-global */
|
|
2
1
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
2
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
3
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -16,13 +15,15 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
16
15
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
17
16
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
18
17
|
};
|
|
19
|
-
var _PrimerTextFieldElement_abortController;
|
|
18
|
+
var _PrimerTextFieldElement_abortController, _PrimerTextFieldElement_characterCounter;
|
|
20
19
|
import '@github/auto-check-element';
|
|
21
20
|
import { controller, target } from '@github/catalyst';
|
|
21
|
+
import { CharacterCounter } from './character_counter';
|
|
22
22
|
let PrimerTextFieldElement = class PrimerTextFieldElement extends HTMLElement {
|
|
23
23
|
constructor() {
|
|
24
24
|
super(...arguments);
|
|
25
25
|
_PrimerTextFieldElement_abortController.set(this, void 0);
|
|
26
|
+
_PrimerTextFieldElement_characterCounter.set(this, null);
|
|
26
27
|
}
|
|
27
28
|
connectedCallback() {
|
|
28
29
|
__classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")?.abort();
|
|
@@ -40,9 +41,15 @@ let PrimerTextFieldElement = class PrimerTextFieldElement extends HTMLElement {
|
|
|
40
41
|
const errorMessage = await event.detail.response.text();
|
|
41
42
|
this.setError(errorMessage);
|
|
42
43
|
}, { signal });
|
|
44
|
+
// Set up character limit tracking if present
|
|
45
|
+
if (this.characterLimitElement) {
|
|
46
|
+
__classPrivateFieldSet(this, _PrimerTextFieldElement_characterCounter, new CharacterCounter(this.inputElement, this.characterLimitElement, this.characterLimitSrElement), "f");
|
|
47
|
+
__classPrivateFieldGet(this, _PrimerTextFieldElement_characterCounter, "f").initialize(signal);
|
|
48
|
+
}
|
|
43
49
|
}
|
|
44
50
|
disconnectedCallback() {
|
|
45
51
|
__classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")?.abort();
|
|
52
|
+
__classPrivateFieldGet(this, _PrimerTextFieldElement_characterCounter, "f")?.cleanup();
|
|
46
53
|
}
|
|
47
54
|
clearContents() {
|
|
48
55
|
this.inputElement.value = '';
|
|
@@ -92,6 +99,7 @@ let PrimerTextFieldElement = class PrimerTextFieldElement extends HTMLElement {
|
|
|
92
99
|
}
|
|
93
100
|
};
|
|
94
101
|
_PrimerTextFieldElement_abortController = new WeakMap();
|
|
102
|
+
_PrimerTextFieldElement_characterCounter = new WeakMap();
|
|
95
103
|
__decorate([
|
|
96
104
|
target
|
|
97
105
|
], PrimerTextFieldElement.prototype, "inputElement", void 0);
|
|
@@ -113,7 +121,13 @@ __decorate([
|
|
|
113
121
|
__decorate([
|
|
114
122
|
target
|
|
115
123
|
], PrimerTextFieldElement.prototype, "leadingSpinner", void 0);
|
|
124
|
+
__decorate([
|
|
125
|
+
target
|
|
126
|
+
], PrimerTextFieldElement.prototype, "characterLimitElement", void 0);
|
|
127
|
+
__decorate([
|
|
128
|
+
target
|
|
129
|
+
], PrimerTextFieldElement.prototype, "characterLimitSrElement", void 0);
|
|
116
130
|
PrimerTextFieldElement = __decorate([
|
|
117
|
-
controller
|
|
131
|
+
controller('primer-text-field')
|
|
118
132
|
], PrimerTextFieldElement);
|
|
119
133
|
export { PrimerTextFieldElement };
|
|
@@ -29,6 +29,6 @@ __decorate([
|
|
|
29
29
|
target
|
|
30
30
|
], ToggleSwitchInputElement.prototype, "validationMessageElement", void 0);
|
|
31
31
|
ToggleSwitchInputElement = __decorate([
|
|
32
|
-
controller
|
|
32
|
+
controller('toggle-switch-input')
|
|
33
33
|
], ToggleSwitchInputElement);
|
|
34
34
|
export { ToggleSwitchInputElement };
|