@tylertech/forge 3.14.0-dev.4 → 3.14.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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+ .forge-secret{--_secret-blur:var(--forge-secret-blur, var(--forge-spacing-xsmall, 8px));--_secret-button-background:var(--forge-secret-button-background, var(--forge-theme-surface-inverse, #333333));--_secret-button-color:var(--forge-secret-button-color, var(--forge-theme-on-surface-inverse, #ffffff));--_secret-button-shape:var(--forge-secret-button-shape, calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1)));--_secret-button-padding:var(--forge-secret-button-padding, var(--forge-spacing-xsmall, 8px));--_secret-icon-size:var(--forge-secret-icon-size, calc(var(--forge-typography-font-size, 1rem) * 1));--_secret-text-button-shape:var(--forge-secret-text-button-shape, calc(var(--forge-shape-full, 9999px) * var(--forge-shape-factor, 1)));--_secret-text-decoration-line:var(--forge-secret-text-decoration-line, underline);--_secret-text-decoration-style:var(--forge-secret-text-decoration-style, dotted);--_secret-text-decoration-color:var(--forge-secret-text-decoration-color, inherit);--_secret-text-underline-offset:var(--forge-secret-text-underline-offset, var(--forge-spacing-xxxsmall, 2px));--_secret-transition-duration:var(--forge-secret-transition-duration, var(--forge-animation-duration-short4, 200ms));--_secret-transition-easing:var(--forge-secret-transition-easing, var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1)));display:inline-block;vertical-align:bottom;display:flex;max-inline-size:fit-content}.forge-secret__content{display:inline-block;position:relative;transition:filter var(--_secret-transition-duration) var(--_secret-transition-easing)}.forge-secret__button{--forge-icon-button-background-color: var(--_secret-button-background);--forge-icon-button-icon-color: var(--_secret-button-color);--forge-icon-button-padding: 0;--forge-icon-button-shape: var(--_secret-button-shape);--forge-icon-button-size: 1lh;--forge-focus-indicator-outward-offset: 2px;block-size:0;padding-inline:var(--_secret-button-padding)}.forge-secret__text-button{--_button-dense-height:var(--forge-button-dense-height, 24px);--forge-button-background: var(--_secret-button-background);--forge-button-color: var(--_secret-button-color);--forge-button-shape: var(--_secret-text-button-shape);--forge-button-height: var(--_button-dense-height);--forge-focus-indicator-shape: var(--_secret-text-button-shape);padding:var(--_secret-button-padding);position:absolute;place-self:center;inset:0;translate:0;transition-property:inset-block-start,translate;transition-duration:var(--_secret-transition-duration);transition-timing-function:var(--_secret-transition-easing)}.forge-secret__icon{--forge-icon-font-size: var(--_secret-icon-size)}.forge-secret--show-on-hover:where(:hover,:has(:where(.forge-secret__button,.forge-secret__text-button):focus-visible)) .forge-secret__content{filter:none !important}.forge-secret--show-on-hover:where(:hover,:has(:where(.forge-secret__button,.forge-secret__text-button):focus-visible)) .forge-secret__content::before{display:none}.forge-secret--show-on-hover:where(:hover,:has(:where(.forge-secret__button,.forge-secret__text-button):focus-visible)).forge-secret--masked:not(.forge-secret--block) .forge-secret__content{display:inline-block !important}.forge-secret--show-on-hover:where(:hover,:has(:where(.forge-secret__button,.forge-secret__text-button):focus-visible)).forge-secret--masked:not(.forge-secret--block) .forge-secret__masked::before{display:none}.forge-secret--show-on-hover:where(:hover,:has(:where(.forge-secret__button,.forge-secret__text-button):focus-visible)) .forge-secret__text-button{inset-block-start:-100%;translate:0 -50%}.forge-secret--block{display:block;vertical-align:initial;display:block;position:relative}.forge-secret--block .forge-secret__content{display:block}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button):not([aria-expanded=true])){cursor:pointer;cursor:unset}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button):not([aria-expanded=true])) .forge-secret__content{interactivity:inert;user-select:none}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button):not([aria-expanded=true])):where(.forge-secret--blur,.forge-secret--block.forge-secret--masked) .forge-secret__content{filter:blur(var(--_secret-blur))}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button):not([aria-expanded=true])).forge-secret--masked:not(.forge-secret--block) .forge-secret__content{display:none}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button):not([aria-expanded=true])).forge-secret--masked:not(.forge-secret--block) .forge-secret__masked::before{content:attr(data-mask)}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button):not([aria-expanded=true])).forge-secret--noise .forge-secret__content::before{content:"";position:absolute;inset:0;width:100%;height:100%;background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600"><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="8" stitchTiles="stitch" /><feColorMatrix type="matrix" values="0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0 0 0 1 0" /></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity=".5" /></svg>');background-size:600px 600px;background-repeat:repeat;backdrop-filter:blur(24px)}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button)[aria-expanded=true]) .forge-secret__text-button{inset-block-start:-100%;translate:0 -50%}.forge-secret:has(:where(.forge-secret__button,.forge-secret__text-button)[aria-expanded=true]):not(.forge-secret--block) .forge-secret__content{text-decoration-line:var(--_secret-text-decoration-line);text-decoration-style:var(--_secret-text-decoration-style);text-decoration-color:var(--_secret-text-decoration-color);text-underline-offset:var(--_secret-text-underline-offset)}@media(prefers-reduced-motion: reduce){.forge-secret{--_secret-transition-duration:var(--forge-secret-transition-duration, 0)}}
@@ -68,9 +68,15 @@ let ButtonAreaComponent = class ButtonAreaComponent extends BaseLitElement {
68
68
  this.#stateLayer = createRef();
69
69
  this.#focusIndicator = createRef();
70
70
  this.#handleClick = (event) => {
71
+ // Ignore the click if it originates from an ignored element
72
+ if (this.#shouldIgnoreEvent(event)) {
73
+ event.stopPropagation();
74
+ return;
75
+ }
71
76
  // Prevent the click if disabled
72
77
  if (this.disabled) {
73
78
  event.stopPropagation();
79
+ return;
74
80
  }
75
81
  // Prevent the click if a selection was made
76
82
  const selection = window.getSelection();
@@ -78,14 +84,19 @@ let ButtonAreaComponent = class ButtonAreaComponent extends BaseLitElement {
78
84
  event.stopPropagation();
79
85
  return;
80
86
  }
81
- // Prevent the click if it originates from an ignored element
82
- if (this.#shouldIgnoreEvent(event)) {
83
- event.stopPropagation();
87
+ // If the click occured outside an external associated element, trigger a click on it
88
+ // The event doesn't bubble so the host won't receive duplicate clicks
89
+ const associatedElement = this.#associatedElement;
90
+ if (!associatedElement) {
84
91
  return;
85
92
  }
86
- // If the click occured outside an external associated element, trigger a click on it
87
- if (this.targetElement && !event.composedPath().includes(this.targetElement)) {
88
- this.targetElement.click();
93
+ const clickOccurredInAssociatedElement = event.composedPath().includes(associatedElement);
94
+ if (!clickOccurredInAssociatedElement) {
95
+ associatedElement.dispatchEvent(new PointerEvent('click', {
96
+ bubbles: false,
97
+ cancelable: true,
98
+ composed: true
99
+ }));
89
100
  }
90
101
  };
91
102
  this.#handleKeydown = (event) => {
package/esm/index.d.ts CHANGED
@@ -6,9 +6,9 @@ export * from './backdrop/index.js';
6
6
  export * from './badge/index.js';
7
7
  export * from './banner/index.js';
8
8
  export * from './bottom-sheet/index.js';
9
- export * from './button/index.js';
10
9
  export * from './button-area/index.js';
11
10
  export * from './button-toggle/index.js';
11
+ export * from './button/index.js';
12
12
  export * from './calendar/index.js';
13
13
  export * from './card/index.js';
14
14
  export * from './checkbox/index.js';
@@ -28,13 +28,13 @@ export * from './field/index.js';
28
28
  export * from './file-picker/index.js';
29
29
  export * from './floating-action-button/index.js';
30
30
  export * from './focus-indicator/index.js';
31
- export * from './icon/index.js';
32
31
  export * from './icon-button/index.js';
32
+ export * from './icon/index.js';
33
33
  export * from './inline-message/index.js';
34
34
  export * from './key/index.js';
35
35
  export * from './keyboard-shortcut/index.js';
36
- export * from './label/index.js';
37
36
  export * from './label-value/index.js';
37
+ export * from './label/index.js';
38
38
  export * from './linear-progress/index.js';
39
39
  export * from './list/index.js';
40
40
  export * from './menu/index.js';
@@ -46,6 +46,7 @@ export * from './paginator/index.js';
46
46
  export * from './popover/index.js';
47
47
  export * from './radio/index.js';
48
48
  export * from './scaffold/index.js';
49
+ export * from './secret/index.js';
49
50
  export * from './select/index.js';
50
51
  export * from './skeleton/index.js';
51
52
  export * from './skip-link/index.js';
package/esm/index.js CHANGED
@@ -12,9 +12,9 @@ import { defineBackdropComponent } from './backdrop/index.js';
12
12
  import { defineBadgeComponent } from './badge/index.js';
13
13
  import { defineBannerComponent } from './banner/index.js';
14
14
  import { defineBottomSheetComponent } from './bottom-sheet/index.js';
15
- import { defineButtonComponent } from './button/index.js';
16
15
  import { defineButtonAreaComponent } from './button-area/index.js';
17
16
  import './button-toggle/index.js';
17
+ import { defineButtonComponent } from './button/index.js';
18
18
  import { defineCalendarComponent } from './calendar/index.js';
19
19
  import { defineCardComponent } from './card/index.js';
20
20
  import { defineCheckboxComponent } from './checkbox/index.js';
@@ -32,13 +32,13 @@ import { defineFieldComponent } from './field/index.js';
32
32
  import { defineFilePickerComponent } from './file-picker/index.js';
33
33
  import { defineFloatingActionButtonComponent } from './floating-action-button/index.js';
34
34
  import { defineFocusIndicatorComponent } from './focus-indicator/index.js';
35
- import { defineIconComponent } from './icon/index.js';
36
35
  import { defineIconButtonComponent } from './icon-button/index.js';
36
+ import { defineIconComponent } from './icon/index.js';
37
37
  import { defineInlineMessageComponent } from './inline-message/index.js';
38
38
  import './key/index.js';
39
39
  import { defineKeyboardShortcutComponent } from './keyboard-shortcut/index.js';
40
- import { defineLabelComponent } from './label/index.js';
41
40
  import { defineLabelValueComponent } from './label-value/index.js';
41
+ import { defineLabelComponent } from './label/index.js';
42
42
  import { defineLinearProgressComponent } from './linear-progress/index.js';
43
43
  import './list/index.js';
44
44
  import { defineListItemComponent } from './list/list-item/index.js';
@@ -51,6 +51,7 @@ import { definePaginatorComponent } from './paginator/index.js';
51
51
  import { definePopoverComponent } from './popover/index.js';
52
52
  import './radio/index.js';
53
53
  import { defineScaffoldComponent } from './scaffold/index.js';
54
+ import { defineSecretComponent } from './secret/index.js';
54
55
  import './select/index.js';
55
56
  import { defineSelectDropdownComponent } from './select/select-dropdown/index.js';
56
57
  import { defineSkeletonComponent } from './skeleton/index.js';
@@ -353,6 +354,7 @@ export { RadioGroupAdapter } from './radio/radio-group/radio-group-adapter.js';
353
354
  export { RadioGroupComponent } from './radio/radio-group/radio-group.js';
354
355
  export { RadioGroupCore } from './radio/radio-group/radio-group-core.js';
355
356
  export { SCAFFOLD_CONSTANTS } from './scaffold/scaffold-constants.js';
357
+ export { SECRET_TAG_NAME, SecretComponent } from './secret/secret.js';
356
358
  export { SELECT_CONSTANTS } from './select/select/select-constants.js';
357
359
  export { SELECT_DROPDOWN_CONSTANTS } from './select/select-dropdown/select-dropdown-constants.js';
358
360
  export { SKELETON_CONSTANTS } from './skeleton/skeleton-constants.js';
@@ -532,6 +534,7 @@ function defineComponents() {
532
534
  defineRadioComponent();
533
535
  defineRadioGroupComponent();
534
536
  defineScaffoldComponent();
537
+ defineSecretComponent();
535
538
  defineSelectComponent();
536
539
  defineSelectDropdownComponent();
537
540
  defineSkeletonComponent();
@@ -564,4 +567,4 @@ function defineDeprecatedComponents() {
564
567
  defineDeprecatedIconButtonComponent();
565
568
  }
566
569
 
567
- export { defineAccordionComponent, defineAppBarComponent, defineAppBarHelpButtonComponent, defineAppBarMenuButtonComponent, defineAppBarNotificationButtonComponent, defineAppBarProfileButtonComponent, defineAppBarSearchComponent, defineAutocompleteComponent, defineAvatarComponent, defineBackdropComponent, defineBadgeComponent, defineBannerComponent, defineBottomSheetComponent, defineButtonAreaComponent, defineButtonComponent, defineButtonToggleComponent, defineButtonToggleGroupComponent, defineCalendarComponent, defineCardComponent, defineCheckboxComponent, defineChipComponent, defineChipFieldComponent, defineChipSetComponent, defineCircularProgressComponent, defineColorPickerComponent, defineComponents, defineDatePickerComponent, defineDateRangePickerComponent, defineDeprecatedButtonComponent, defineDeprecatedComponents, defineDeprecatedIconButtonComponent, defineDialogComponent, defineDividerComponent, defineDrawerComponent, defineExpansionPanelComponent, defineFieldComponent, defineFilePickerComponent, defineFloatingActionButtonComponent, defineFocusIndicatorComponent, defineIconButtonComponent, defineIconComponent, defineInlineMessageComponent, defineKeyComponent, defineKeyItemComponent, defineKeyboardShortcutComponent, defineLabelComponent, defineLabelValueComponent, defineLinearProgressComponent, defineListComponent, defineListItemComponent, defineMenuComponent, defineMeterComponent, defineMeterGroupComponent, defineMiniDrawerComponent, defineModalDrawerComponent, defineOpenIconComponent, defineOptionComponent, defineOptionGroupComponent, defineOverlayComponent, definePageStateComponent, definePaginatorComponent, definePopoverComponent, defineProfileCardComponent, defineRadioComponent, defineRadioGroupComponent, defineScaffoldComponent, defineSelectComponent, defineSelectDropdownComponent, defineSkeletonComponent, defineSkipLinkComponent, defineSliderComponent, defineSplitViewComponent, defineStackComponent, defineStateLayerComponent, defineStepComponent, defineStepperComponent, defineSwitchComponent, defineTabBarComponent, defineTabComponent, defineTableComponent, defineTextFieldComponent, defineTimePickerComponent, defineToastComponent, defineToolbarComponent, defineTooltipComponent, defineTreeComponent, defineTreeItemComponent, defineViewComponent, defineViewSwitcherComponent };
570
+ export { defineAccordionComponent, defineAppBarComponent, defineAppBarHelpButtonComponent, defineAppBarMenuButtonComponent, defineAppBarNotificationButtonComponent, defineAppBarProfileButtonComponent, defineAppBarSearchComponent, defineAutocompleteComponent, defineAvatarComponent, defineBackdropComponent, defineBadgeComponent, defineBannerComponent, defineBottomSheetComponent, defineButtonAreaComponent, defineButtonComponent, defineButtonToggleComponent, defineButtonToggleGroupComponent, defineCalendarComponent, defineCardComponent, defineCheckboxComponent, defineChipComponent, defineChipFieldComponent, defineChipSetComponent, defineCircularProgressComponent, defineColorPickerComponent, defineComponents, defineDatePickerComponent, defineDateRangePickerComponent, defineDeprecatedButtonComponent, defineDeprecatedComponents, defineDeprecatedIconButtonComponent, defineDialogComponent, defineDividerComponent, defineDrawerComponent, defineExpansionPanelComponent, defineFieldComponent, defineFilePickerComponent, defineFloatingActionButtonComponent, defineFocusIndicatorComponent, defineIconButtonComponent, defineIconComponent, defineInlineMessageComponent, defineKeyComponent, defineKeyItemComponent, defineKeyboardShortcutComponent, defineLabelComponent, defineLabelValueComponent, defineLinearProgressComponent, defineListComponent, defineListItemComponent, defineMenuComponent, defineMeterComponent, defineMeterGroupComponent, defineMiniDrawerComponent, defineModalDrawerComponent, defineOpenIconComponent, defineOptionComponent, defineOptionGroupComponent, defineOverlayComponent, definePageStateComponent, definePaginatorComponent, definePopoverComponent, defineProfileCardComponent, defineRadioComponent, defineRadioGroupComponent, defineScaffoldComponent, defineSecretComponent, defineSelectComponent, defineSelectDropdownComponent, defineSkeletonComponent, defineSkipLinkComponent, defineSliderComponent, defineSplitViewComponent, defineStackComponent, defineStateLayerComponent, defineStepComponent, defineStepperComponent, defineSwitchComponent, defineTabBarComponent, defineTabComponent, defineTableComponent, defineTextFieldComponent, defineTimePickerComponent, defineToastComponent, defineToolbarComponent, defineTooltipComponent, defineTreeComponent, defineTreeItemComponent, defineViewComponent, defineViewSwitcherComponent };
@@ -0,0 +1,3 @@
1
+ export * from './secret.js';
2
+ /** @deprecated Definition functions are deprecated and replaced with side effect imports (`import '@tylertech/forge/secret'`). */
3
+ export declare function defineSecretComponent(): void;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+
7
+ import { defineCustomElement } from '@tylertech/forge-core';
8
+ import { SecretComponent } from './secret.js';
9
+ export { SECRET_TAG_NAME } from './secret.js';
10
+
11
+ /** @deprecated Definition functions are deprecated and replaced with side effect imports (`import '@tylertech/forge/secret'`). */
12
+ function defineSecretComponent() {
13
+ defineCustomElement(SecretComponent);
14
+ }
15
+
16
+ export { SecretComponent, defineSecretComponent };
@@ -0,0 +1,138 @@
1
+ import { CUSTOM_ELEMENT_DEPENDENCIES_PROPERTY, CUSTOM_ELEMENT_NAME_PROPERTY } from '@tylertech/forge-core';
2
+ import { PropertyValues, TemplateResult } from 'lit';
3
+ import { BaseLitElement } from '../core/base/base-lit-element.js';
4
+ import { IconButtonComponent } from '../icon-button/index.js';
5
+ import { IconComponent } from '../icon/index.js';
6
+ import { TooltipComponent } from '../tooltip/index.js';
7
+ import { ButtonComponent } from '../button/index.js';
8
+ import '../button/button.js';
9
+ import '../icon-button/icon-button.js';
10
+ import '../tooltip/tooltip.js';
11
+ export type SecretVariant = 'blur' | 'masked' | 'noise';
12
+ export type SecretButtonPosition = 'start' | 'end';
13
+ export declare const SECRET_TAG_NAME: keyof HTMLElementTagNameMap;
14
+ /**
15
+ * @tag forge-secret
16
+ *
17
+ * @summary A component that conceals content with a blur or dot mask, revealing it on user interaction.
18
+ *
19
+ * @description
20
+ * The secret component blurs or masks content and provides a connected button to reveal it.
21
+ * Content is revealed on click/keyboard interaction, with optional hover reveal. Multiple instances
22
+ * sharing a name act as a radio group where only one can be revealed at a time.
23
+ *
24
+ * @cssproperty --forge-secret-blur - The radius of the blur effect.
25
+ * @cssproperty --forge-secret-button-background - The background color of the button.
26
+ * @cssproperty --forge-secret-button-color - The text and icon color of the button.
27
+ * @cssproperty --forge-secret-button-shape - The button's border radius.
28
+ * @cssproperty --forge-secret-button-padding - The inline padding around the button or inline and block padding around the text button.
29
+ * @cssproperty --forge-secret-icon-size - The icon's size.
30
+ * @cssproperty --forge-secret-text-button-shape - The text button's border radius.
31
+ * @cssproperty --forge-secret-text-decoration-line - The decoration line applied to open inline content.
32
+ * @cssproperty --forge-secret-text-decoration-style - The style of the text decoration line.
33
+ * @cssproperty --forge-secret-text-decoration-color - The color of the text decoration line.
34
+ * @cssproperty --forge-secret-text-underline-offset - The spacing between the inline content and the text decoration line.
35
+ * @cssproperty --forge-secret-transition-duration - The duration of transitions.
36
+ * @cssproperty --forge-secret-transition-easing - The timing function of transitions.
37
+ *
38
+ * @slot - Default slot for the secret content.
39
+ * @slot close-icon - An icon that is shown when the secret is open, indicating it can be closed.
40
+ * @slot open-icon - An icon that is shown when the secret is closed, indicating it can be opened.
41
+ * @slot label - Text content that appears in the tooltip attached to the button or as the button text when set to block.
42
+ *
43
+ * @state open - Indicates that the content is visible.
44
+ * @state block - Indicates that the secret is displayed as a block element instead of inline.
45
+ *
46
+ * @csspart root - The root container.
47
+ * @csspart content - The content container.
48
+ * @csspart button - The toggle button element.
49
+ * @csspart text-button - The toggle button element when the secret is set to block.
50
+ *
51
+ * @cssclass forge-secret - The secret component container (required).
52
+ * @cssclass forge-secret--blur - Applies a blur effect to conceal content when the button has `aria-expanded="false"`.
53
+ * @cssclass forge-secret--masked - Applies a dot mask to conceal content when the button has `aria-expanded="false"`. Not applicable when the secret is set to block. In that case the blur variant is used instead.
54
+ * @cssclass forge-secret--noise - Applies a noise effect to conceal content when the button has `aria-expanded="false"`.
55
+ * @cssclass forge-secret--block - Displays the secret as a block element.
56
+ * @cssclass forge-secret--show-on-hover - Reveals the secret content when hovering over the component or focusing the button.
57
+ * @cssclass forge-secret__content - The content to be concealed or revealed.
58
+ * @cssclass forge-secret__button - The icon button used to toggle an inline secret.
59
+ * @cssclass forge-secret__text-button - The text button used to toggle a block secret.
60
+ * @cssclass forge-secret__masked - The element replacing concealed content when the masked variant is used. The `data-mask` attribute on this element sets its content.
61
+ *
62
+ * @fires {ToggleEvent} toggle - Dispatched when the secret opens or closes.
63
+ */
64
+ export declare class SecretComponent extends BaseLitElement {
65
+ #private;
66
+ static styles: import("lit").CSSResult;
67
+ /** @deprecated Used for compatibility with legacy Forge @customElement decorator. */
68
+ static [CUSTOM_ELEMENT_NAME_PROPERTY]: keyof HTMLElementTagNameMap;
69
+ /** @deprecated Used for compatibility with legacy Forge @customElement decorator. */
70
+ static [CUSTOM_ELEMENT_DEPENDENCIES_PROPERTY]: (typeof IconComponent | typeof IconButtonComponent | typeof TooltipComponent | typeof ButtonComponent)[];
71
+ /**
72
+ * Whether the secret content is visible.
73
+ * @attribute
74
+ * @default false
75
+ */
76
+ open: boolean;
77
+ /**
78
+ * The style applied to hidden content when the secret is set to inline. Possible values are blur, masked, and noise.
79
+ * @attribute
80
+ * @default 'blur'
81
+ */
82
+ variant: SecretVariant;
83
+ /**
84
+ * The mask pattern to use with the masked variant. When empty the slotted text content is used as the mask.
85
+ * @attribute
86
+ * @default ''
87
+ */
88
+ mask: string;
89
+ /**
90
+ * The character to replace characters with in the masked variant.
91
+ * @attribute mask-character
92
+ * @default '●'
93
+ */
94
+ maskCharacter: string;
95
+ /**
96
+ * Characters that will not be replaced by the mask character in the masked variant.
97
+ * @attribute unmasked-characters
98
+ * @default ''
99
+ */
100
+ unmaskedCharacters: string;
101
+ /**
102
+ * Whether the secret content should be displayed as a block element instead of inline with text.
103
+ * @attribute
104
+ * @default false
105
+ */
106
+ block: boolean;
107
+ /**
108
+ * The position of the button when the secret is set to inline. Possible values are start and end.
109
+ * @attribute button-position
110
+ * @default 'end'
111
+ */
112
+ buttonPosition: SecretButtonPosition;
113
+ /**
114
+ * Whether to reveal content on hover/focus in addition to clicks.
115
+ * @attribute show-on-hover
116
+ * @default false
117
+ */
118
+ showOnHover: boolean;
119
+ /**
120
+ * When set, secrets with the same name will close when another secret with that name is opened.
121
+ * @attribute
122
+ * @default ''
123
+ */
124
+ name: string;
125
+ private _buttonElement;
126
+ private _contentNodes;
127
+ private _mask;
128
+ constructor();
129
+ firstUpdated(): void;
130
+ willUpdate(changedProperties: PropertyValues<this>): void;
131
+ updated(changedProperties: PropertyValues<this>): void;
132
+ render(): TemplateResult;
133
+ }
134
+ declare global {
135
+ interface HTMLElementTagNameMap {
136
+ 'forge-secret': SecretComponent;
137
+ }
138
+ }
@@ -0,0 +1,331 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+
7
+ import { __decorate } from 'tslib';
8
+ import { CUSTOM_ELEMENT_NAME_PROPERTY, CUSTOM_ELEMENT_DEPENDENCIES_PROPERTY, LiveAnnouncer } from '@tylertech/forge-core';
9
+ import { tylIconEyeOutline, tylIconEyeClosed } from '@tylertech/tyler-icons';
10
+ import { unsafeCSS, html, nothing } from 'lit';
11
+ import { property, query, queryAssignedNodes, state, customElement } from 'lit/decorators.js';
12
+ import { classMap } from 'lit/directives/class-map.js';
13
+ import { createRef, ref } from 'lit/directives/ref.js';
14
+ import { BaseLitElement } from '../core/base/base-lit-element.js';
15
+ import { setDefaultAria } from '../core/utils/a11y-utils.js';
16
+ import { toggleState } from '../core/utils/utils.js';
17
+ import '../icon-button/index.js';
18
+ import '../icon/index.js';
19
+ import '../tooltip/index.js';
20
+ import '../button/index.js';
21
+ import { ButtonComponent } from '../button/button.js';
22
+ import { IconButtonComponent } from '../icon-button/icon-button.js';
23
+ import { TooltipComponent } from '../tooltip/tooltip.js';
24
+ import styles from './secret.scss.js';
25
+ import { IconRegistry } from '../icon/icon-registry.js';
26
+ import { IconComponent } from '../icon/icon.js';
27
+
28
+ var _a, _b;
29
+ const SECRET_TAG_NAME = 'forge-secret';
30
+ /**
31
+ * @tag forge-secret
32
+ *
33
+ * @summary A component that conceals content with a blur or dot mask, revealing it on user interaction.
34
+ *
35
+ * @description
36
+ * The secret component blurs or masks content and provides a connected button to reveal it.
37
+ * Content is revealed on click/keyboard interaction, with optional hover reveal. Multiple instances
38
+ * sharing a name act as a radio group where only one can be revealed at a time.
39
+ *
40
+ * @cssproperty --forge-secret-blur - The radius of the blur effect.
41
+ * @cssproperty --forge-secret-button-background - The background color of the button.
42
+ * @cssproperty --forge-secret-button-color - The text and icon color of the button.
43
+ * @cssproperty --forge-secret-button-shape - The button's border radius.
44
+ * @cssproperty --forge-secret-button-padding - The inline padding around the button or inline and block padding around the text button.
45
+ * @cssproperty --forge-secret-icon-size - The icon's size.
46
+ * @cssproperty --forge-secret-text-button-shape - The text button's border radius.
47
+ * @cssproperty --forge-secret-text-decoration-line - The decoration line applied to open inline content.
48
+ * @cssproperty --forge-secret-text-decoration-style - The style of the text decoration line.
49
+ * @cssproperty --forge-secret-text-decoration-color - The color of the text decoration line.
50
+ * @cssproperty --forge-secret-text-underline-offset - The spacing between the inline content and the text decoration line.
51
+ * @cssproperty --forge-secret-transition-duration - The duration of transitions.
52
+ * @cssproperty --forge-secret-transition-easing - The timing function of transitions.
53
+ *
54
+ * @slot - Default slot for the secret content.
55
+ * @slot close-icon - An icon that is shown when the secret is open, indicating it can be closed.
56
+ * @slot open-icon - An icon that is shown when the secret is closed, indicating it can be opened.
57
+ * @slot label - Text content that appears in the tooltip attached to the button or as the button text when set to block.
58
+ *
59
+ * @state open - Indicates that the content is visible.
60
+ * @state block - Indicates that the secret is displayed as a block element instead of inline.
61
+ *
62
+ * @csspart root - The root container.
63
+ * @csspart content - The content container.
64
+ * @csspart button - The toggle button element.
65
+ * @csspart text-button - The toggle button element when the secret is set to block.
66
+ *
67
+ * @cssclass forge-secret - The secret component container (required).
68
+ * @cssclass forge-secret--blur - Applies a blur effect to conceal content when the button has `aria-expanded="false"`.
69
+ * @cssclass forge-secret--masked - Applies a dot mask to conceal content when the button has `aria-expanded="false"`. Not applicable when the secret is set to block. In that case the blur variant is used instead.
70
+ * @cssclass forge-secret--noise - Applies a noise effect to conceal content when the button has `aria-expanded="false"`.
71
+ * @cssclass forge-secret--block - Displays the secret as a block element.
72
+ * @cssclass forge-secret--show-on-hover - Reveals the secret content when hovering over the component or focusing the button.
73
+ * @cssclass forge-secret__content - The content to be concealed or revealed.
74
+ * @cssclass forge-secret__button - The icon button used to toggle an inline secret.
75
+ * @cssclass forge-secret__text-button - The text button used to toggle a block secret.
76
+ * @cssclass forge-secret__masked - The element replacing concealed content when the masked variant is used. The `data-mask` attribute on this element sets its content.
77
+ *
78
+ * @fires {ToggleEvent} toggle - Dispatched when the secret opens or closes.
79
+ */
80
+ let SecretComponent = class SecretComponent extends BaseLitElement {
81
+ static { _a = CUSTOM_ELEMENT_NAME_PROPERTY, _b = CUSTOM_ELEMENT_DEPENDENCIES_PROPERTY; }
82
+ static {
83
+ IconRegistry.define([tylIconEyeOutline, tylIconEyeClosed]);
84
+ }
85
+ static { this.styles = unsafeCSS(styles); }
86
+ /** @deprecated Used for compatibility with legacy Forge @customElement decorator. */
87
+ static { this[_a] = SECRET_TAG_NAME; }
88
+ /** @deprecated Used for compatibility with legacy Forge @customElement decorator. */
89
+ static { this[_b] = [ButtonComponent, IconButtonComponent, IconComponent, TooltipComponent]; }
90
+ #toggleTextRef;
91
+ #internals;
92
+ constructor() {
93
+ super();
94
+ /**
95
+ * Whether the secret content is visible.
96
+ * @attribute
97
+ * @default false
98
+ */
99
+ this.open = false;
100
+ /**
101
+ * The style applied to hidden content when the secret is set to inline. Possible values are blur, masked, and noise.
102
+ * @attribute
103
+ * @default 'blur'
104
+ */
105
+ this.variant = 'blur';
106
+ /**
107
+ * The mask pattern to use with the masked variant. When empty the slotted text content is used as the mask.
108
+ * @attribute
109
+ * @default ''
110
+ */
111
+ this.mask = '';
112
+ /**
113
+ * The character to replace characters with in the masked variant.
114
+ * @attribute mask-character
115
+ * @default '●'
116
+ */
117
+ this.maskCharacter = '●';
118
+ /**
119
+ * Characters that will not be replaced by the mask character in the masked variant.
120
+ * @attribute unmasked-characters
121
+ * @default ''
122
+ */
123
+ this.unmaskedCharacters = '';
124
+ /**
125
+ * Whether the secret content should be displayed as a block element instead of inline with text.
126
+ * @attribute
127
+ * @default false
128
+ */
129
+ this.block = false;
130
+ /**
131
+ * The position of the button when the secret is set to inline. Possible values are start and end.
132
+ * @attribute button-position
133
+ * @default 'end'
134
+ */
135
+ this.buttonPosition = 'end';
136
+ /**
137
+ * Whether to reveal content on hover/focus in addition to clicks.
138
+ * @attribute show-on-hover
139
+ * @default false
140
+ */
141
+ this.showOnHover = false;
142
+ /**
143
+ * When set, secrets with the same name will close when another secret with that name is opened.
144
+ * @attribute
145
+ * @default ''
146
+ */
147
+ this.name = '';
148
+ this._mask = '';
149
+ this.#toggleTextRef = createRef();
150
+ this.#internals = this.attachInternals();
151
+ }
152
+ firstUpdated() {
153
+ setDefaultAria(this, this.#internals, { role: 'group', ariaLabel: 'secret' });
154
+ }
155
+ willUpdate(changedProperties) {
156
+ if (changedProperties.has('block')) {
157
+ toggleState(this.#internals, 'block', this.block);
158
+ }
159
+ if (changedProperties.has('open')) {
160
+ toggleState(this.#internals, 'open', this.open);
161
+ if (this.open && this.name) {
162
+ this.#hideOtherSecretsInGroup();
163
+ }
164
+ }
165
+ }
166
+ updated(changedProperties) {
167
+ this.#tryUpdateInternalMask(changedProperties);
168
+ if (changedProperties.has('open') && this.open) {
169
+ this.#announceContent();
170
+ }
171
+ }
172
+ render() {
173
+ return html `
174
+ <span
175
+ part="root"
176
+ class=${classMap({ 'forge-secret': true, reverse: this.buttonPosition === 'start', 'show-on-hover': this.showOnHover })}
177
+ @click="${this.#handleClick}">
178
+ <span
179
+ class=${classMap({
180
+ content: true,
181
+ blur: !this.open && (this.variant === 'blur' || (this.block && this.variant === 'masked')),
182
+ masked: !this.open && !this.block && this.variant === 'masked',
183
+ noise: !this.open && this.variant === 'noise'
184
+ })}
185
+ part="content"
186
+ ?inert=${!this.open}
187
+ data-mask=${this._mask || nothing}>
188
+ <slot></slot>
189
+ </span>
190
+ ${this.block ? this.#renderTextButton() : this.#renderIconButton()}
191
+ </span>
192
+ `;
193
+ }
194
+ #renderTextButton() {
195
+ return html `
196
+ <forge-button
197
+ class="text-button"
198
+ exportparts="text-button"
199
+ aria-expanded="${this.open}"
200
+ .ariaControlsElements=${[this]}
201
+ @keydown="${this.#handleKeyDown}">
202
+ ${this.#renderIcon()}
203
+ <slot name="label">${this.open ? 'Hide' : 'Show'}</slot>
204
+ </forge-button>
205
+ `;
206
+ }
207
+ #renderIconButton() {
208
+ const buttonAriaLabelledByElements = [this];
209
+ if (this.#toggleTextRef.value) {
210
+ buttonAriaLabelledByElements.unshift(this.#toggleTextRef.value);
211
+ }
212
+ return html `
213
+ <span hidden ${ref(this.#toggleTextRef)}>Toggle</span>
214
+ <forge-icon-button
215
+ class="button"
216
+ exportparts="button"
217
+ aria-expanded="${this.open}"
218
+ .ariaControlsElements=${[this]}
219
+ .ariaLabelledByElements=${buttonAriaLabelledByElements}
220
+ @keydown="${this.#handleKeyDown}">
221
+ ${this.#renderIcon()}
222
+ </forge-icon-button>
223
+ <forge-tooltip anchor="button" placement="top">
224
+ <slot name="label">${this.open ? 'Hide' : 'Show'}</slot>
225
+ </forge-tooltip>
226
+ `;
227
+ }
228
+ #renderIcon() {
229
+ return html `
230
+ <slot name="${this.open ? 'close-icon' : 'open-icon'}">
231
+ <forge-icon class="icon" .name=${this.open ? 'eye_closed' : 'eye_outline'}></forge-icon>
232
+ </slot>
233
+ `;
234
+ }
235
+ #handleClick(evt) {
236
+ const didClickButton = evt.composedPath().indexOf(this._buttonElement) !== -1;
237
+ if (!didClickButton) {
238
+ // Ignore clicks from the content when visible
239
+ if (this.open) {
240
+ return;
241
+ }
242
+ // Focus the button as if it were clicked instead of the content
243
+ this._buttonElement.focus({ focusVisible: false });
244
+ }
245
+ this.open = !this.open;
246
+ this.#dispatchToggleEvent();
247
+ }
248
+ #handleKeyDown(event) {
249
+ if (event.key === 'Escape' && this.open) {
250
+ event.stopPropagation();
251
+ this.open = false;
252
+ this.#dispatchToggleEvent();
253
+ }
254
+ }
255
+ #hideOtherSecretsInGroup() {
256
+ const root = this.getRootNode();
257
+ const allSecrets = Array.from(root.querySelectorAll('forge-secret'));
258
+ allSecrets
259
+ .filter(secret => secret !== this && secret.name === this.name && secret.open)
260
+ .forEach(secret => {
261
+ secret.open = false;
262
+ secret.#dispatchToggleEvent();
263
+ });
264
+ }
265
+ #dispatchToggleEvent() {
266
+ const event = new ToggleEvent('toggle', { oldState: (!this.open).toString(), bubbles: true, composed: true });
267
+ this.dispatchEvent(event);
268
+ }
269
+ #getTextContent() {
270
+ return Array.from(this._contentNodes)
271
+ .map(node => node.textContent)
272
+ .join(' ')
273
+ .trim();
274
+ }
275
+ #announceContent() {
276
+ LiveAnnouncer.instance.announce(this.#getTextContent(), 'polite');
277
+ }
278
+ #tryUpdateInternalMask(changedProperties) {
279
+ if (this.block || this.variant !== 'masked') {
280
+ return;
281
+ }
282
+ const maskRelatedProperties = ['unmaskedCharacters', 'block', 'mask', 'maskCharacter', 'variant'];
283
+ if (!maskRelatedProperties.some(prop => changedProperties.has(prop))) {
284
+ return;
285
+ }
286
+ const content = this.mask || this.#getTextContent();
287
+ const regex = new RegExp(`[^${this.unmaskedCharacters.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}]`, 'g');
288
+ this._mask = content.replace(regex, this.maskCharacter);
289
+ }
290
+ };
291
+ __decorate([
292
+ property({ type: Boolean })
293
+ ], SecretComponent.prototype, "open", void 0);
294
+ __decorate([
295
+ property()
296
+ ], SecretComponent.prototype, "variant", void 0);
297
+ __decorate([
298
+ property()
299
+ ], SecretComponent.prototype, "mask", void 0);
300
+ __decorate([
301
+ property({ attribute: 'mask-character' })
302
+ ], SecretComponent.prototype, "maskCharacter", void 0);
303
+ __decorate([
304
+ property({ attribute: 'unmasked-characters' })
305
+ ], SecretComponent.prototype, "unmaskedCharacters", void 0);
306
+ __decorate([
307
+ property({ type: Boolean })
308
+ ], SecretComponent.prototype, "block", void 0);
309
+ __decorate([
310
+ property({ attribute: 'button-position' })
311
+ ], SecretComponent.prototype, "buttonPosition", void 0);
312
+ __decorate([
313
+ property({ type: Boolean, attribute: 'show-on-hover' })
314
+ ], SecretComponent.prototype, "showOnHover", void 0);
315
+ __decorate([
316
+ property({ reflect: true })
317
+ ], SecretComponent.prototype, "name", void 0);
318
+ __decorate([
319
+ query(':is(forge-button, forge-icon-button)')
320
+ ], SecretComponent.prototype, "_buttonElement", void 0);
321
+ __decorate([
322
+ queryAssignedNodes({ flatten: true })
323
+ ], SecretComponent.prototype, "_contentNodes", void 0);
324
+ __decorate([
325
+ state()
326
+ ], SecretComponent.prototype, "_mask", void 0);
327
+ SecretComponent = __decorate([
328
+ customElement(SECRET_TAG_NAME)
329
+ ], SecretComponent);
330
+
331
+ export { SECRET_TAG_NAME, SecretComponent };