@hashicorp/design-system-components 2.14.2 → 3.0.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.
- package/CHANGELOG-FIGMA-COMPONENTS.md +177 -0
- package/CHANGELOG-FIGMA-FOUNDATIONS.md +2 -0
- package/CHANGELOG.md +368 -55
- package/README.md +1 -1
- package/addon/components/hds/accordion/item/index.hbs +16 -5
- package/addon/components/hds/application-state/body.hbs +2 -2
- package/addon/components/hds/application-state/header.hbs +4 -4
- package/addon/components/hds/button/{index.js → index.ts} +34 -4
- package/addon/components/hds/copy/button/index.hbs +1 -1
- package/addon/components/hds/copy/button/index.js +14 -2
- package/addon/components/hds/copy/snippet/index.hbs +3 -3
- package/addon/components/hds/copy/snippet/index.js +19 -2
- package/addon/components/hds/dropdown/index.js +0 -3
- package/addon/components/hds/dropdown/list-item/checkmark.hbs +9 -3
- package/addon/components/hds/dropdown/list-item/copy-item.hbs +8 -4
- package/addon/components/hds/dropdown/list-item/copy-item.js +13 -0
- package/addon/components/hds/dropdown/list-item/description.hbs +9 -2
- package/addon/components/hds/dropdown/list-item/description.js +0 -18
- package/addon/components/hds/dropdown/list-item/interactive.hbs +4 -4
- package/addon/components/hds/dropdown/list-item/radio.hbs +6 -1
- package/addon/components/hds/dropdown/list-item/title.hbs +9 -2
- package/addon/components/hds/dropdown/list-item/title.js +0 -18
- package/addon/components/hds/flyout/description.hbs +2 -2
- package/addon/components/hds/flyout/header.hbs +4 -4
- package/addon/components/hds/form/error/index.hbs +2 -2
- package/addon/components/hds/form/error/index.js +0 -3
- package/addon/components/hds/form/error/message.hbs +2 -2
- package/addon/components/hds/form/helper-text/index.hbs +10 -2
- package/addon/components/hds/form/helper-text/index.js +0 -3
- package/addon/components/hds/form/indicator/index.hbs +1 -1
- package/addon/components/hds/form/indicator/index.js +0 -3
- package/addon/components/hds/form/masked-input/base.hbs +8 -10
- package/addon/components/hds/form/masked-input/base.js +14 -14
- package/addon/components/hds/form/masked-input/field.hbs +3 -1
- package/addon/components/hds/form/radio-card/description.hbs +6 -1
- package/addon/components/hds/form/radio-card/group.hbs +1 -2
- package/addon/components/hds/form/radio-card/index.js +5 -33
- package/addon/components/hds/form/radio-card/label.hbs +7 -1
- package/addon/components/hds/form/text-input/base.js +5 -0
- package/addon/components/hds/form/text-input/field.hbs +23 -11
- package/addon/components/hds/form/text-input/field.js +59 -0
- package/addon/components/hds/form/visibility-toggle/index.hbs +8 -0
- package/addon/components/hds/interactive/{index.js → index.ts} +28 -3
- package/addon/components/hds/modal/header.hbs +4 -4
- package/addon/components/hds/page-header/description.hbs +7 -1
- package/addon/components/hds/page-header/subtitle.hbs +7 -1
- package/addon/components/hds/page-header/title.hbs +7 -1
- package/addon/components/hds/pagination/compact/index.js +0 -16
- package/addon/components/hds/pagination/info/index.hbs +2 -2
- package/addon/components/hds/pagination/nav/arrow.hbs +16 -4
- package/addon/components/hds/pagination/nav/arrow.js +0 -2
- package/addon/components/hds/pagination/nav/number.hbs +2 -1
- package/addon/components/hds/pagination/nav/number.js +1 -6
- package/addon/components/hds/side-nav/list/index.hbs +2 -2
- package/addon/components/hds/stepper/step/indicator.hbs +6 -1
- package/addon/components/hds/tag/index.hbs +2 -2
- package/addon/modifiers/hds-clipboard.js +163 -0
- package/addon/template-registry.ts +12 -0
- package/app/components/hds/form/visibility-toggle/index.js +6 -0
- package/app/modifiers/hds-clipboard.js +6 -0
- package/app/styles/components/button.scss +2 -0
- package/app/styles/components/copy/snippet.scss +22 -14
- package/app/styles/components/dropdown.scss +5 -8
- package/app/styles/components/flyout.scss +0 -2
- package/app/styles/components/form/group.scss +5 -0
- package/app/styles/components/form/index.scss +1 -0
- package/app/styles/components/form/masked-input.scss +0 -9
- package/app/styles/components/form/radio-card.scss +2 -4
- package/app/styles/components/form/text-input.scss +17 -0
- package/app/styles/components/form/visibility-toggle.scss +23 -0
- package/app/styles/components/stepper/step-indicator.scss +0 -3
- package/app/styles/components/tag.scss +1 -5
- package/app/styles/mixins/_button.scss +14 -4
- package/index.js +3 -0
- package/package.json +33 -4
- package/tsconfig.json +46 -0
- package/types/dummy/index.d.ts +6 -0
- package/types/global.d.ts +12 -0
|
@@ -12,10 +12,8 @@ import { setAriaDescribedBy } from '@hashicorp/design-system-components/utils/hd
|
|
|
12
12
|
|
|
13
13
|
export const DEFAULT_CONTROL_POSITION = 'bottom';
|
|
14
14
|
export const DEFAULT_ALIGNMENT = 'left';
|
|
15
|
-
export const DEFAULT_LAYOUT = 'fluid';
|
|
16
15
|
export const CONTROL_POSITIONS = ['bottom', 'left'];
|
|
17
16
|
export const ALIGNMENTS = ['left', 'center'];
|
|
18
|
-
export const LAYOUTS = ['fluid', 'fixed'];
|
|
19
17
|
|
|
20
18
|
export default class HdsFormRadioCardIndexComponent extends Component {
|
|
21
19
|
@tracked ariaDescribedBy = this.args.extraAriaDescribedBy;
|
|
@@ -71,34 +69,6 @@ export default class HdsFormRadioCardIndexComponent extends Component {
|
|
|
71
69
|
return alignment;
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
/**
|
|
75
|
-
* Sets the layout of the card within the group
|
|
76
|
-
* Accepted values: fluid, fixed
|
|
77
|
-
*
|
|
78
|
-
* @param layout
|
|
79
|
-
* @type {string}
|
|
80
|
-
* @default 'fluid'
|
|
81
|
-
*/
|
|
82
|
-
get layout() {
|
|
83
|
-
let { layout = DEFAULT_LAYOUT } = this.args;
|
|
84
|
-
|
|
85
|
-
assert(
|
|
86
|
-
`@layout for "Hds::Form::RadioCard" must be one of the following: ${LAYOUTS.join(
|
|
87
|
-
', '
|
|
88
|
-
)}; received: ${layout}`,
|
|
89
|
-
LAYOUTS.includes(layout)
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// if the `@layout` is set to 'fixed' we need a `@maxWidth` value to constrain the card to
|
|
93
|
-
if (layout === 'fixed') {
|
|
94
|
-
assert(
|
|
95
|
-
`@maxWidth for "Hds::Form::RadioCard" with @layout "fixed" is required`,
|
|
96
|
-
this.args.maxWidth
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
return layout;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
72
|
/**
|
|
103
73
|
* Get the class names to apply to the component.
|
|
104
74
|
* @method classNames
|
|
@@ -113,6 +83,11 @@ export default class HdsFormRadioCardIndexComponent extends Component {
|
|
|
113
83
|
if (this.args.disabled) {
|
|
114
84
|
classes.push('hds-form-radio-card--disabled');
|
|
115
85
|
}
|
|
86
|
+
if (this.args.maxWidth) {
|
|
87
|
+
classes.push('hds-form-radio-card--has-fixed-width');
|
|
88
|
+
} else {
|
|
89
|
+
classes.push('hds-form-radio-card--has-fluid-width');
|
|
90
|
+
}
|
|
116
91
|
|
|
117
92
|
// add a class based on the @controlPosition argument
|
|
118
93
|
classes.push(`hds-form-radio-card--control-${this.controlPosition}`);
|
|
@@ -120,9 +95,6 @@ export default class HdsFormRadioCardIndexComponent extends Component {
|
|
|
120
95
|
// add a class based on the @alignment argument
|
|
121
96
|
classes.push(`hds-form-radio-card--align-${this.alignment}`);
|
|
122
97
|
|
|
123
|
-
// add a class based on the @layout argument
|
|
124
|
-
classes.push(`hds-form-radio-card--layout-${this.layout}`);
|
|
125
|
-
|
|
126
98
|
return classes.join(' ');
|
|
127
99
|
}
|
|
128
100
|
}
|
|
@@ -2,4 +2,10 @@
|
|
|
2
2
|
Copyright (c) HashiCorp, Inc.
|
|
3
3
|
SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
}}
|
|
5
|
-
<
|
|
5
|
+
<Hds::Text::Display
|
|
6
|
+
class="hds-form-radio-card__label"
|
|
7
|
+
@tag="span"
|
|
8
|
+
@size="300"
|
|
9
|
+
@weight="bold"
|
|
10
|
+
...attributes
|
|
11
|
+
>{{yield}}</Hds::Text::Display>
|
|
@@ -57,6 +57,11 @@ export default class HdsFormTextInputBaseComponent extends Component {
|
|
|
57
57
|
classes.push(`hds-form-text-input--is-invalid`);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// add a class based on the @hasVisibilityToggle argument
|
|
61
|
+
if (this.args.hasVisibilityToggle) {
|
|
62
|
+
classes.push(`hds-form-text-input--has-visibility-toggle`);
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
// add a class based on the @isLoading argument
|
|
61
66
|
if (this.args.isLoading) {
|
|
62
67
|
classes.push(`hds-form-text-input--is-loading`);
|
|
@@ -14,16 +14,28 @@
|
|
|
14
14
|
{{yield (hash Label=F.Label isRequired=F.isRequired isOptional=F.isOptional)}}
|
|
15
15
|
{{yield (hash HelperText=F.HelperText Error=F.Error)}}
|
|
16
16
|
<F.Control>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
<div class="hds-form-text-input__wrapper" {{style width=@width}}>
|
|
18
|
+
<Hds::Form::TextInput::Base
|
|
19
|
+
@type={{this.type}}
|
|
20
|
+
@value={{@value}}
|
|
21
|
+
@isInvalid={{@isInvalid}}
|
|
22
|
+
@isLoading={{@isLoading}}
|
|
23
|
+
@hasVisibilityToggle={{this.showVisibilityToggle}}
|
|
24
|
+
required={{@isRequired}}
|
|
25
|
+
...attributes
|
|
26
|
+
id={{F.id}}
|
|
27
|
+
aria-describedby={{F.ariaDescribedBy}}
|
|
28
|
+
/>
|
|
29
|
+
{{#if this.showVisibilityToggle}}
|
|
30
|
+
<Hds::Form::VisibilityToggle
|
|
31
|
+
@isVisible={{this.isPasswordMasked}}
|
|
32
|
+
@ariaLabel={{this.visibilityToggleAriaLabel}}
|
|
33
|
+
@ariaMessageText={{this.visibilityToggleAriaMessageText}}
|
|
34
|
+
aria-controls={{F.id}}
|
|
35
|
+
class="hds-form-text-input__visibility-toggle"
|
|
36
|
+
{{on "click" this.onClickTogglePasswordReadability}}
|
|
37
|
+
/>
|
|
38
|
+
{{/if}}
|
|
39
|
+
</div>
|
|
28
40
|
</F.Control>
|
|
29
41
|
</Hds::Form::Field>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) HashiCorp, Inc.
|
|
3
|
+
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Component from '@glimmer/component';
|
|
7
|
+
import { tracked } from '@glimmer/tracking';
|
|
8
|
+
import { action } from '@ember/object';
|
|
9
|
+
|
|
10
|
+
export default class HdsFormTextInputFieldComponent extends Component {
|
|
11
|
+
@tracked isPasswordMasked = true;
|
|
12
|
+
@tracked hasVisibilityToggle = this.args.hasVisibilityToggle ?? true;
|
|
13
|
+
@tracked type = this.args.type ?? 'text';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param showVisibilityToggle
|
|
17
|
+
* @type {boolean}
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
get showVisibilityToggle() {
|
|
21
|
+
return this.args.type === 'password' && this.hasVisibilityToggle;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param visibilityToggleAriaLabel
|
|
26
|
+
* @type {string}
|
|
27
|
+
* @default 'Show password'
|
|
28
|
+
*/
|
|
29
|
+
get visibilityToggleAriaLabel() {
|
|
30
|
+
if (this.args.visibilityToggleAriaLabel) {
|
|
31
|
+
return this.args.visibilityToggleAriaLabel;
|
|
32
|
+
} else if (this.isPasswordMasked) {
|
|
33
|
+
return 'Show password';
|
|
34
|
+
} else {
|
|
35
|
+
return 'Hide password';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param visibilityToggleAriaMessageText
|
|
41
|
+
* @type {string}
|
|
42
|
+
* @default 'Password is now hidden'
|
|
43
|
+
*/
|
|
44
|
+
get visibilityToggleAriaMessageText() {
|
|
45
|
+
if (this.args.visibilityToggleAriaMessageText) {
|
|
46
|
+
return this.args.visibilityToggleAriaMessageText;
|
|
47
|
+
} else if (this.isPasswordMasked) {
|
|
48
|
+
return 'Password is hidden';
|
|
49
|
+
} else {
|
|
50
|
+
return 'Password is visible';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@action
|
|
55
|
+
onClickTogglePasswordReadability() {
|
|
56
|
+
this.isPasswordMasked = !this.isPasswordMasked;
|
|
57
|
+
this.type = this.isPasswordMasked ? 'password' : 'text';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{{!
|
|
2
|
+
Copyright (c) HashiCorp, Inc.
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
}}
|
|
5
|
+
<button class="hds-form-visibility-toggle" type="button" aria-label={{@ariaLabel}} ...attributes>
|
|
6
|
+
<FlightIcon @name={{if @isVisible "eye" "eye-off"}} @size="16" @isInlineBlock={{false}} />
|
|
7
|
+
<span class="sr-only" aria-live="polite">{{@ariaMessageText}}</span>
|
|
8
|
+
</button>
|
|
@@ -6,7 +6,25 @@
|
|
|
6
6
|
import Component from '@glimmer/component';
|
|
7
7
|
import { action } from '@ember/object';
|
|
8
8
|
|
|
9
|
-
export
|
|
9
|
+
export interface HdsInteractiveSignature {
|
|
10
|
+
Args: {
|
|
11
|
+
href?: string;
|
|
12
|
+
isHrefExternal?: boolean;
|
|
13
|
+
isRouteExternal?: boolean;
|
|
14
|
+
route?: unknown;
|
|
15
|
+
models?: unknown;
|
|
16
|
+
model?: unknown;
|
|
17
|
+
query?: unknown;
|
|
18
|
+
'current-when'?: unknown;
|
|
19
|
+
replace?: unknown;
|
|
20
|
+
};
|
|
21
|
+
Blocks: {
|
|
22
|
+
default: [];
|
|
23
|
+
};
|
|
24
|
+
Element: HTMLAnchorElement | HTMLButtonElement;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default class HdsInteractiveIndexComponent extends Component<HdsInteractiveSignature> {
|
|
10
28
|
/**
|
|
11
29
|
* Determines if a @href value is "external" (it adds target="_blank" rel="noopener noreferrer")
|
|
12
30
|
*
|
|
@@ -30,9 +48,16 @@ export default class HdsInteractiveComponent extends Component {
|
|
|
30
48
|
}
|
|
31
49
|
|
|
32
50
|
@action
|
|
33
|
-
onKeyUp(event) {
|
|
51
|
+
onKeyUp(event: KeyboardEvent) {
|
|
34
52
|
if (event.key === ' ' || event.code === 'Space') {
|
|
35
|
-
event.target.click();
|
|
53
|
+
(event.target as HTMLElement).click();
|
|
36
54
|
}
|
|
37
55
|
}
|
|
38
56
|
}
|
|
57
|
+
|
|
58
|
+
declare module '@glint/environment-ember-loose/registry' {
|
|
59
|
+
export default interface Registry {
|
|
60
|
+
'Hds::Interactive': typeof HdsInteractiveIndexComponent;
|
|
61
|
+
'hds/interactive': typeof HdsInteractiveIndexComponent;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
{{#if @icon}}
|
|
7
7
|
<FlightIcon class="hds-modal__icon" @name={{@icon}} @size="24" @isInlineBlock={{false}} />
|
|
8
8
|
{{/if}}
|
|
9
|
-
<
|
|
9
|
+
<Hds::Text::Display class="hds-modal__title" @tag="div" @size="300" @weight="semibold" id={{@id}}>
|
|
10
10
|
{{#if @tagline}}
|
|
11
|
-
<
|
|
11
|
+
<Hds::Text::Body class="hds-modal__tagline" @tag="div" @size="100" @weight="regular">
|
|
12
12
|
{{@tagline}}
|
|
13
|
-
</
|
|
13
|
+
</Hds::Text::Body>
|
|
14
14
|
{{/if}}
|
|
15
15
|
{{yield}}
|
|
16
|
-
</
|
|
16
|
+
</Hds::Text::Display>
|
|
17
17
|
<Hds::DismissButton class="hds-modal__dismiss" {{on "click" @onDismiss}} />
|
|
18
18
|
</div>
|
|
@@ -2,4 +2,10 @@
|
|
|
2
2
|
Copyright (c) HashiCorp, Inc.
|
|
3
3
|
SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
}}
|
|
5
|
-
<
|
|
5
|
+
<Hds::Text::Body
|
|
6
|
+
class="hds-page-header__description"
|
|
7
|
+
@tag="p"
|
|
8
|
+
@size="200"
|
|
9
|
+
@color="primary"
|
|
10
|
+
...attributes
|
|
11
|
+
>{{yield}}</Hds::Text::Body>
|
|
@@ -2,4 +2,10 @@
|
|
|
2
2
|
Copyright (c) HashiCorp, Inc.
|
|
3
3
|
SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
}}
|
|
5
|
-
<
|
|
5
|
+
<Hds::Text::Body
|
|
6
|
+
class="hds-page-header__subtitle"
|
|
7
|
+
@tag="p"
|
|
8
|
+
@size="200"
|
|
9
|
+
@color="faint"
|
|
10
|
+
...attributes
|
|
11
|
+
>{{yield}}</Hds::Text::Body>
|
|
@@ -2,4 +2,10 @@
|
|
|
2
2
|
Copyright (c) HashiCorp, Inc.
|
|
3
3
|
SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
}}
|
|
5
|
-
<
|
|
5
|
+
<Hds::Text::Display
|
|
6
|
+
class="hds-page-header__title"
|
|
7
|
+
@tag="h1"
|
|
8
|
+
@size="500"
|
|
9
|
+
@color="strong"
|
|
10
|
+
...attributes
|
|
11
|
+
>{{yield}}</Hds::Text::Display>
|
|
@@ -7,15 +7,12 @@ import Component from '@glimmer/component';
|
|
|
7
7
|
import { tracked } from '@glimmer/tracking';
|
|
8
8
|
import { action } from '@ember/object';
|
|
9
9
|
import { assert } from '@ember/debug';
|
|
10
|
-
import { inject as service } from '@ember/service';
|
|
11
10
|
|
|
12
11
|
// for context about the decision to use these values, see:
|
|
13
12
|
// https://hashicorp.slack.com/archives/C03A0N1QK8S/p1673546329082759
|
|
14
13
|
export const DEFAULT_PAGE_SIZES = [10, 30, 50];
|
|
15
14
|
|
|
16
15
|
export default class HdsPaginationCompactIndexComponent extends Component {
|
|
17
|
-
@service router;
|
|
18
|
-
|
|
19
16
|
// This private variable is used to differentiate between
|
|
20
17
|
// "uncontrolled" component (where the state is handled internally) and
|
|
21
18
|
// "controlled" component (where the state is handled externally, by the consumer's code).
|
|
@@ -109,10 +106,6 @@ export default class HdsPaginationCompactIndexComponent extends Component {
|
|
|
109
106
|
return pageSizes;
|
|
110
107
|
}
|
|
111
108
|
|
|
112
|
-
get routeQueryParams() {
|
|
113
|
-
return this.router.currentRoute?.queryParams || {};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
109
|
buildQueryParamsObject(page, pageSize) {
|
|
117
110
|
if (this.isControlled) {
|
|
118
111
|
return this.args.queryFunction(page, pageSize);
|
|
@@ -162,15 +155,6 @@ export default class HdsPaginationCompactIndexComponent extends Component {
|
|
|
162
155
|
onPageSizeChange(newPageSize) {
|
|
163
156
|
let { onPageSizeChange } = this.args;
|
|
164
157
|
|
|
165
|
-
// we need to manually update the query parameters in the route (it's not a link!)
|
|
166
|
-
if (this.isControlled) {
|
|
167
|
-
// we pass `null` as value for the `page` argument, so consumers can handle this condition accordingly (probably will just change the side of the data/array slice)
|
|
168
|
-
const queryParams = this.buildQueryParamsObject(null, newPageSize);
|
|
169
|
-
this.router.transitionTo({ queryParams });
|
|
170
|
-
} else {
|
|
171
|
-
this.currentPageSize = newPageSize;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
158
|
// invoke the callback function
|
|
175
159
|
if (typeof onPageSizeChange === 'function') {
|
|
176
160
|
onPageSizeChange(newPageSize);
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Copyright (c) HashiCorp, Inc.
|
|
3
3
|
SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
}}
|
|
5
|
-
<
|
|
5
|
+
<Hds::Text::Body class="hds-pagination-info" @tag="div" @size="100" @weight="medium" ...attributes>
|
|
6
6
|
{{@itemsRangeStart}}–{{@itemsRangeEnd}}
|
|
7
7
|
{{#if this.showTotalItems}}
|
|
8
8
|
of
|
|
9
9
|
{{@totalItems}}
|
|
10
10
|
{{/if}}
|
|
11
|
-
</
|
|
11
|
+
</Hds::Text::Body>
|
|
@@ -6,9 +6,15 @@
|
|
|
6
6
|
<Hds::Interactive class={{this.classNames}} aria-label={{this.content.ariaLabel}} disabled={{true}} ...attributes>
|
|
7
7
|
<FlightIcon @name={{this.content.icon}} />
|
|
8
8
|
{{#if this.showLabel}}
|
|
9
|
-
<
|
|
9
|
+
<Hds::Text::Body
|
|
10
|
+
class="hds-pagination-nav__arrow-label"
|
|
11
|
+
@tag="span"
|
|
12
|
+
@size="100"
|
|
13
|
+
@weight="medium"
|
|
14
|
+
aria-hidden="true"
|
|
15
|
+
>
|
|
10
16
|
{{this.content.label}}
|
|
11
|
-
</
|
|
17
|
+
</Hds::Text::Body>
|
|
12
18
|
{{/if}}
|
|
13
19
|
</Hds::Interactive>
|
|
14
20
|
{{else}}
|
|
@@ -24,9 +30,15 @@
|
|
|
24
30
|
>
|
|
25
31
|
<FlightIcon @name={{this.content.icon}} />
|
|
26
32
|
{{#if this.showLabel}}
|
|
27
|
-
<
|
|
33
|
+
<Hds::Text::Body
|
|
34
|
+
class="hds-pagination-nav__arrow-label"
|
|
35
|
+
@tag="span"
|
|
36
|
+
@size="100"
|
|
37
|
+
@weight="medium"
|
|
38
|
+
aria-hidden="true"
|
|
39
|
+
>
|
|
28
40
|
{{this.content.label}}
|
|
29
|
-
</
|
|
41
|
+
</Hds::Text::Body>
|
|
30
42
|
{{/if}}
|
|
31
43
|
</Hds::Interactive>
|
|
32
44
|
{{/if}}
|
|
@@ -61,8 +61,6 @@ export default class HdsPaginationControlArrowComponent extends Component {
|
|
|
61
61
|
'hds-pagination-nav__control',
|
|
62
62
|
'hds-pagination-nav__arrow',
|
|
63
63
|
`hds-pagination-nav__arrow--direction-${this.args.direction}`,
|
|
64
|
-
'hds-typography-body-100',
|
|
65
|
-
'hds-font-weight-medium',
|
|
66
64
|
];
|
|
67
65
|
|
|
68
66
|
return classes.join(' ');
|
|
@@ -12,5 +12,6 @@
|
|
|
12
12
|
...attributes
|
|
13
13
|
aria-current={{if @isSelected "page" null}}
|
|
14
14
|
>
|
|
15
|
-
<span class="sr-only">page
|
|
15
|
+
<Hds::Text::Body @tag="span" @size="100" @weight="medium"><span class="sr-only">page
|
|
16
|
+
</span>{{this.page}}</Hds::Text::Body>
|
|
16
17
|
</Hds::Interactive>
|
|
@@ -25,12 +25,7 @@ export default class HdsPaginationControlNumberComponent extends Component {
|
|
|
25
25
|
* @return {string} The "class" attribute to apply to the component.
|
|
26
26
|
*/
|
|
27
27
|
get classNames() {
|
|
28
|
-
let classes = [
|
|
29
|
-
'hds-pagination-nav__control',
|
|
30
|
-
'hds-pagination-nav__number',
|
|
31
|
-
'hds-typography-body-100',
|
|
32
|
-
'hds-font-weight-medium"',
|
|
33
|
-
];
|
|
28
|
+
let classes = ['hds-pagination-nav__control', 'hds-pagination-nav__number'];
|
|
34
29
|
|
|
35
30
|
if (this.args.isSelected) {
|
|
36
31
|
classes.push(`hds-pagination-nav__number--is-selected`);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
}}
|
|
5
5
|
|
|
6
6
|
<nav class="hds-side-nav__list-wrapper" ...attributes>
|
|
7
|
-
{{yield (hash
|
|
7
|
+
{{yield (hash ExtraBefore=(component "hds/yield"))}}
|
|
8
8
|
<ul class="hds-side-nav__list" role="list">
|
|
9
9
|
{{yield
|
|
10
10
|
(hash
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
)
|
|
16
16
|
}}
|
|
17
17
|
</ul>
|
|
18
|
-
{{yield (hash
|
|
18
|
+
{{yield (hash ExtraAfter=(component "hds/yield"))}}
|
|
19
19
|
</nav>
|
|
@@ -17,7 +17,12 @@
|
|
|
17
17
|
{{else if (eq @status "complete")}}
|
|
18
18
|
<FlightIcon class="hds-stepper-indicator-step__icon" @name="check" @size="16" />
|
|
19
19
|
{{else}}
|
|
20
|
-
<
|
|
20
|
+
<Hds::Text::Body
|
|
21
|
+
class="hds-stepper-indicator-step__text"
|
|
22
|
+
@tag="span"
|
|
23
|
+
@size="100"
|
|
24
|
+
@weight="medium"
|
|
25
|
+
>{{@text}}</Hds::Text::Body>
|
|
21
26
|
{{/if}}
|
|
22
27
|
</div>
|
|
23
28
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Copyright (c) HashiCorp, Inc.
|
|
3
3
|
SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
}}
|
|
5
|
-
<
|
|
5
|
+
<Hds::Text::Body class={{this.classNames}} @tag="span" @size="100" @weight="medium" @color="primary" ...attributes>
|
|
6
6
|
{{#if this.onDismiss}}
|
|
7
7
|
<button class="hds-tag__dismiss" type="button" aria-label={{this.ariaLabel}} {{on "click" this.onDismiss}}>
|
|
8
8
|
<FlightIcon class="hds-tag__dismiss-icon" @name="x" @size="16" @isInlineBlock={{false}} />
|
|
@@ -27,4 +27,4 @@
|
|
|
27
27
|
{{this.text}}
|
|
28
28
|
</span>
|
|
29
29
|
{{/if}}
|
|
30
|
-
</
|
|
30
|
+
</Hds::Text::Body>
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) HashiCorp, Inc.
|
|
3
|
+
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { modifier } from 'ember-modifier';
|
|
7
|
+
import { assert, warn } from '@ember/debug';
|
|
8
|
+
|
|
9
|
+
export const getTextToCopy = (text) => {
|
|
10
|
+
let textToCopy;
|
|
11
|
+
|
|
12
|
+
if (text) {
|
|
13
|
+
if (typeof text === 'string') {
|
|
14
|
+
textToCopy = text;
|
|
15
|
+
} else if (
|
|
16
|
+
// context: https://github.com/hashicorp/design-system/pull/1564
|
|
17
|
+
typeof text === 'number' ||
|
|
18
|
+
typeof text === 'bigint'
|
|
19
|
+
) {
|
|
20
|
+
textToCopy = text.toString();
|
|
21
|
+
} else {
|
|
22
|
+
assert(
|
|
23
|
+
`\`hds-clipboard\` modifier - \`text\` argument must be a string - provided: ${typeof text}`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return textToCopy;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getTargetElement = (target) => {
|
|
31
|
+
let targetElement;
|
|
32
|
+
if (typeof target === 'string') {
|
|
33
|
+
targetElement = document.querySelector(target);
|
|
34
|
+
|
|
35
|
+
if (!targetElement) {
|
|
36
|
+
console.error(
|
|
37
|
+
'`hds-clipboard` modifier - `target` selector provided does not point to an existing DOM node, check your selector string',
|
|
38
|
+
targetElement
|
|
39
|
+
);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
} else if (target instanceof Node && target.nodeType === Node.ELEMENT_NODE) {
|
|
43
|
+
targetElement = target;
|
|
44
|
+
} else {
|
|
45
|
+
if (target instanceof NodeList) {
|
|
46
|
+
assert(
|
|
47
|
+
'`hds-clipboard` modifier - `target` argument must be a string or a DOM node - provided: a list of DOM nodes'
|
|
48
|
+
);
|
|
49
|
+
} else {
|
|
50
|
+
assert(
|
|
51
|
+
`\`hds-clipboard\` modifier - \`target\` argument must be a string or a DOM node - provided: ${typeof target}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return targetElement;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getTextToCopyFromTargetElement = (targetElement) => {
|
|
59
|
+
let textToCopy;
|
|
60
|
+
if (
|
|
61
|
+
targetElement instanceof Node &&
|
|
62
|
+
targetElement.nodeType === Node.ELEMENT_NODE
|
|
63
|
+
) {
|
|
64
|
+
if (
|
|
65
|
+
targetElement instanceof HTMLInputElement || // targetElement.nodeName === 'INPUT' ||
|
|
66
|
+
targetElement instanceof HTMLTextAreaElement || // targetElement.nodeName === 'TEXTAREA' ||
|
|
67
|
+
targetElement instanceof HTMLSelectElement // targetElement.nodeName === 'SELECT'
|
|
68
|
+
) {
|
|
69
|
+
textToCopy = targetElement.value;
|
|
70
|
+
} else {
|
|
71
|
+
// simplest approach
|
|
72
|
+
textToCopy = targetElement.innerText;
|
|
73
|
+
|
|
74
|
+
// approach based on text selection (left for backup just in case)
|
|
75
|
+
// var selection = window.getSelection();
|
|
76
|
+
// var range = document.createRange();
|
|
77
|
+
// selection.removeAllRanges();
|
|
78
|
+
// range.selectNodeContents(targetElement);
|
|
79
|
+
// selection.addRange(range);
|
|
80
|
+
// textToCopy = selection.toString();
|
|
81
|
+
// selection.removeAllRanges();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return textToCopy;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const writeTextToClipboard = async (textToCopy) => {
|
|
88
|
+
// finally copy the text to the clipboard using the Clipboard API
|
|
89
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API
|
|
90
|
+
if (textToCopy) {
|
|
91
|
+
try {
|
|
92
|
+
// notice: the "clipboard-write" permission is granted automatically to pages when they are in the active tab
|
|
93
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write
|
|
94
|
+
await navigator.clipboard.writeText(textToCopy);
|
|
95
|
+
// DEBUG uncomment this for easy debugging
|
|
96
|
+
// console.log('success', textToCopy);
|
|
97
|
+
return true;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
// clipboard write failed
|
|
100
|
+
// this probably never happens (see comment above) or happens only for very old browsers that don't for which `navigator.clipboard` is undefined
|
|
101
|
+
warn('copy action failed, please check your browser‘s permissions', {
|
|
102
|
+
id: 'hds-clipboard.write-text-to-clipboard.catch-error',
|
|
103
|
+
});
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const copyToClipboard = async (text, target) => {
|
|
112
|
+
let textToCopy;
|
|
113
|
+
|
|
114
|
+
if (text) {
|
|
115
|
+
textToCopy = getTextToCopy(text);
|
|
116
|
+
} else if (target) {
|
|
117
|
+
const targetElement = getTargetElement(target);
|
|
118
|
+
textToCopy = getTextToCopyFromTargetElement(targetElement);
|
|
119
|
+
} else {
|
|
120
|
+
assert(
|
|
121
|
+
'`hds-clipboard` modifier - either a `text` or a `target` argument is required'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const success = await writeTextToClipboard(textToCopy);
|
|
125
|
+
return success;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Notice: we use a function-based modifier here instead of a class-based one
|
|
129
|
+
// because it's quite simple in its logic, and doesn't require injecting services
|
|
130
|
+
// see: https://github.com/ember-modifier/ember-modifier#function-based-modifiers
|
|
131
|
+
|
|
132
|
+
export default modifier((element, positional, named) => {
|
|
133
|
+
assert(
|
|
134
|
+
'`hds-clipboard` modifier - the modifier must be applied to an element',
|
|
135
|
+
element
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const { text, target, onSuccess, onError } = named;
|
|
139
|
+
|
|
140
|
+
const onClick = async (event) => {
|
|
141
|
+
const trigger = event.currentTarget;
|
|
142
|
+
const success = await copyToClipboard(text, target);
|
|
143
|
+
|
|
144
|
+
// fire the `onSuccess/onError` callbacks (if provided)
|
|
145
|
+
if (success) {
|
|
146
|
+
if (typeof onSuccess === 'function') {
|
|
147
|
+
onSuccess({ trigger, text, target });
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
if (typeof onError === 'function') {
|
|
151
|
+
onError({ trigger, text, target });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// add the "onClick" event listener to the element
|
|
157
|
+
element.addEventListener('click', onClick);
|
|
158
|
+
|
|
159
|
+
// this (teardown) function is run when the element is removed
|
|
160
|
+
return () => {
|
|
161
|
+
element.removeEventListener('click', onClick);
|
|
162
|
+
};
|
|
163
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) HashiCorp, Inc.
|
|
3
|
+
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type HdsButtonIndexComponent from '@hashicorp/design-system-components/components/hds/button';
|
|
7
|
+
import type HdsInteractiveIndexComponent from '@hashicorp/design-system-components/components/hds/interactive';
|
|
8
|
+
|
|
9
|
+
export default interface HdsComponentsRegistry {
|
|
10
|
+
HdsButtonComponent: typeof HdsButtonIndexComponent;
|
|
11
|
+
HdsInteractiveComponent: typeof HdsInteractiveIndexComponent;
|
|
12
|
+
}
|