Skip to content

Commit

Permalink
refactor: scope scl text field
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobVogelsang committed Dec 17, 2024
1 parent 34a3c92 commit 62740d9
Show file tree
Hide file tree
Showing 8 changed files with 1,492 additions and 1,188 deletions.
79 changes: 53 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,57 @@ This is a web component meant to be used for SCL type attributes. In addition to
This element was meant to be used only for plugins in this organization. If it still fills you bill please use or re-use it. But be aware that we will not react on features wishes that do not contribute to the needs of plugin in this organization.


## `scl-text-field.ts`:
## `SclTextField.ts`:

### class: `SclTextField`
### class: `SclTextField`, `scl-text-field`

#### Superclass

| Name | Module | Package |
| ------------ | ------ | ------- |
| `LitElement` | | lit |

#### Mixins

| Name | Module | Package |
| --------------------- | ------ | --------------------------------------- |
| `ScopedElementsMixin` | | @open-wc/scoped-elements/lit-element.js |

#### Static Fields

| Name | Privacy | Type | Default | Description | Inherited From |
| ---------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------- |
| `scopedElements` | | `object` | `{
'md-filled-text-field':MdFilledTextField,
'md-switch': MdSwitch,
'md-icon': MdIcon,
'md-icon-button': MdIconButton,
'md-menu': MdMenu,
'md-menu-item': MdMenuItem,
}` | | |

#### Fields

