Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(list): refactored list components #376

Merged
merged 6 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,226 changes: 644 additions & 582 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"@esm-bundle/chai": "^4.3.4-fix.0",
"@open-wc/testing": "^3.1.8",
"@tylertech-eslint/eslint-plugin": "^1.0.9",
"@tylertech/forge-cli": "^3.0.0-next.3",
"@tylertech/forge-cli": "^3.0.0-next.4",
"@tylertech/forge-testing": "^2.0.0",
"@tylertech/stylelint-rules": "^4.3.4",
"@types/jasmine": "^3.10.3",
Expand Down
380 changes: 257 additions & 123 deletions src/dev/pages/list/list.ejs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/dev/pages/list/list.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.list-demo {
max-width: 600px;
border: 1px solid var(--forge-theme-border-color);
}
}
5 changes: 3 additions & 2 deletions src/dev/pages/list/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import '@tylertech/forge/expansion-panel';
import '@tylertech/forge/drawer';
import './list.scss';
import { IconRegistry } from '@tylertech/forge/icon';
import { tylIconBluetooth, tylIconCode, tylIconDataUsage, tylIconFace, tylIconInfo, tylIconWifi } from '@tylertech/tyler-icons/standard';
import { tylIconBluetooth, tylIconCode, tylIconDataUsage, tylIconFace, tylIconInfo, tylIconWifi, tylIconOpenInNew } from '@tylertech/tyler-icons/standard';

IconRegistry.define([
tylIconWifi,
tylIconBluetooth,
tylIconDataUsage,
tylIconInfo,
tylIconCode,
tylIconFace
tylIconFace,
tylIconOpenInNew
]);
50 changes: 48 additions & 2 deletions src/lib/calendar/calendar-menu/_mixins.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
@use '@material/ripple/ripple' as mdc-ripple;
@use '@material/ripple/ripple-theme' as mdc-ripple-theme;
@use '@material/theme/theme' as mdc-theme;
@use '@material/typography/typography' as mdc-typography;
@use '../../theme';
@use '../../theme/theme-values';
@use '../../list/list-item/mixins' as list-item;
@use './variables';

@mixin core-styles() {
Expand Down Expand Up @@ -225,7 +225,7 @@
}

@mixin list-item() {
@include list-item.interactable;
@include _deprecated-interactable;

display: flex;
align-items: center;
Expand Down Expand Up @@ -412,3 +412,49 @@
}
}
}

@mixin _deprecated-interactable() {
@include mdc-ripple.surface;
@include mdc-ripple.radius-bounded;
@include mdc-ripple-theme.states(on-surface);
@include mdc-ripple-theme.states-activated(primary);
@include mdc-ripple-theme.states-selected(primary);

&--selected:focus:hover.mdc-ripple-upgraded--background-focused:not(.mdc-ripple-upgraded--foreground-activation)::before {
opacity: mdc-ripple-theme.states-opacity(primary, activated) + mdc-ripple-theme.states-opacity(primary, focus) +
mdc-ripple-theme.states-opacity(primary, hover);
}

&:focus,
&--active {
&:not(.forge-list-item--selected):not(.forge-list-item--activated):not(.mdc-ripple-upgraded--foreground-activation) {
&::before {
opacity: mdc-ripple-theme.states-opacity(primary, focus);
}
&:hover {
&::before {
opacity: mdc-ripple-theme.states-opacity(primary, focus) + mdc-ripple-theme.states-opacity(primary, hover);
}
}

&::before,
&::after {
@include mdc-theme.property(background-color, on-surface);
}
}
}

&--active:focus {
&.forge-list-item--selected.forge-list-item--activated:not(.mdc-ripple-upgraded--foreground-activation) {
&::before {
opacity: mdc-ripple-theme.states-opacity(primary, focus) + mdc-ripple-theme.states-opacity(primary, activated);
}
&:hover {
&::before {
opacity: mdc-ripple-theme.states-opacity(primary, focus) + mdc-ripple-theme.states-opacity(primary, hover) +
mdc-ripple-theme.states-opacity(primary, activated);
}
}
}
}
}
19 changes: 16 additions & 3 deletions src/lib/core/base/base-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { emitEvent, toggleAttribute } from '@tylertech/forge-core';
import { IBaseComponent } from './base-component';

