After Width: | Height: | Size: 209 B |
After Width: | Height: | Size: 127 B |
After Width: | Height: | Size: 127 B |
After Width: | Height: | Size: 568 B |
After Width: | Height: | Size: 411 B |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 428 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 326 B |
After Width: | Height: | Size: 221 B |
After Width: | Height: | Size: 399 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 394 B |
After Width: | Height: | Size: 725 B |
After Width: | Height: | Size: 349 B |
After Width: | Height: | Size: 613 B |
After Width: | Height: | Size: 167 B |
After Width: | Height: | Size: 385 B |
After Width: | Height: | Size: 393 B |
After Width: | Height: | Size: 195 B |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 294 B |
After Width: | Height: | Size: 282 B |
After Width: | Height: | Size: 389 B |
After Width: | Height: | Size: 885 B |
After Width: | Height: | Size: 109 B |
After Width: | Height: | Size: 116 B |
After Width: | Height: | Size: 336 B |
After Width: | Height: | Size: 398 B |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 635 B |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 165 B |
After Width: | Height: | Size: 148 B |
After Width: | Height: | Size: 532 B |
After Width: | Height: | Size: 599 B |
After Width: | Height: | Size: 321 B |
After Width: | Height: | Size: 453 B |
After Width: | Height: | Size: 369 B |
After Width: | Height: | Size: 240 B |
After Width: | Height: | Size: 190 B |
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 216 B |
After Width: | Height: | Size: 367 B |
After Width: | Height: | Size: 334 B |
After Width: | Height: | Size: 168 B |
After Width: | Height: | Size: 370 B |
After Width: | Height: | Size: 319 B |
After Width: | Height: | Size: 299 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 637 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 345 B |
After Width: | Height: | Size: 336 B |
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 170 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 602 B |
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 170 KiB |
After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 237 KiB |
After Width: | Height: | Size: 236 KiB |
After Width: | Height: | Size: 235 KiB |
After Width: | Height: | Size: 237 KiB |
After Width: | Height: | Size: 239 KiB |
After Width: | Height: | Size: 242 KiB |
After Width: | Height: | Size: 246 KiB |
After Width: | Height: | Size: 248 KiB |
After Width: | Height: | Size: 248 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 245 KiB |
After Width: | Height: | Size: 246 KiB |
After Width: | Height: | Size: 334 KiB |
After Width: | Height: | Size: 340 KiB |
After Width: | Height: | Size: 327 KiB |
After Width: | Height: | Size: 250 KiB |
After Width: | Height: | Size: 815 KiB |
After Width: | Height: | Size: 331 KiB |
After Width: | Height: | Size: 330 KiB |
After Width: | Height: | Size: 252 KiB |
After Width: | Height: | Size: 253 KiB |
After Width: | Height: | Size: 248 KiB |
After Width: | Height: | Size: 253 KiB |
@ -0,0 +1,171 @@ |
|||
import { EditorComponent, EDITOR_COMPONENT } from '../../editor' |
|||
import './dialog.css' |
|||
|
|||
export interface IDialogData { |
|||
type: string |
|||
label?: string |
|||
name: string |
|||
value?: string |
|||
options?: { label: string; value: string }[] |
|||
placeholder?: string |
|||
width?: number |
|||
height?: number |
|||
required?: boolean |
|||
} |
|||
|
|||
export interface IDialogConfirm { |
|||
name: string |
|||
value: string |
|||
} |
|||
|
|||
export interface IDialogOptions { |
|||
onClose?: () => void |
|||
onCancel?: () => void |
|||
onConfirm?: (payload: IDialogConfirm[]) => void |
|||
title: string |
|||
data: IDialogData[] |
|||
} |
|||
|
|||
export class Dialog { |
|||
private options: IDialogOptions |
|||
private mask: HTMLDivElement | null |
|||
private container: HTMLDivElement | null |
|||
private inputList: ( |
|||
| HTMLInputElement |
|||
| HTMLTextAreaElement |
|||
| HTMLSelectElement |
|||
)[] |
|||
|
|||
constructor(options: IDialogOptions) { |
|||
this.options = options |
|||
this.mask = null |
|||
this.container = null |
|||
this.inputList = [] |
|||
this._render() |
|||
} |
|||
|
|||
private _render() { |
|||
const { title, data, onClose, onCancel, onConfirm } = this.options |
|||
// 渲染遮罩层
|
|||
const mask = document.createElement('div') |
|||
mask.classList.add('dialog-mask') |
|||
mask.setAttribute(EDITOR_COMPONENT, EditorComponent.COMPONENT) |
|||
document.body.append(mask) |
|||
// 渲染容器
|
|||
const container = document.createElement('div') |
|||
container.classList.add('dialog-container') |
|||
container.setAttribute(EDITOR_COMPONENT, EditorComponent.COMPONENT) |
|||
// 弹窗
|
|||
const dialogContainer = document.createElement('div') |
|||
dialogContainer.classList.add('dialog') |
|||
container.append(dialogContainer) |
|||
// 标题容器
|
|||
const titleContainer = document.createElement('div') |
|||
titleContainer.classList.add('dialog-title') |
|||
// 标题&关闭按钮
|
|||
const titleSpan = document.createElement('span') |
|||
titleSpan.append(document.createTextNode(title)) |
|||
const titleClose = document.createElement('i') |
|||
titleClose.onclick = () => { |
|||
if (onClose) { |
|||
onClose() |
|||
} |
|||
this._dispose() |
|||
} |
|||
titleContainer.append(titleSpan) |
|||
titleContainer.append(titleClose) |
|||
dialogContainer.append(titleContainer) |
|||
// 选项容器
|
|||
const optionContainer = document.createElement('div') |
|||
optionContainer.classList.add('dialog-option') |
|||
// 选项
|
|||
for (let i = 0; i < data.length; i++) { |
|||
const option = data[i] |
|||
const optionItemContainer = document.createElement('div') |
|||
optionItemContainer.classList.add('dialog-option__item') |
|||
// 选项名称
|
|||
if (option.label) { |
|||
const optionName = document.createElement('span') |
|||
optionName.append(document.createTextNode(option.label)) |
|||
optionItemContainer.append(optionName) |
|||
if (option.required) { |
|||
optionName.classList.add('dialog-option__item--require') |
|||
} |
|||
} |
|||
// 选项输入框
|
|||
let optionInput: |
|||
| HTMLInputElement |
|||
| HTMLTextAreaElement |
|||
| HTMLSelectElement |
|||
if (option.type === 'select') { |
|||
optionInput = document.createElement('select') |
|||
option.options?.forEach(item => { |
|||
const optionItem = document.createElement('option') |
|||
optionItem.value = item.value |
|||
optionItem.label = item.label |
|||
optionInput.append(optionItem) |
|||
}) |
|||
} else if (option.type === 'textarea') { |
|||
optionInput = document.createElement('textarea') |
|||
} else { |
|||
optionInput = document.createElement('input') |
|||
optionInput.type = option.type |
|||
} |
|||
if (option.width) { |
|||
optionInput.style.width = `${option.width}px` |
|||
} |
|||
if (option.height) { |
|||
optionInput.style.height = `${option.height}px` |
|||
} |
|||
optionInput.name = option.name |
|||
optionInput.value = option.value || '' |
|||
if (!(optionInput instanceof HTMLSelectElement)) { |
|||
optionInput.placeholder = option.placeholder || '' |
|||
} |
|||
optionItemContainer.append(optionInput) |
|||
optionContainer.append(optionItemContainer) |
|||
this.inputList.push(optionInput) |
|||
} |
|||
dialogContainer.append(optionContainer) |
|||
// 按钮容器
|
|||
const menuContainer = document.createElement('div') |
|||
menuContainer.classList.add('dialog-menu') |
|||
// 取消按钮
|
|||
const cancelBtn = document.createElement('button') |
|||
cancelBtn.classList.add('dialog-menu__cancel') |
|||
cancelBtn.append(document.createTextNode('取消')) |
|||
cancelBtn.type = 'button' |
|||
cancelBtn.onclick = () => { |
|||
if (onCancel) { |
|||
onCancel() |
|||
} |
|||
this._dispose() |
|||
} |
|||
menuContainer.append(cancelBtn) |
|||
// 确认按钮
|
|||
const confirmBtn = document.createElement('button') |
|||
confirmBtn.append(document.createTextNode('确定')) |
|||
confirmBtn.type = 'submit' |
|||
confirmBtn.onclick = () => { |
|||
if (onConfirm) { |
|||
const payload = this.inputList.map<IDialogConfirm>(input => ({ |
|||
name: input.name, |
|||
value: input.value |
|||
})) |
|||
onConfirm(payload) |
|||
} |
|||
this._dispose() |
|||
} |
|||
menuContainer.append(confirmBtn) |
|||
dialogContainer.append(menuContainer) |
|||
// 渲染
|
|||
document.body.append(container) |
|||
this.container = container |
|||
this.mask = mask |
|||
} |
|||
|
|||
private _dispose() { |
|||
this.mask?.remove() |
|||
this.container?.remove() |
|||
} |
|||
} |
@ -0,0 +1,131 @@ |
|||
.dialog-mask { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
opacity: .5; |
|||
background: #000000; |
|||
z-index: 99; |
|||
} |
|||
|
|||
.dialog-container { |
|||
position: fixed; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
overflow: auto; |
|||
z-index: 999; |
|||
margin: 0; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.dialog { |
|||
position: absolute; |
|||
padding: 0 30px 30px; |
|||
background: #ffffff; |
|||
box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%); |
|||
border: 1px solid #e2e6ed; |
|||
border-radius: 2px; |
|||
} |
|||
|
|||
.dialog-title { |
|||
position: relative; |
|||
border-bottom: 1px solid #e2e6ed; |
|||
margin-bottom: 30px; |
|||
height: 60px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.dialog-title i { |
|||
width: 16px; |
|||
height: 16px; |
|||
cursor: pointer; |
|||
display: inline-block; |
|||
background: url(../../assets/images/close.svg); |
|||
} |
|||
|
|||
.dialog-option__item { |
|||
margin-bottom: 18px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.dialog-option__item span { |
|||
margin-right: 12px; |
|||
font-size: 14px; |
|||
color: #3d4757; |
|||
position: relative; |
|||
} |
|||
|
|||
.dialog-option__item input, |
|||
.dialog-option__item textarea, |
|||
.dialog-option__item select { |
|||
width: 276px; |
|||
height: 30px; |
|||
border-radius: 2px; |
|||
border: 1px solid #d3d3d3; |
|||
min-height: 30px; |
|||
padding: 5px; |
|||
box-sizing: border-box; |
|||
outline: none; |
|||
appearance: none; |
|||
user-select: none; |
|||
font-family: inherit; |
|||
} |
|||
|
|||
.dialog-option__item input:focus, |
|||
.dialog-option__item textarea:focus { |
|||
border-color: #4991f2; |
|||
} |
|||
|
|||
.dialog-option__item--require::before { |
|||
content: "*"; |
|||
color: #f56c6c; |
|||
margin-right: 4px; |
|||
position: absolute; |
|||
left: -8px; |
|||
} |
|||
|
|||
.dialog-menu { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: flex-end; |
|||
} |
|||
|
|||
.dialog-menu button { |
|||
position: relative; |
|||
display: inline-block; |
|||
border: 1px solid #e2e6ed; |
|||
border-radius: 2px; |
|||
background: #ffffff; |
|||
line-height: 22px; |
|||
padding: 0 16px; |
|||
white-space: nowrap; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.dialog-menu button:hover { |
|||
background: rgba(25, 55, 88, .04); |
|||
} |
|||
|
|||
.dialog-menu__cancel { |
|||
margin-right: 16px; |
|||
} |
|||
|
|||
.dialog-menu button[type='submit'] { |
|||
color: #ffffff; |
|||
background: #4991f2; |
|||
border-color: #4991f2; |
|||
} |
|||
|
|||
.dialog-menu button[type='submit']:hover { |
|||
background: #5b9cf3; |
|||
border-color: #5b9cf3; |
|||
} |