UNPKG

@alegendstale/holly-components

Version:

Reusable UI components created using lit

448 lines (432 loc) 15.3 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { LitElement, html, nothing, css } from 'lit'; import { property, query, state } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import { repeat } from 'lit/directives/repeat.js'; import { defaultSettings, Direction, PaletteError, parseColors, Status } from './color-palette-utils.js'; import { ColorPaletteItem } from './item/color-palette-item.js'; import { copyToClipboard } from '../../utils/basicUtils.js'; import { EventEmitter } from '../../utils/EventEmitter.js'; import { CanvasGradient } from '../canvas/canvas-gradient.js'; import { classMap } from 'lit/directives/class-map.js'; import { ColorPaletteItemEdit } from './item/color-palette-item-edit.js'; import { createRef, ref } from 'lit/directives/ref.js'; import { condCustomElement } from '../../decorators/condCustomElement.js'; let ColorPalette = class ColorPalette extends LitElement { constructor() { super(...arguments); this._editMode = false; this.colors = []; // Settings this.height = defaultSettings.height; this.width = defaultSettings.width; this.direction = defaultSettings.direction; this.gradient = false; this.preventHover = false; this.hideText = false; this.override = false; this.aliases = []; this.maxWidth = 0; this.status = Status.VALID; this.pulse = false; this.emitter = new EventEmitter(); this.pluginSettings = defaultSettings; this.createPalette = () => { try { if (this.status === Status.VALID) { if (this.gradient) return this.createGradient(); else return this.createColors(); } else { // Throw error & create Invalid Palette throw new PaletteError(this.status); } } catch (err) { if (!(err instanceof PaletteError)) return nothing; this.emitter.emit('notice', err.message); return this.createInvalidPalette(err.status, err.message); } }; } set editMode(val) { this._editMode = val; this.emitter.emit('editMode', val); } get editMode() { return this._editMode; } /** * Helper accessor for settings */ set settings({ height, width, direction, gradient, preventHover, override, aliases }) { this.height = height; this.width = width; this.direction = direction; this.gradient = gradient; this.preventHover = preventHover; this.override = override; this.aliases = aliases; } get settings() { return { height: this.height, width: this.width, direction: this.direction, gradient: this.gradient, preventHover: this.preventHover, hideText: this.hideText, override: this.override, aliases: this.aliases, }; } connectedCallback() { super.connectedCallback(); // Pulse the Invalid Palette to show its location if (this.pluginSettings.errorPulse) { this.pulse = true; setTimeout(() => { this.pulse = false; }, this.pluginSettings.noticeDuration); } } disconnectedCallback() { super.disconnectedCallback(); this.emitter.clear(); } willUpdate(_changedProperties) { super.willUpdate(_changedProperties); this.setStatusAndColors(); } /** * Sets the current status and parses colors */ setStatusAndColors() { const isSettingsValid = () => { return (!isNaN(this.height) && !isNaN(this.width) && Object.values(Direction).includes(this.direction) && this.gradient != null && this.preventHover != null && this.hideText != null && this.override != null && (this.aliases instanceof Array || typeof this.aliases === 'undefined')); }; const colorsResult = parseColors(this.colors, this.override); if (!isSettingsValid()) { this.status = Status.INVALID_SETTINGS; } // Set status to invalid if parsedColors returns a status, or if parsedColors returns an empty array if (typeof colorsResult === 'string' || (typeof colorsResult === 'object' && colorsResult.length === 0)) { this.status = this.status === Status.INVALID_SETTINGS ? Status.INVALID_COLORS_AND_SETTINGS : Status.INVALID_COLORS; } if (typeof colorsResult === 'object') { this.colors = colorsResult; } if (this.colors.length <= 1 && this.gradient) this.status = Status.INVALID_GRADIENT; } render() { const paletteStyles = { // Set default corner style '--palette-corners': this.pluginSettings.corners ? '5px' : '0px', '--palette-height': `${this.height}px`, '--palette-width': `${this.getPaletteWidth()}px`, '--palette-column-flex-basis': `${(this.height / this.colors.length / 2)}px` }; const invalidStyles = { animation: this.pulse ? `pulse ${this.pluginSettings.noticeDuration / 1000 / 2}s infinite` : '' }; const paletteClasses = { "palette-scroll": this.width > defaultSettings.width, }; return html ` <div id="palette" class=${classMap(paletteClasses)} style=${this.status === Status.VALID ? styleMap(paletteStyles) : styleMap(invalidStyles)} > ${this.createPalette()} </div> `; } /** * @returns `user` OR `auto` width based on which is more appropriate */ getPaletteWidth() { // Automatically set width if offset is less than settings width if (this.maxWidth < this.settings.width && this.maxWidth > 0) return this.maxWidth; // Set user-set width else return this.settings.width; } createColors() { return html ` ${repeat(this.colors, (color, index) => { let itemRef = (item) => { if (!(item instanceof ColorPaletteItem)) return; item.emitter.clear(); item.emitter.on('click', async (e) => await copyToClipboard(color.toUpperCase(), this.pluginSettings.copyFormat)); }; let itemEditRef = (item) => { if (!(item instanceof ColorPaletteItemEdit)) return; item.emitter.clear(); item.emitter.on('click', async (e) => await copyToClipboard(color.toUpperCase(), this.pluginSettings.copyFormat)); item.emitter.on('trash', (e) => { e.stopPropagation(); const deletedIndex = this.colors.indexOf(color); this.colors = this.colors.filter((_, index) => index !== deletedIndex); this.aliases = this.aliases.filter((_, index) => index !== deletedIndex); this.emitter.emit('changed', this.colors, this.settings); }); item.emitter.on('alias', (alias) => { // Get the index of the alias relative to the PaletteItem color const aliasIndex = this.colors.findIndex((val) => val === color); for (let i = 0; i < aliasIndex; i++) { // Set empty strings to empty indexes if (!this.aliases[i]) this.aliases[i] = ''; } // Set modified alias index this.aliases[aliasIndex] = alias; this.emitter.emit('changed', this.colors, this.settings); }); }; return !this.editMode ? html ` <color-palette-item color=${color} alias=${this.aliases?.[index] || ''} aliasMode=${this.pluginSettings.aliasMode} direction=${this.direction} ?editMode=${this.editMode} height=${this.height} ?preventHover=${this.preventHover} ?hideText=${this.hideText} colorCount=${this.colors.length} ${ref(itemRef)} > </color-palette-item> ` : html ` <color-palette-item-edit draggable=${true} color=${color} alias=${this.aliases?.[index] || ''} aliasMode=${this.pluginSettings.aliasMode} direction=${this.direction} ?editMode=${this.editMode} height=${this.height} ?preventHover=${this.preventHover} ?hideText=${this.hideText} colorCount=${this.colors.length} ?stabilizeWhileEditing=${this.pluginSettings.stabilizeWhileEditing} ${ref(itemEditRef)} > </color-palette-item-edit> `; })} `; } createGradient() { let tooltipRef = createRef(); let canvasRef = (canvas) => { if (!(canvas instanceof CanvasGradient)) return; canvas.emitter.clear(); canvas.emitter.on('click', async (color) => await copyToClipboard(color.toUpperCase(), this.pluginSettings.copyFormat)); canvas.emitter.on('move', (pos) => { if (!tooltipRef.value) return; let tooltip = tooltipRef.value; // Set tooltip text tooltip.text = canvas.getCanvasHex(pos.x, pos.y).toUpperCase(); // Set tooltip position tooltip.setClampedPosition(pos, this.getBoundingClientRect()); }); }; return html ` <tool-tip id="tooltip" trigger="hover" ?display=${!this.preventHover} ${ref(tooltipRef)} > <canvas-gradient .colors=${this.colors} height=${this.height} width=${this.getPaletteWidth()} direction=${this.direction} id="canvas" ${ref(canvasRef)} > </canvas-gradient> </tool-tip> `; } /** * Create invalid palette based on palette status * @param type Palette status type */ createInvalidPalette(type, message = '') { let defaultMessage = 'Invalid palette'; switch (type) { case Status.INVALID_COLORS: defaultMessage = 'Colors are defined incorrectly'; break; case Status.INVALID_SETTINGS: defaultMessage = 'Issues parsing settings'; break; case Status.INVALID_COLORS_AND_SETTINGS: defaultMessage = 'Colors and settings are defined incorrectly'; break; case Status.INVALID_GRADIENT: defaultMessage = 'Gradients require more than 1 color to display'; break; } this.emitter.emit('notice', `Palette:\n${message ? message : defaultMessage}`); return html ` <section id="invalid"> <span>${type}</span> </section> `; } }; ColorPalette.styles = [ css ` :host { display: block; width: fit-content; } :host([direction='row']) #palette { flex-direction: column; } /* Edit Mode */ :host([editMode]) #palette { overflow-x: auto; } // Invalid Palette :host(:not([status='Valid'])) #palette { } /* Palette Container */ #container { cursor: pointer; position: relative; background-color: #000; contain: paint; } #container.palette-scroll { overflow-x: auto; } /* Palette */ #palette { /* fallback vars */ --palette-height: 150px; --palette-width: 700px; --palette-background-color: transparent; --palette-color: #000; --palette-column-flex-basis: 80px; --palette-corners: 5px; display: flex; flex-direction: row; border-radius: var(--palette-corners); overflow: hidden; cursor: pointer; height: var(--palette-height); width: var(--palette-width); position: relative; scrollbar-width: thin; /* Drag & Drop */ & > .is-dragging { opacity: 0; } & > #invalid { position: absolute; display: flex; justify-content: center; align-items: center; height: var(--palette-height); width: var(--palette-width); background-color: #000000c0; border-radius: var(--palette-corners); & > span { display: flex; justify-content: center; align-items: center; color: white; background-color: #000; width: 100%; height: 30%; text-align: center; user-select: none; } } } @keyframes pulse { 50% { background-color: #FFFF0080; } } `, ]; __decorate([ query('#palette') ], ColorPalette.prototype, "palette", void 0); __decorate([ property({ type: Boolean }) ], ColorPalette.prototype, "editMode", null); __decorate([ property({ type: Array, reflect: true }) ], ColorPalette.prototype, "colors", void 0); __decorate([ property({ type: Number, reflect: true }) ], ColorPalette.prototype, "height", void 0); __decorate([ property({ type: Number, reflect: true }) ], ColorPalette.prototype, "width", void 0); __decorate([ property({ type: String, reflect: true }) ], ColorPalette.prototype, "direction", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], ColorPalette.prototype, "gradient", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], ColorPalette.prototype, "preventHover", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], ColorPalette.prototype, "hideText", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], ColorPalette.prototype, "override", void 0); __decorate([ property({ type: Array, reflect: true }) ], ColorPalette.prototype, "aliases", void 0); __decorate([ property({ type: Number }) ], ColorPalette.prototype, "maxWidth", void 0); __decorate([ state() ], ColorPalette.prototype, "status", void 0); __decorate([ state() ], ColorPalette.prototype, "pulse", void 0); __decorate([ property({ type: Object }) ], ColorPalette.prototype, "pluginSettings", void 0); ColorPalette = __decorate([ condCustomElement('color-palette') ], ColorPalette); export { ColorPalette };