export interface IBaseAdapter {
export interface IBaseAdapter<T extends HTMLElement = HTMLElement> {
readonly hostElement: T;
readonly isConnected: boolean;
removeHostAttribute(name: string): void;
getHostAttribute(name: string): string | null;
Expand All @@ -22,6 +23,10 @@ export interface IBaseAdapter {
export class BaseAdapter<T extends IBaseComponent> implements IBaseAdapter {
constructor(protected _component: T) {}

public get hostElement(): T {
return this._component;
}

public getHostAttribute(name: string): string | null {
return this._component.getAttribute(name);
}
Expand All @@ -42,12 +47,20 @@ export class BaseAdapter<T extends IBaseComponent> implements IBaseAdapter {
return emitEvent(this._component, type, data, bubble, cancelable);
}

public toggleHostListener(event: string, listener: EventListener, value: boolean, options?: boolean | AddEventListenerOptions): void {
if (value) {
this.addHostListener(event, listener, options);
} else {
this.removeHostListener(event, listener, options);
}
}

public addHostListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void {
this._component.addEventListener(event, callback, options);
}

public removeHostListener(event: string, callback: (event: Event) => void): void {
this._component.removeEventListener(event, callback);
public removeHostListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void {
this._component.removeEventListener(event, callback, options);
}

public addWindowListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void {
Expand Down
50 changes: 50 additions & 0 deletions src/lib/core/keyboard/key-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export type CanNavigateCallback = (index: number) => boolean;

export interface IKeyManagerConfig {
canNavigateCallback?: CanNavigateCallback;
}

export const NAVIGATION_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'End', 'Home'];

export class KeyManager {
constructor(private _config?: IKeyManagerConfig) {}

public isNavigationKey({ key, ctrlKey, altKey, shiftKey, metaKey }: KeyboardEvent): boolean {
if (ctrlKey || altKey || shiftKey || metaKey) {
return false;
}
return NAVIGATION_KEYS.includes(key);
}

public navigate(key: string, currentIndex: number, total: number): number {
const nextIndex = currentIndex + this.calculateNextIndex(key, total);
if (!this.canNavigateAtIndex(nextIndex)) {
return this.navigate(key, nextIndex, total);
}
return nextIndex;
}

public calculateNextIndex(key: string, total: number): number {
switch (key) {
case 'ArrowUp':
case 'ArrowLeft':
return -1;
case 'ArrowDown':
case 'ArrowRight':
return 1;
case 'Home':
return 0;
case 'End':
return total - 1;
default:
return 0;
}
}

public canNavigateAtIndex(index: number): boolean {
if (typeof this._config?.canNavigateCallback === 'function') {
return this._config.canNavigateCallback.call(null, index);
}
return true;
}
}
15 changes: 13 additions & 2 deletions src/lib/core/styles/_utils.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,19 @@
/// Creates a CSS custom property declaration for a token in the provided module
/// referencing an existing module token variable as the fallback.
///
@function module-ref($module, $token, $ref-token, $prefix: config.$prefix) {
@return _create-var($module, $token, var(--_#{$ref-token}), $prefix);
/// To provide a module reference variable with a different fallback value, set the
/// $type parameter to `value` and `$value` will be used verbatim.
///
@function module-ref($module, $token, $value, $type: ref, $prefix: config.$prefix) {
$fallback: if($type == ref, var(--_#{$value}), $value);
@return _create-var($module, $token, $fallback, $prefix);
}

///
/// Gets a module token CSS variable.
///
@function module-var($token, $prefix: config.$prefix) {
@return var(--_#{$token});
}

///
Expand Down
2 changes: 1 addition & 1 deletion src/lib/core/styles/tokens/focus-indicator/_tokens.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ $tokens: (
color: utils.module-val(focus-indicator, color, theme.variable(primary)),
duration: utils.module-val(focus-indicator, duration, animation.variable(duration-long4)),
outward-offset: utils.module-val(focus-indicator, outward-offset, spacing.variable('050')),
inward-offset: utils.module-val(focus-indicator, inward-offset, 0), // Requires unit
inward-offset: utils.module-val(focus-indicator, inward-offset, 0px), // Requires unit
shape: utils.module-val(focus-indicator, shape, shape.variable(extra-small)),
width: utils.module-val(focus-indicator, width, border.variable(medium)),
easing: utils.module-val(focus-indicator, easing, animation.variable(easing-emphasized)),
Expand Down
72 changes: 72 additions & 0 deletions src/lib/core/styles/tokens/list/list-item/_tokens.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@use 'sass:map';
@use '../../../utils';
@use '../../../theme';
@use '../../../shape';
@use '../../../typography';

$tokens: (
// Base
background-color: utils.module-val(list-item, color, theme.variable(surface)),
shape: utils.module-val(list-item, shape, 0),
padding: utils.module-val(list-item, padding, 8px 16px),
margin: utils.module-val(list-item, margin, 0),
height: utils.module-val(list-item, height, 48px),
dense-height: utils.module-val(list-item, dense-height, 32px),
indent: utils.module-val(list-item, indent, 56px),
cursor: utils.module-val(list-item, cursor, pointer),

// Supporting text
supporting-text-color: utils.module-val(list-item, supporting-text-color, theme.variable(text-secondary)),
supporting-text-font-size: utils.module-val(list-item, supporting-text-font-size, typography.variable(body, font-size)),
supporting-text-font-weight: utils.module-val(list-item, supporting-text-font-weight, typography.variable(body, font-weight)),
supporting-text-line-height: utils.module-val(list-item, supporting-text-line-height, typography.scale(24)),

// Selected
selected-color: utils.module-val(list-item, selected-color, theme.variable(primary)),
selected-opacity: utils.module-val(list-item, selected-opacity, 0.08),
selected-leading-color: utils.module-ref(list-item, selected-leading-color, selected-color),
selected-trailing-color: utils.module-ref(list-item, selected-trailing-color, selected-color),
selected-supporting-text-color: utils.module-val(list-item, selected-supporting-text-color, theme.variable(text-secondary)),

// Disabled
disabled-opacity: utils.module-val(list-item, disabled-opacity, 0.38),
disabled-cursor: utils.module-val(list-item, disabled-cursor, not-allowed),

// Line variants
one-line-height: utils.module-ref(list-item, one-line-height, height),
two-line-height: utils.module-val(list-item, two-line-height, utils.module-ref(list-item, height, 72px, value)),
three-line-height: utils.module-val(list-item, three-line-height, utils.module-ref(list-item, height, 88px, value)),

// Dense
dense-one-line-height: utils.module-ref(list-item, dense-one-line-height, dense-height),
dense-two-line-height: utils.module-val(list-item, dense-two-line-height, utils.module-ref(list-item, dense-height, 56px, value)),
dense-three-line-height: utils.module-val(list-item, dense-three-line-height, utils.module-ref(list-item, dense-height, 72px, value)),
dense-font-size: utils.module-val(list-item, dense-font-size, 0.875rem),
dense-indent: utils.module-val(list-item, dense-indent, 48px),

// Dense leading/trailing
dense-leading-margin-end: utils.module-val(list-item, dense-leading-margin-end, utils.module-ref(list-item, leading-margin-end, 24px, value)),
dense-trailing-margin-start: utils.module-val(list-item, dense-trailing-margin-start, utils.module-ref(list-item, trailing-margin-start, 24px, value)),

// Leading
leading-margin-start: utils.module-val(list-item, leading-margin-start, 0),
leading-margin-end: utils.module-val(list-item, leading-margin-end, 32px),
leading-selected-color: utils.module-ref(list-item, leading-selected-color, selected-color),

// Trailing
trailing-margin-start: utils.module-val(list-item, trailing-margin-start, 32px),
trailing-margin-end: utils.module-val(list-item, trailing-margin-end, 0),
trailing-selected-color: utils.module-ref(list-item, trailing-selected-color, selected-color),

// Avatar
avatar-background-color: utils.module-val(list-item, avatar-background-color, theme.variable(text-secondary)),
avatar-color: utils.module-val(list-item, avatar-color, theme.variable(on-primary)),
avatar-shape: utils.module-val(list-item, avatar-shape, shape.variable(round)),
avatar-margin-start: utils.module-val(list-item, avatar-margin-start, 0),
avatar-margin-end: utils.module-val(list-item, avatar-margin-end, 16px),
avatar-size: utils.module-val(list-item, avatar-size, 40px),
) !default;

@function get($key) {
@return map.get($tokens, $key);
}
11 changes: 11 additions & 0 deletions src/lib/core/styles/tokens/list/list/_tokens.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@use '../../../utils';

$tokens: (
container-color: utils.module-val(list, container-color, transparent),
block-padding: utils.module-val(list, block-padding, 8px),
inline-padding: utils.module-val(list, inline-padding, 0),
) !default;

@function get($key) {
@return map-get($tokens, $key);
}
10 changes: 5 additions & 5 deletions src/lib/core/styles/tokens/state-layer/_tokens.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
$tokens: (
color: utils.module-val(state-layer, color, theme.variable(on-surface)),
hover-color: utils.module-ref(state-layer, hover-color, color),
hover-opacity: 0.08,
hover-opacity: utils.module-val(state-layer, hover-opacity, 0.08),
pressed-color: utils.module-ref(state-layer, pressed-color, color),
pressed-opacity: 0.12,
hover-duration: 15ms,
pressed-duration: 105ms,
animation-duration: 375ms
pressed-opacity: utils.module-val(state-layer, pressed-opacity, 0.12),
hover-duration: utils.module-val(state-layer, hover-duration, 15ms),
pressed-duration: utils.module-val(state-layer, pressed-duration, 105ms),
animation-duration: utils.module-val(state-layer, animation-duration, 375ms)
) !default;

@function get($key) {
Expand Down
23 changes: 22 additions & 1 deletion src/lib/core/styles/typography/index.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use 'sass:map';
@use 'sass:list';
@use '../core/config';
@use '../utils';
@use '../tokens/typography/tokens';

@forward '../tokens/typography/tokens';
Expand All @@ -15,7 +16,7 @@
///
/// Allows for providing an optional list of tokens to exclude from the output:
/// ```scss
/// @include style(title, [font-family font-size]);
/// @include style(title, $exclude: [font-family font-size]);
/// ```
///
@mixin style($style, $exclude: null) {
Expand All @@ -40,6 +41,26 @@
}
}

///
/// Gets a CSS custom property declaration for a specific typography style token, with its token value as the fallback value.
///
@function variable($style, $token, $prefix: config.$prefix) {
@return _get-style-value($style, $token);
}

///
/// Gets the value of a typography scale token.
///
@function scale($scale) {
@return #{map.get(tokens.$font-scale, $scale)}rem;
}

@mixin ellipse {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

///
/// Sets base typography styles.
///
Expand Down
16 changes: 16 additions & 0 deletions src/lib/core/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,19 @@ export function locateTargetHeuristic(element: HTMLElement, id?: string | null):

return targetEl;
}

/**
* Replaces an existing element with a new element, while optionally moving the children of the old element into the new element.
* @param oldElement The element to replace.
* @param newElement The element to replace with.
* @param preserveChildren Whether or not to preserve the children of the old element in the new element.
* @returns The new element.
*/
export function replaceElement(oldElement: HTMLElement, newElement: HTMLElement, preserveChildren = true): HTMLElement {
if (preserveChildren) {
newElement.append(...oldElement.childNodes);
}
oldElement.insertAdjacentElement('beforebegin', newElement);
oldElement.remove();
return newElement;
}
Loading