|
| 1 | +import { IconButton } from '@material/mwc-icon-button'; |
| 2 | +import { ListItem } from '@material/mwc-list/mwc-list-item'; |
| 3 | +import { Menu } from '@material/mwc-menu'; |
| 4 | +import { |
| 5 | + css, |
| 6 | + customElement, |
| 7 | + html, |
| 8 | + LitElement, |
| 9 | + property, |
| 10 | + query, |
| 11 | + TemplateResult, |
| 12 | +} from 'lit-element'; |
| 13 | +import { newWizardEvent, SCLTag, tags, Wizard } from './foundation.js'; |
| 14 | +import { emptyWizard, wizards } from './wizards/wizard-library.js'; |
| 15 | + |
| 16 | +@customElement('editor-container') |
| 17 | +export class EditorContainer extends LitElement { |
| 18 | + @property({ type: String }) |
| 19 | + header = ''; |
| 20 | + @property({ type: Array }) |
| 21 | + addOptions: string[] = []; |
| 22 | + @property({ type: String }) |
| 23 | + level: 'high' | 'mid' | 'low' = 'mid'; |
| 24 | + @property({ type: String }) |
| 25 | + colorTheme: 'primary' | 'secondary' = 'primary'; |
| 26 | + @property({ type: Boolean }) |
| 27 | + highlighted = false; |
| 28 | + |
| 29 | + @property() |
| 30 | + addElementAction: (tagName: string) => Wizard | undefined = () => { |
| 31 | + return undefined; |
| 32 | + }; |
| 33 | + |
| 34 | + @query('mwc-icon-button[icon="playlist_add"]') addIcon?: IconButton; |
| 35 | + @query('#menu') addMenu!: Menu; |
| 36 | + @query('#header') headerContainer!: HTMLElement; |
| 37 | + @query('#more') moreVert?: IconButton; |
| 38 | + |
| 39 | + renderAddButtons(): TemplateResult[] { |
| 40 | + return this.addOptions.map( |
| 41 | + child => |
| 42 | + html`<mwc-list-item graphic="icon" value="${child}" |
| 43 | + ><span>${child}</span |
| 44 | + ><mwc-icon slot="graphic">playlist_add</mwc-icon></mwc-list-item |
| 45 | + >` |
| 46 | + ); |
| 47 | + } |
| 48 | + |
| 49 | + styleFabButtonTransform(): TemplateResult[] { |
| 50 | + let transform = 0; |
| 51 | + return Array.from(this.children).map((child, i) => { |
| 52 | + if (child.tagName === 'MWC-FAB') |
| 53 | + return html`#more:focus-within ~ ::slotted(mwc-fab:nth-child(${i + 1})) |
| 54 | + { transform: translate(0, ${++transform * 48}px); }`; |
| 55 | + return html``; |
| 56 | + }); |
| 57 | + } |
| 58 | + |
| 59 | + renderHeaderBody(): TemplateResult { |
| 60 | + return html`${this.addOptions.length |
| 61 | + ? html`<mwc-icon-button |
| 62 | + icon="playlist_add" |
| 63 | + @click=${() => (this.addMenu.open = true)} |
| 64 | + ></mwc-icon-button> |
| 65 | + <mwc-menu |
| 66 | + id="menu" |
| 67 | + corner="TOP_RIGHT" |
| 68 | + menuCorner="END" |
| 69 | + @selected=${(e: Event) => { |
| 70 | + const tagName = (<ListItem>(<Menu>e.target).selected).value; |
| 71 | + const wizard = this.addElementAction(tagName); |
| 72 | + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); |
| 73 | + }} |
| 74 | + >${this.renderAddButtons()} |
| 75 | + </mwc-menu>` |
| 76 | + : html``} |
| 77 | + ${Array.from(this.children).some(child => child.tagName === 'MWC-FAB') |
| 78 | + ? html`<mwc-icon-button id="more" icon="more_vert"></mwc-icon-button>` |
| 79 | + : html``}<slot |
| 80 | + ><style> |
| 81 | +
|
| 82 | + ${this.addOptions.length |
| 83 | + ? html`::slotted(mwc-fab) {right: 48px;}` |
| 84 | + : html`::slotted(mwc-fab) {right: 0px;}`} |
| 85 | + ${this.styleFabButtonTransform()} |
| 86 | + </style></slot |
| 87 | + >`; |
| 88 | + } |
| 89 | + |
| 90 | + renderLevel1(): TemplateResult { |
| 91 | + return html`<h1>${this.header} ${this.renderHeaderBody()}</h1>`; |
| 92 | + } |
| 93 | + |
| 94 | + renderLevel2(): TemplateResult { |
| 95 | + return html`<h2>${this.header} ${this.renderHeaderBody()}</h2>`; |
| 96 | + } |
| 97 | + |
| 98 | + renderLevel3(): TemplateResult { |
| 99 | + return html`<h3>${this.header} ${this.renderHeaderBody()}</h3>`; |
| 100 | + } |
| 101 | + |
| 102 | + renderHeader(): TemplateResult { |
| 103 | + return html`<div id="header"> |
| 104 | + ${this.level === 'high' |
| 105 | + ? this.renderLevel1() |
| 106 | + : this.level === 'mid' |
| 107 | + ? this.renderLevel2() |
| 108 | + : this.renderLevel3()} |
| 109 | + </div>`; |
| 110 | + } |
| 111 | + |
| 112 | + render(): TemplateResult { |
| 113 | + return html`<section |
| 114 | + class="container ${this.colorTheme} ${this.highlighted |
| 115 | + ? 'highlighted' |
| 116 | + : ''}" |
| 117 | + tabindex="0" |
| 118 | + > |
| 119 | + ${this.renderHeader()} |
| 120 | + </section>`; |
| 121 | + } |
| 122 | + |
| 123 | + async firstUpdated(): Promise<void> { |
| 124 | + await super.updateComplete; |
| 125 | + if (this.addMenu) this.addMenu.anchor = this.headerContainer; |
| 126 | + } |
| 127 | + |
| 128 | + static styles = css` |
| 129 | + :host(.moving) section { |
| 130 | + opacity: 0.3; |
| 131 | + } |
| 132 | +
|
| 133 | + .container { |
| 134 | + background-color: var(--mdc-theme-surface); |
| 135 | + transition: all 200ms linear; |
| 136 | + outline-style: solid; |
| 137 | + margin: 8px 12px 16px; |
| 138 | + opacity: 1; |
| 139 | + } |
| 140 | +
|
| 141 | + .container.primary { |
| 142 | + outline-color: var(--mdc-theme-primary); |
| 143 | + } |
| 144 | +
|
| 145 | + .container.secondary { |
| 146 | + outline-color: var(--mdc-theme-secondary); |
| 147 | + } |
| 148 | +
|
| 149 | + .highlighted { |
| 150 | + outline-width: 2px; |
| 151 | + } |
| 152 | +
|
| 153 | + .container:focus { |
| 154 | + box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), |
| 155 | + 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); |
| 156 | + } |
| 157 | +
|
| 158 | + .container:focus-within { |
| 159 | + outline-width: 2px; |
| 160 | + transition: all 250ms linear; |
| 161 | + } |
| 162 | +
|
| 163 | + .container:focus-within h1, |
| 164 | + .container:focus-within h2, |
| 165 | + .container:focus-within h3 { |
| 166 | + color: var(--mdc-theme-surface); |
| 167 | + transition: background-color 200ms linear; |
| 168 | + } |
| 169 | +
|
| 170 | + .container.primary:focus-within h1, |
| 171 | + .container.primary:focus-within h2, |
| 172 | + .container.primary:focus-within h3 { |
| 173 | + background-color: var(--mdc-theme-primary); |
| 174 | + } |
| 175 | +
|
| 176 | + .container.secondary:focus-within h1, |
| 177 | + .container.secondary:focus-within h2, |
| 178 | + .container.secondary:focus-within h3 { |
| 179 | + background-color: var(--mdc-theme-secondary); |
| 180 | + } |
| 181 | +
|
| 182 | + h1, |
| 183 | + h2, |
| 184 | + h3 { |
| 185 | + color: var(--mdc-theme-on-surface); |
| 186 | + font-family: 'Roboto', sans-serif; |
| 187 | + font-weight: 300; |
| 188 | + overflow: hidden; |
| 189 | + white-space: nowrap; |
| 190 | + text-overflow: ellipsis; |
| 191 | + margin: 0px; |
| 192 | + line-height: 48px; |
| 193 | + padding-left: 0.3em; |
| 194 | + transition: background-color 150ms linear; |
| 195 | + } |
| 196 | +
|
| 197 | + h1 > ::slotted(mwc-icon-button), |
| 198 | + h2 > ::slotted(mwc-icon-button), |
| 199 | + h3 > ::slotted(mwc-icon-button), |
| 200 | + h1 > ::slotted(abbr), |
| 201 | + h2 > ::slotted(abbr), |
| 202 | + h3 > ::slotted(abbr) { |
| 203 | + float: right; |
| 204 | + } |
| 205 | +
|
| 206 | + h1 > mwc-icon-button, |
| 207 | + h2 > mwc-icon-button, |
| 208 | + h3 > mwc-icon-button { |
| 209 | + float: right; |
| 210 | + } |
| 211 | +
|
| 212 | + #header { |
| 213 | + position: relative; |
| 214 | + } |
| 215 | +
|
| 216 | + abbr { |
| 217 | + text-decoration: none; |
| 218 | + border-bottom: none; |
| 219 | + } |
| 220 | +
|
| 221 | + ::slotted(mwc-fab) { |
| 222 | + color: var(--mdc-theme-on-surface); |
| 223 | + opacity: 0; |
| 224 | + position: absolute; |
| 225 | + pointer-events: none; |
| 226 | + z-index: 1; |
| 227 | + transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1), |
| 228 | + opacity 200ms linear; |
| 229 | + } |
| 230 | +
|
| 231 | + #more:focus-within ~ ::slotted(mwc-fab) { |
| 232 | + transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), |
| 233 | + opacity 250ms linear; |
| 234 | + pointer-events: auto; |
| 235 | + opacity: 1; |
| 236 | + } |
| 237 | + `; |
| 238 | +} |
0 commit comments