| Name | Privacy | Type | Default | Description | Inherited From |
| ---------------- | ------- | --------------------- | ------------ | ------------------------------------------------------------------------------------------------------------- | -------------- |
| `nullable` | | `boolean` | `false` | Whether \[\[\`value\`]] may be set to \`null\` by nullSwitch | |
| `value` | | `string \| null` | | SCL attributes \`value\`, can only be \`null\` if \[\[\`nullable\`]]. | |
| `disabled` | | `boolean` | `false` | | |
| `label` | | `string` | `''` | | |
| `required` | | `boolean` | `false` | | |
| `supportingText` | | `string` | `''` | | |
| `suffixText` | | `string` | `''` | The inputs suffix text so long there is no \[\[\`unit\`]] defined | |
| `placeholder` | | `string` | `''` | | |
| `type` | | `'number' \| 'text'` | `'text'` | | |
| `pattern` | | `string` | `''` | | |
| `max` | | `string` | `''` | | |
| `min` | | `string` | `''` | | |
| `maxLength` | | `number` | `-1` | | |
| `minLength` | | `number` | `-1` | | |
| `unit` | | `string` | `''` | SI Unit, must be non-empty to allow for selecting a \[\[\`multiplier\`]].
Overrides \[\[\`suffixText\`]]. | |
| `multipliers` | | `array` | `[null, '']` | Selectable SI multipliers for a non-empty \[\[\`unit\`]]. | |
| `multiplier` | | `string \| null` | | The current selected \[\[\`multiplier\`]] | |
| `nullSwitch` | | `Switch \| undefined` | | | |
| Name | Privacy | Type | Default | Description | Inherited From |
| ---------------- | ------- | ----------------------- | ------------ | ------------------------------------------------------------------------------------------------------------- | -------------- |
| `nullable` | | `boolean` | `false` | Whether \[\[\`value\`]] may be set to \`null\` by nullSwitch | |
| `value` | | `string \| null` | | SCL attributes \`value\`, can only be \`null\` if \[\[\`nullable\`]]. | |
| `disabled` | | `boolean` | `false` | | |
| `label` | | `string` | `''` | | |
| `required` | | `boolean` | `false` | | |
| `supportingText` | | `string` | `''` | | |
| `suffixText` | | `string` | `''` | The inputs suffix text so long there is no \[\[\`unit\`]] defined | |
| `placeholder` | | `string` | `''` | | |
| `type` | | `'number' \| 'text'` | `'text'` | | |
| `pattern` | | `string` | `''` | | |
| `max` | | `string` | `''` | | |
| `min` | | `string` | `''` | | |
| `maxLength` | | `number` | `-1` | | |
| `minLength` | | `number` | `-1` | | |
| `unit` | | `string` | `''` | SI Unit, must be non-empty to allow for selecting a \[\[\`multiplier\`]].
Overrides \[\[\`suffixText\`]]. | |
| `multipliers` | | `array` | `[null, '']` | Selectable SI multipliers for a non-empty \[\[\`unit\`]]. | |
| `multiplier` | | `string \| null` | | The current selected \[\[\`multiplier\`]] | |
| `nullSwitch` | | `MdSwitch \| undefined` | | | |

#### Methods

Expand Down Expand Up @@ -66,7 +85,7 @@ This element was meant to be used only for plugins in this organization. If it s
| `isNull` | private | `boolean` | `false` | | |
| `parkedValue` | private | `string \| null` | `null` | | |
| `null` | private | `boolean` | | | |
| `multiplierMenu` | private | `Menu \| undefined` | | | |
| `multiplierMenu` | private | `MdMenu \| undefined` | | | |
| `textField` | private | `TextField \| undefined` | | | |

#### Methods
Expand All @@ -86,9 +105,17 @@ This element was meant to be used only for plugins in this organization. If it s

### Exports

| Kind | Name | Declaration | Module | Package |
| ---- | -------------- | ------------ | ----------------- | ------- |
| `js` | `SclTextField` | SclTextField | scl-text-field.ts | |
| Kind | Name | Declaration | Module | Package |
| ---- | -------------- | ------------ | --------------- | ------- |
| `js` | `SclTextField` | SclTextField | SclTextField.ts | |

## `scl-text-field.ts`:

### Exports

| Kind | Name | Declaration | Module | Package |
| --------------------------- | ---------------- | ------------ | ---------------- | ------- |
| `custom-element-definition` | `scl-text-field` | SclTextField | /SclTextField.js | |


© 2023 The Contributors
281 changes: 281 additions & 0 deletions SclTextField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
import { css, html, LitElement, TemplateResult } from 'lit';

import { property, query, state } from 'lit/decorators.js';

import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';

import {
CloseMenuEvent,
MdMenu,
} from '@scopedelement/matarial-web/menu/MdMenu.js';
import { MdFilledTextField } from '@scopedelement/matarial-web/textfield/MdFilledTextField.js';
import { MdIcon } from '@scopedelement/matarial-web/icon/MdIcon.js';
import { MdIconButton } from '@scopedelement/matarial-web/iconbutton/MdIconButton.js';
import { MdMenuItem } from '@scopedelement/matarial-web/menu/MdMenuItem.js';
import { MdSwitch } from '@scopedelement/matarial-web/switch/MdSwtich.js';
import { TextField } from '@scopedelement/matarial-web/textfield/internal/text-field';

/** TextField designed to be used for SCL element */
export class SclTextField extends ScopedElementsMixin(LitElement) {
static scopedElements = {
'md-filled-text-field': MdFilledTextField,
'md-switch': MdSwitch,
'md-icon': MdIcon,
'md-icon-button': MdIconButton,
'md-menu': MdMenu,
'md-menu-item': MdMenuItem,
};

/** Whether [[`value`]] may be set to `null` by nullSwitch */
@property({ type: Boolean })
nullable = false;

@state()
private textFieldValue: string = '';

/** SCL attributes `value`, can only be `null` if [[`nullable`]]. */
@property({ type: String })
set value(value: string | null) {
if (value === null) this.null = true;
else {
this.null = false;
this.textFieldValue = value;
}
}

get value(): string | null {
return this.null ? null : this.textFieldValue;
}

@property({ type: Boolean })
disabled: boolean = false;

@property({ type: String })
label: string = '';

@property({ type: Boolean })
required: boolean = false;

@property({ type: String })
supportingText: string = '';

/** The inputs suffix text so long there is no [[`unit`]] defined */
@property({ type: String })
suffixText: string = '';

@property({ type: String })
placeholder: string = '';

@property({ type: String })
type: 'number' | 'text' = 'text';

@property({ type: String })
pattern: string = '';

@property({ type: String })
max: string = '';

@property({ type: String })
min: string = '';

@property({ type: Number })
maxLength: number = -1;

@property({ type: Number })
minLength: number = -1;

/** SI Unit, must be non-empty to allow for selecting a [[`multiplier`]].
* Overrides [[`suffixText`]]. */
@property({ type: String })
unit = '';

private multiplierIndex = 0;

/** Selectable SI multipliers for a non-empty [[`unit`]]. */
@property({ type: Array })
multipliers = [null, ''];

/** The current selected [[`multiplier`]] */
@property({ type: String })
get multiplier(): string | null {
if (this.unit === '') return null;
return (
this.multipliers[this.multiplierIndex] ?? this.multipliers[0] ?? null
);
}

set multiplier(value: string | null) {
const index = this.multipliers.indexOf(value);
if (index >= 0) this.multiplierIndex = index;
this.suffixText = (this.multiplier ?? '') + this.unit;
}

@state()
private isNull = false;

private parkedValue: string | null = null;

@state()
private get null(): boolean {
return this.nullable && this.isNull;
}

private set null(value: boolean) {
if (!this.nullable || value === this.isNull) return;
this.isNull = value;
// if (this.isNull) this.parkValue();
// else this.returnParkedValue();
}

@query('.nullswitch.element') nullSwitch?: MdSwitch;

@query('.multipliers') private multiplierMenu?: MdMenu;

@query('.input.element') private textField?: TextField;

reportValidity(): boolean {
return this.textField!.reportValidity();
}

setCustomValidity(message: string): void {
this.textField?.setCustomValidity(message);
}

checkValidity(): boolean {
return this.textField!.checkValidity();
}

reset(): void {
return this.textField!.reset();
}

private returnParkedValue(): void {
if (this.parkedValue === null) return;
this.textFieldValue = this.parkedValue;
this.parkedValue = null;
}

private parkValue(): void {
if (this.parkedValue !== null) return;
this.parkedValue = this.textFieldValue;
}

private selectMultiplier(se: CloseMenuEvent): void {
let selection =
se.detail.initiator.querySelector(':scope > div')?.textContent;

if (selection === 'No multiplier') selection = null;
if (selection !== undefined) this.multiplier = selection;
}

private renderMultiplierList(): TemplateResult {
return html`${this.multipliers.map(multiplier => {
const value = multiplier === null ? 'No multiplier' : multiplier;
return html`<md-menu-item
?selected=${multiplier === this.multiplier}
value="${value}"
@close-menu="${this.selectMultiplier}"
><div slot="headline">${value}</div>
</md-menu-item>`;
})}`;
}

private renderUnitSelector(): TemplateResult {
if (this.multipliers.length && this.unit)
return html`<div class="units container">
<md-icon-button
id="multiplier-anchor"
style="margin:5px;"
?disabled=${this.null || this.disabled}
@click=${() => this.multiplierMenu?.show()}
><md-icon>more</md-icon></md-icon-button
>
<md-menu class="multipliers" anchor="multiplier-anchor"
>${this.renderMultiplierList()}</md-menu
>
</div>`;

return html``;
}

private renderNullSwitch(): TemplateResult {
if (this.nullable) {
return html`<md-switch
class="nullswitch element"
?selected=${!this.null}
?disabled=${this.disabled}
@input="${async (evt: Event) => {
/** TODO(jakob-vogelsang): change when
* https://github.com/material-components/material-web/issues/5486
* is fixed */
evt.stopPropagation();
}}"
@change="${async (evt: Event) => {
this.null = !(evt.target as MdSwitch).selected;
await this.updateComplete;
this.dispatchEvent(new Event('input'));
}}"
></md-switch>`;
}
return html``;
}

render(): TemplateResult {
return html`
<div style="display: flex; flex-direction: row;">
<div class="input container">
<md-filled-text-field
class="input element"
@input="${(evt: InputEvent) => {
this.textFieldValue = (evt.target as TextField).value;
}}"
value="${this.textFieldValue}"
?disabled=${this.disabled || this.isNull}
label="${this.label}"
?required=${this.required}
supporting-text="${this.supportingText}"
pattern="${this.pattern}"
placeholder="${this.placeholder}"
max="${this.max}"
min="${this.min}"
type="${this.type}"
.maxLength=${this.maxLength}
.minLength=${this.minLength}
suffix-text="${this.suffixText || this.unit}"
></md-filled-text-field>
</div>
${this.renderUnitSelector()}
<div class="nullswitch container">${this.renderNullSwitch()}</div>
</div>
`;
}

static styles = css`
.units.container {
position: relative;
}
.nullswitch.element {
margin-left: 12px;
}
.nullswitch.container {
display: flex;
align-items: center;
height: 56px;
}
.input.container {
flex: auto;
}
.input.element {
width: 100%;
}
md-icon-button {
--md-icon-button-icon-size: 48px;
}
`;
}
Loading

0 comments on commit 62740d9

Please sign in to comment.