UNPKG

ember-material-icons

Version:

Google Material icons for your ember-cli app

207 lines (169 loc) 6.46 kB
import { FIXME, Opaque, Option, Maybe } from '@glimmer/util'; import { DOMNamespace } from './helper'; import * as Simple from './interfaces'; import { sanitizeAttributeValue, requiresSanitization } from './sanitized-values'; import { normalizeProperty } from './props'; import { SVG_NAMESPACE } from './helper'; import { normalizeTextValue } from '../compiled/opcodes/content'; import { Environment } from '../environment'; export function defaultManagers(element: Simple.Element, attr: string, _isTrusting: boolean, _namespace: Option<string>): AttributeManager { let tagName = element.tagName; let isSVG = element.namespaceURI === SVG_NAMESPACE; if (isSVG) { return defaultAttributeManagers(tagName, attr); } let { type, normalized } = normalizeProperty(element, attr); if (type === 'attr') { return defaultAttributeManagers(tagName, normalized); } else { return defaultPropertyManagers(tagName, normalized); } } export function defaultPropertyManagers(tagName: string, attr: string): AttributeManager { if (requiresSanitization(tagName, attr)) { return new SafePropertyManager(attr); } if (isUserInputValue(tagName, attr)) { return INPUT_VALUE_PROPERTY_MANAGER; } if (isOptionSelected(tagName, attr)) { return OPTION_SELECTED_MANAGER; } return new PropertyManager(attr); } export function defaultAttributeManagers(tagName: string, attr: string): AttributeManager { if (requiresSanitization(tagName, attr)) { return new SafeAttributeManager(attr); } return new AttributeManager(attr); } export function readDOMAttr(element: Element, attr: string) { let isSVG = element.namespaceURI === SVG_NAMESPACE; let { type, normalized } = normalizeProperty(element, attr); if (isSVG) { return element.getAttribute(normalized); } if (type === 'attr') { return element.getAttribute(normalized); } { return element[normalized]; } }; export class AttributeManager { constructor(public attr: string) {} setAttribute(env: Environment, element: Simple.Element, value: Opaque, namespace?: DOMNamespace) { let dom = env.getAppendOperations(); let normalizedValue = normalizeAttributeValue(value); if (!isAttrRemovalValue(normalizedValue)) { dom.setAttribute(element, this.attr, normalizedValue, namespace); } } updateAttribute(env: Environment, element: Element, value: Opaque, namespace?: DOMNamespace) { if (value === null || value === undefined || value === false) { if (namespace) { env.getDOM().removeAttributeNS(element, namespace, this.attr); } else { env.getDOM().removeAttribute(element, this.attr); } } else { this.setAttribute(env, element, value); } } }; export class PropertyManager extends AttributeManager { setAttribute(_env: Environment, element: Simple.Element, value: Opaque, _namespace?: DOMNamespace) { if (!isAttrRemovalValue(value)) { element[this.attr] = value; } } protected removeAttribute(env: Environment, element: Element, namespace?: DOMNamespace) { // TODO this sucks but to preserve properties first and to meet current // semantics we must do this. let { attr } = this; if (namespace) { env.getDOM().removeAttributeNS(element, namespace, attr); } else { env.getDOM().removeAttribute(element, attr); } } updateAttribute(env: Environment, element: Element, value: Opaque, namespace?: DOMNamespace) { // ensure the property is always updated element[this.attr] = value; if (isAttrRemovalValue(value)) { this.removeAttribute(env, element, namespace); } } }; function normalizeAttributeValue(value: Opaque): Option<string> { if (value === false || value === undefined || value === null) { return null; } if (value === true) { return ''; } // onclick function etc in SSR if (typeof value === 'function') { return null; } return String(value); } function isAttrRemovalValue<T>(value: Maybe<T>): value is (null | undefined) { return value === null || value === undefined; } class SafePropertyManager extends PropertyManager { setAttribute(env: Environment, element: Simple.Element, value: Opaque) { super.setAttribute(env, element, sanitizeAttributeValue(env, element, this.attr, value)); } updateAttribute(env: Environment, element: Element, value: Opaque) { super.updateAttribute(env, element, sanitizeAttributeValue(env, element, this.attr, value)); } } function isUserInputValue(tagName: string, attribute: string) { return (tagName === 'INPUT' || tagName === 'TEXTAREA') && attribute === 'value'; } class InputValuePropertyManager extends AttributeManager { setAttribute(_env: Environment, element: Simple.Element, value: Opaque) { let input = element as FIXME<HTMLInputElement, "This breaks SSR">; input.value = normalizeTextValue(value); } updateAttribute(_env: Environment, element: Element, value: Opaque) { let input = <HTMLInputElement>element; let currentValue = input.value; let normalizedValue = normalizeTextValue(value); if (currentValue !== normalizedValue) { input.value = normalizedValue; } } } export const INPUT_VALUE_PROPERTY_MANAGER: AttributeManager = new InputValuePropertyManager('value'); function isOptionSelected(tagName: string, attribute: string) { return tagName === 'OPTION' && attribute === 'selected'; } class OptionSelectedManager extends PropertyManager { setAttribute(_env: Environment, element: Simple.Element, value: Opaque) { if (value !== null && value !== undefined && value !== false) { let option = <HTMLOptionElement>element; option.selected = true; } } updateAttribute(_env: Environment, element: Element, value: Opaque) { let option = <HTMLOptionElement>element; if (value) { option.selected = true; } else { option.selected = false; } } } export const OPTION_SELECTED_MANAGER: AttributeManager = new OptionSelectedManager('selected'); class SafeAttributeManager extends AttributeManager { setAttribute(env: Environment, element: Element, value: Opaque) { super.setAttribute(env, element, sanitizeAttributeValue(env, element, this.attr, value)); } updateAttribute(env: Environment, element: Element, value: Opaque, _namespace?: DOMNamespace) { super.updateAttribute(env, element, sanitizeAttributeValue(env, element, this.attr, value)); } }