Skip to content

Commit

Permalink
feat: support for global configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
DRiFTy17 committed May 23, 2024
1 parent 4526eff commit 41cdf88
Show file tree
Hide file tree
Showing 24 changed files with 148 additions and 18 deletions.
11 changes: 11 additions & 0 deletions src/dev/src/partials/page.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
<html lang="en">
<head>
<%- include('./src/partials/head.ejs', { title: page.title }) %>

<!-- Used for testing global configuration -->
<!--
<script>
window.TylerForgeGlobalConfiguration = {
'forge-field': {
'labelPosition': 'block-start'
}
};
</script>
-->
</head>
<body>
<forge-scaffold>
Expand Down
7 changes: 4 additions & 3 deletions src/lib/button/base/base-button-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { BASE_BUTTON_CONSTANTS } from './base-button-constants';
import { BUTTON_FORM_ATTRIBUTES, cloneAttributes } from '../../core/utils/reflect-utils';
import { internals, setDefaultAria } from '../../constants';
import { supportsPopover } from '../../core/utils/feature-detection';
import { IBaseComponent } from '../../core';

export interface IBaseButtonAdapter extends IBaseAdapter {
export interface IBaseButtonAdapter<T extends IBaseComponent> extends IBaseAdapter<T> {
readonly hasSlottedAnchor: boolean;
initialize(): void;
setDisabled(value: boolean): void;
Expand All @@ -23,14 +24,14 @@ export interface IBaseButtonAdapter extends IBaseAdapter {
addDefaultSlotChangeListener(listener: EventListener): void;
}

export abstract class BaseButtonAdapter extends BaseAdapter<IBaseButton> implements IBaseButtonAdapter {
export abstract class BaseButtonAdapter<T extends IBaseButton> extends BaseAdapter<T> implements IBaseButtonAdapter<T> {
protected readonly _rootElement: HTMLElement;
protected readonly _focusIndicatorElement: IFocusIndicatorComponent;
protected readonly _stateLayerElement: IStateLayerComponent;
protected readonly _defaultSlotElement: HTMLSlotElement;
protected readonly _endSlotElement: HTMLSlotElement;

constructor(component: IBaseButton) {
constructor(component: T) {
super(component);
this._rootElement = getShadowElement(this._component, BASE_BUTTON_CONSTANTS.selectors.ROOT) as HTMLButtonElement;
this._focusIndicatorElement = getShadowElement(this._component, FOCUS_INDICATOR_CONSTANTS.elementName) as IFocusIndicatorComponent;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/button/base/base-button-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ExperimentalFocusOptions } from '../../constants';
import { task } from '../../core/utils/event-utils';
import { IBaseButtonAdapter } from './base-button-adapter';
import { BASE_BUTTON_CONSTANTS, ButtonClickOptions, ButtonType } from './base-button-constants';
import { IBaseButton } from './base-button';

export interface IBaseButtonFoundation extends ICustomElementFoundation {
type: ButtonType;
Expand All @@ -13,7 +14,7 @@ export interface IBaseButtonFoundation extends ICustomElementFoundation {
focus(options?: ExperimentalFocusOptions): void;
}

export abstract class BaseButtonFoundation<T extends IBaseButtonAdapter> implements IBaseButtonFoundation {
export abstract class BaseButtonFoundation<T extends IBaseButtonAdapter<IBaseButton>> implements IBaseButtonFoundation {
private _type: ButtonType = 'button'; // We default our buttons to the "button" type instead of "submit" as that is more common
private _disabled = false;
private _popoverIcon = false;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/button/base/base-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface IBaseButton extends IWithLabelAwareness, IWithElementInternals,
focus(options?: ExperimentalFocusOptions): void;
}

export abstract class BaseButton<T extends BaseButtonFoundation<IBaseButtonAdapter>> extends WithDefaultAria(WithElementInternals(WithLabelAwareness(BaseComponent))) implements IBaseButton {
export abstract class BaseButton<T extends BaseButtonFoundation<IBaseButtonAdapter<IBaseButton>>> extends WithDefaultAria(WithElementInternals(WithLabelAwareness(BaseComponent))) implements IBaseButton {
public static get observedAttributes(): string[] {
return Object.values(BASE_BUTTON_CONSTANTS.observedAttributes);
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/button/button-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BaseButtonAdapter, IBaseButtonAdapter } from './base/base-button-adapter';
import { IButtonComponent } from './button';

export interface IButtonAdapter extends IBaseButtonAdapter {
export interface IButtonAdapter extends IBaseButtonAdapter<IButtonComponent> {
toggleStateLayer(value: boolean): void;
}

export class ButtonAdapter extends BaseButtonAdapter implements IButtonAdapter {
export class ButtonAdapter extends BaseButtonAdapter<IButtonComponent> implements IButtonAdapter {
constructor(component: IButtonComponent) {
super(component);
}
Expand Down
5 changes: 5 additions & 0 deletions src/lib/button/button-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export class ButtonFoundation extends BaseButtonFoundation<IButtonAdapter> imple
super(adapter);
}

public override initialize(): void {
super.initialize();
this._adapter.tryApplyGlobalConfiguration(['variant', 'dense']);
}

public get variant(): ButtonVariant {
return this._variant;
}
Expand Down
20 changes: 20 additions & 0 deletions src/lib/core/base/base-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { emitEvent, toggleAttribute } from '@tylertech/forge-core';
import { IBaseComponent } from './base-component';
import { GlobalConfiguration } from '../configuration/global-configuration';

export interface IBaseAdapter<T extends HTMLElement = HTMLElement> {
readonly hostElement: T;
Expand All @@ -24,6 +25,7 @@ export interface IBaseAdapter<T extends HTMLElement = HTMLElement> {
removeBodyAttribute(name: string): void;
focusHost(options?: FocusOptions): void;
clickHost(): void;
tryApplyGlobalConfiguration(properties: Array<keyof T>): void;
}

export class BaseAdapter<T extends IBaseComponent> implements IBaseAdapter<T> {
Expand Down Expand Up @@ -138,4 +140,22 @@ export class BaseAdapter<T extends IBaseComponent> implements IBaseAdapter<T> {
public get isConnected(): boolean {
return this._component.isConnected;
}

public tryApplyGlobalConfiguration(properties: Array<keyof T>): void {
const tagName = this._component.tagName.toLowerCase() as keyof HTMLElementTagNameMap;
const entry = GlobalConfiguration.get<any>(tagName);

if (!entry) {
return;
}

for (const property of properties) {
if (entry.has(property)) {
const value = entry.valueOf(property);
if (value) {
this._component[property] = value;
}
}
}
}
}
35 changes: 35 additions & 0 deletions src/lib/core/configuration/global-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type TylerForgeGlobalConfiguration = {
[K in keyof HTMLElementTagNameMap]?: Partial<HTMLElementTagNameMap[K]>;
};

declare global {
interface Window {
TylerForgeGlobalConfiguration: TylerForgeGlobalConfiguration;
}
}

export class GlobalConfiguration {
// eslint-disable-next-line @tylertech-eslint/require-private-modifier
private constructor() {}

/**
* Gets an entry from the global configuration for the specified element.
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
public static get<T extends keyof HTMLElementTagNameMap>(element: T) {
const entry = window.TylerForgeGlobalConfiguration?.[element];

if (!entry) {
return null;
}

return {
has(key: keyof HTMLElementTagNameMap[T]): key is keyof HTMLElementTagNameMap[T] {
return key in entry;
},
valueOf<TKey extends keyof HTMLElementTagNameMap[T]>(key: TKey) {
return entry[key];
}
};
}
}
2 changes: 1 addition & 1 deletion src/lib/dialog/dialog-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BaseAdapter, IBaseAdapter } from '../core/base/base-adapter';
import { DialogComponent, IDialogComponent } from './dialog';
import { dialogStack, DIALOG_CONSTANTS, hideBackdrop, showBackdrop } from './dialog-constants';

export interface IDialogAdapter extends IBaseAdapter {
export interface IDialogAdapter extends IBaseAdapter<IDialogComponent> {
readonly hostElement: IDialogComponent;
readonly moveHandleElement: HTMLElement;
readonly surfaceElement: HTMLElement;
Expand Down
8 changes: 8 additions & 0 deletions src/lib/dialog/dialog-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ export class DialogFoundation implements IDialogFoundation {
constructor(public _adapter: IDialogAdapter) {}

public initialize(): void {
this._adapter.tryApplyGlobalConfiguration([
'animationType',
'positionStrategy',
'sizeStrategy',
'persistent',
'moveable'
]);

if (this._trigger && !this._adapter.triggerElement) {
this._adapter.tryLocateTriggerElement(this._trigger);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/field/field-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FieldLabelPosition } from './base/base-field-constants';
import { IFieldComponent } from './field';
import { FIELD_CONSTANTS } from './field-constants';

export interface IFieldAdapter extends IBaseAdapter {
export interface IFieldAdapter extends IBaseAdapter<IFieldComponent> {
readonly focusIndicator: IFocusIndicatorComponent;
addRootListener(name: keyof HTMLElementEventMap, listener: EventListener): void;
addPopoverIconClickListener(listener: EventListener): void;
Expand Down
3 changes: 3 additions & 0 deletions src/lib/field/field-foundation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ICustomElementFoundation } from '@tylertech/forge-core';
import { GlobalConfiguration } from '../core/configuration/global-configuration';
import { FocusIndicatorFocusMode } from '../focus-indicator';
import { FieldDensity, FieldLabelAlignment, FieldLabelPosition, FieldShape, FieldSupportTextInset, FieldTheme, FieldVariant } from './base/base-field-constants';
import { IFieldAdapter } from './field-adapter';
Expand Down Expand Up @@ -53,6 +54,7 @@ export class FieldFoundation implements IFieldFoundation {

public initialize(): void {
this._adapter.addRootListener('slotchange', this._slotChangeListener);
this._adapter.tryApplyGlobalConfiguration(['labelPosition']);
this._adapter.setLabelPosition(this._labelPosition);

if (this._popoverIcon) {
Expand All @@ -61,6 +63,7 @@ export class FieldFoundation implements IFieldFoundation {
if (this._multiline) {
this._adapter.attachResizeContainer();
}

}

private _onSlotChange(evt: Event): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { BaseButtonAdapter, IBaseButtonAdapter } from '../button/base/base-butto
import { IFloatingActionButtonComponent } from './floating-action-button';
import { FLOATING_ACTION_BUTTON_CONSTANTS } from './floating-action-button-constants';

export interface IFloatingActionButtonAdapter extends IBaseButtonAdapter {
export interface IFloatingActionButtonAdapter extends IBaseButtonAdapter<IFloatingActionButtonComponent> {
destroy(): void;
}

export class FloatingActionButtonAdapter extends BaseButtonAdapter implements IFloatingActionButtonAdapter {
export class FloatingActionButtonAdapter extends BaseButtonAdapter<IFloatingActionButtonComponent> implements IFloatingActionButtonAdapter {
private _labelSlotElement: HTMLSlotElement;
private _extendedObserver: MutationObserver | undefined;

Expand Down
4 changes: 2 additions & 2 deletions src/lib/icon-button/icon-button-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BaseButtonAdapter, IBaseButtonAdapter } from '../button/base/base-button-adapter';
import { IIconButtonComponent } from './icon-button';

export interface IIconButtonAdapter extends IBaseButtonAdapter {}
export interface IIconButtonAdapter extends IBaseButtonAdapter<IIconButtonComponent> {}

export class IconButtonAdapter extends BaseButtonAdapter implements IIconButtonAdapter {
export class IconButtonAdapter extends BaseButtonAdapter<IIconButtonComponent> implements IIconButtonAdapter {
constructor(component: IIconButtonComponent) {
super(component);
}
Expand Down
5 changes: 5 additions & 0 deletions src/lib/icon-button/icon-button-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export class IconButtonFoundation extends BaseButtonFoundation<IIconButtonAdapte
super(adapter);
}

public override initialize(): void {
super.initialize();
this._adapter.tryApplyGlobalConfiguration(['variant', 'shape', 'density']);
}

protected override async _onClick(evt: MouseEvent): Promise<void> {
if (this._toggle) {
this._onToggle();
Expand Down
2 changes: 1 addition & 1 deletion src/lib/overlay/base/overlay-aware-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BaseAdapter, IBaseAdapter } from '../../core';
import { IOverlayComponent } from '../overlay';
import { IOverlayAware } from '../base/overlay-aware';

export interface IOverlayAwareAdapter extends IBaseAdapter {
export interface IOverlayAwareAdapter<T extends IOverlayAware = IOverlayAware> extends IBaseAdapter<T> {
readonly overlayElement: IOverlayComponent;
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/overlay/overlay-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
SUPPORTS_POPOVER
} from './overlay-constants';

export interface IOverlayAdapter extends IBaseAdapter {
export interface IOverlayAdapter extends IBaseAdapter<IOverlayComponent> {
show(): void;
hide(): void;
tryHideDescendantOverlays(): void;
Expand Down
11 changes: 11 additions & 0 deletions src/lib/overlay/overlay-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ export class OverlayFoundation extends BaseOverlayFoundation<IOverlayAdapter> im
}

public initialize(): void {
this._adapter.tryApplyGlobalConfiguration([
'placement',
'positionStrategy',
'shift',
'hide',
'flip',
'boundaryElement',
'fallbackPlacements',
'persistent'
]);

if (!this._noAnchor && !this._anchorElement && this._anchor) {
this._anchorElement = this._adapter.locateAnchorElement(this._anchor);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/popover/popover-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IOverlayAwareAdapter, OverlayAwareAdapter } from '../overlay/base/overl
import { IPopoverComponent } from './popover';
import { POPOVER_CONSTANTS } from './popover-constants';

export interface IPopoverAdapter extends IOverlayAwareAdapter {
export interface IPopoverAdapter extends IOverlayAwareAdapter<IPopoverComponent> {
readonly hostElement: IPopoverComponent;
destroy(): void;
initializeAnchorElement(): void;
Expand Down
13 changes: 13 additions & 0 deletions src/lib/popover/popover-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ export class PopoverFoundation extends WithLongpressListener(OverlayAwareFoundat
public override initialize(): void {
super.initialize();

this._adapter.tryApplyGlobalConfiguration([
'placement',
'animationType',
'positionStrategy',
'shift',
'hide',
'flip',
'boundaryElement',
'fallbackPlacements',
'persistent',
'arrow'
]);

if (!this.anchorElement) {
this._adapter.tryLocateAnchorElement(this._anchor);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/toast/toast-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IOverlayComponent, OVERLAY_CONSTANTS } from '../overlay';
import { IToastComponent } from './toast';
import { TOAST_CONSTANTS } from './toast-constants';

export interface IToastAdapter extends IBaseAdapter {
export interface IToastAdapter extends IBaseAdapter<IToastComponent> {
show(): void;
hide(): void;
addCloseListener(listener: EventListener): void;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/toast/toast-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export class ToastFoundation implements IToastFoundation {
constructor(private _adapter: IToastAdapter) {}

public initialize(): void {
this._adapter.tryApplyGlobalConfiguration([
'duration',
'placement',
'dismissible'
]);

if (this._open) {
this.show();
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/tooltip/tooltip-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IOverlayComponent, OVERLAY_CONSTANTS } from '../overlay';
import { ITooltipComponent } from './tooltip';
import { TOOLTIP_CONSTANTS } from './tooltip-constants';

export interface ITooltipAdapter extends IBaseAdapter {
export interface ITooltipAdapter extends IBaseAdapter<ITooltipComponent> {
readonly hostElement: ITooltipComponent;
readonly anchorElement: HTMLElement | null;
syncAria(): void;
Expand Down
11 changes: 11 additions & 0 deletions src/lib/tooltip/tooltip-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ export class TooltipFoundation extends WithLongpressListener() implements IToolt
}

public initialize(): void {
this._adapter.tryApplyGlobalConfiguration([
'type',
'delay',
'placement',
'offset',
'flip',
'boundaryElement',
'fallbackPlacements',
'triggerType'
]);

if (!this._adapter.anchorElement) {
this._adapter.tryLocateAnchorElement(this._anchor);
}
Expand Down

0 comments on commit 41cdf88

Please sign in to comment.