TASK: #104293 - add homescreen function for storing apps' order

This commit is contained in:
luojiahao 2022-09-19 09:46:47 +08:00
parent b12d05000e
commit 0d64d0e603
14 changed files with 385 additions and 125 deletions

View File

@ -20,3 +20,4 @@
- add dock
- add function for dragging icon into dock
- add function for dragging icon into container
- add homescreen function for storing apps' order

View File

@ -23,6 +23,10 @@ export default class DockChild {
x: 0,
y: 0,
}
// 状态计时器
removed: number | undefined = undefined
added: number | undefined = undefined
constructor(_element: HTMLElement, manager: StarDock) {
this._element = _element
this.manager = manager
@ -82,6 +86,7 @@ export default class DockChild {
this._lastMasterWidth = this._master.offsetWidth
this._lastMasterHeight = this._master.offsetHeight
this.center.x = left + this._lastMasterWidth / 2
this.center.y = top + this._lastMasterHeight / 2
!this.container.classList.contains('dragging') &&
(container.style.transform = 'translate(' + left + 'px, ' + top + 'px)')
}

View File

@ -1,5 +1,5 @@
import {html, css, LitElement, CSSResultGroup} from 'lit'
import {customElement, query, state} from 'lit/decorators.js'
import {customElement, query} from 'lit/decorators.js'
import DockChild from './dock-child'
import customStyle from './style'
interface DragAndDrop {
@ -59,6 +59,7 @@ interface DragAndDrop {
const DND_MOVE_THROTTLE = 50
const DND_THRESHOLD = 5
const DEFAULT_DND_TIMEOUT = 300
const STATE_CHANGE_TIMEOUT = 100
@customElement('star-dock')
export default class StarDock extends LitElement {
@ -111,27 +112,40 @@ export default class StarDock extends LitElement {
},
}
appendContainerChild = (element: HTMLElement, options?: number) => {
appendContainerChild = (
element: HTMLElement,
order?: number,
callback?: Function
) => {
const child =
this.getChildByElement(element) || new DockChild(element, this)
this._children.push(child)
this.container.appendChild(child.master)
if (typeof order == 'number') {
child.order = order
}
this.changeState(child, 'added', callback)
}
removeContainerChild = (element: HTMLElement) => {
removeContainerChild = (element: HTMLElement, callback?: Function) => {
const target = this.getChildByElement(element)
if (!target) return null
this._children = this._children.filter((child) => child !== target)
this.changeState(target, 'removed', callback)
target.master.remove()
return target
}
realRemoveChild = (element: HTMLElement) => {}
realRemoveChild = (element: HTMLElement) => {
element
}
realInsertBefore = (element: HTMLElement, reference: HTMLElement) => {
element.style.order = reference.style.order
this.container.insertBefore(element, reference)
}
realInsertAfter = (element: HTMLElement, reference: HTMLElement) => {
element.style.order = reference.style.order
if (reference.nextSibling) {
this.container.insertBefore(element, reference.nextElementSibling)
} else {
@ -142,10 +156,14 @@ export default class StarDock extends LitElement {
synchronise() {
for (const child of this._children) {
child.master.style.order = String(child.order)
child.synchroniseContainer()
}
this.reorderChild()
if (this._children.length) {
this._gridSize = this._children[0].master.offsetWidth
}
}
reorderChild() {
@ -188,7 +206,6 @@ export default class StarDock extends LitElement {
handleEvent(event: TouchEvent) {
switch (event.type) {
case 'touchstart':
case 'click':
case 'mousedown':
if (this._dnd.active || this._dnd.timeout) {
break
@ -217,7 +234,10 @@ export default class StarDock extends LitElement {
// Find the child
let children = [...this._children]
for (let child of children) {
if (child.element === target || child.master === target) {
if (
child.master === target ||
child.master.compareDocumentPosition(target) & 16
) {
this._dnd.child = child
break
}
@ -276,6 +296,8 @@ export default class StarDock extends LitElement {
this._dnd.center.x + (this._dnd.last.pageX - this._dnd.start.pageX)
const centerY =
this._dnd.center.y + (this._dnd.last.pageY - this._dnd.start.pageY)
const gridHeight = this._dnd.child.master.offsetHeight
const gridWidth = this._dnd.child.master.offsetWidth
if (centerY < this.offsetTop) {
this.dropPosition = 'outter'
this.dispatchEvent(
@ -284,6 +306,8 @@ export default class StarDock extends LitElement {
target: this._dnd.child.element,
centerX,
centerY,
gridHeight,
gridWidth,
},
composed: true,
})
@ -313,7 +337,7 @@ export default class StarDock extends LitElement {
event.preventDefault()
}
this.endDrag(event)
this.endDrag()
break
case 'contextmenu':
@ -346,6 +370,8 @@ export default class StarDock extends LitElement {
element: HTMLElement | DockChild
centerX: number
centerY: number
gridHeight: number
gridWidth: number
}) => {
const {element, centerX, centerY} = info
const dropChild = this.getChildFromPoint(centerX, centerY)
@ -362,9 +388,9 @@ export default class StarDock extends LitElement {
child.container.style.transform =
'translate(' +
(centerX - (child.master.offsetWidth || this._gridSize) / 2) +
(centerX - this._gridSize / 2) +
'px, ' +
(centerY - (child.master.offsetHeight || this._gridSize) / 2) +
(centerY - this._gridSize / 2) +
'px)'
if (!dropChild) {
@ -391,6 +417,7 @@ export default class StarDock extends LitElement {
if (this.container.lastElementChild == this._dragChild!.master)
return true
this._dragChild!.order = this._children.length
this.container.appendChild(this._dragChild?.master!)
}
}
@ -503,18 +530,28 @@ export default class StarDock extends LitElement {
this._dnd.center.x + (this._dnd.last.pageX - this._dnd.start.pageX),
centerY:
this._dnd.center.y + (this._dnd.last.pageY - this._dnd.start.pageY),
gridHeight: this._dnd.child.master.offsetHeight,
gridWidth: this._dnd.child.master.offsetWidth,
})
}
endDrag = (evt: TouchEvent) => {
endDrag = () => {
if (this._dnd.active) {
const centerX =
this._dnd.center.x + (this._dnd.last.pageX - this._dnd.start.pageX)
const centerY =
this._dnd.center.y + (this._dnd.last.pageY - this._dnd.start.pageY)
const gridHeight = this._dnd.child.master.offsetHeight
const gridWidth = this._dnd.child.master.offsetWidth
this.dispatchEvent(
new CustomEvent('dock-drag-end', {
detail: {target: this._dnd.child.element, centerX, centerY},
detail: {
target: this._dnd.child.element,
centerX,
centerY,
gridHeight,
gridWidth,
},
composed: true,
})
)
@ -534,7 +571,9 @@ export default class StarDock extends LitElement {
/**
*
*/
getElementInfo = (element: HTMLElement) => {}
getElementInfo = (element: HTMLElement) => {
element
}
/**
*
@ -561,8 +600,6 @@ export default class StarDock extends LitElement {
}
protected firstUpdated(): void {
this._gridSize = 100
this.container.addEventListener('touchstart', this)
this.container.addEventListener('click', this)
this.container.addEventListener('touchmove', this)
@ -577,6 +614,57 @@ export default class StarDock extends LitElement {
this._containerOffset.left = this.container.offsetLeft
}
changeState(
child: DockChild,
state: 'added' | 'removed',
callback?: Function
) {
// Check that the child is still attached to this parent (can happen if
// the child is removed while frozen).
if (
(child.master.parentElement as HTMLElement).parentNode !== this.shadowRoot
) {
return
}
// Check for a redundant state change.
if (child.container.classList.contains(state)) {
return
}
let animStart = (e: AnimationEvent) => {
if (!e.animationName.endsWith(state)) {
return
}
child.container.removeEventListener('animationstart', animStart)
window.clearTimeout(child[state])
delete child[state]
// let self = this;
child.container.addEventListener('animationend', function animEnd() {
child.container.removeEventListener('animationend', animEnd)
child.container.classList.remove(state)
if (callback) {
callback()
}
})
}
child.container.addEventListener('animationstart', animStart)
child.container.classList.add(state)
child[state] = window.setTimeout(() => {
delete child[state]
child.container.removeEventListener('animationstart', animStart)
child.container.classList.remove(state)
if (callback) {
callback()
}
}, STATE_CHANGE_TIMEOUT)
}
protected render() {
return html`
<div id="container"></div>
@ -589,24 +677,25 @@ export default class StarDock extends LitElement {
// position: relative;
display: flex;
flex: 1;
margin-bottom: var(--dock-margin-bottom, 60px);
margin-bottom: var(--dock-margin-bottom);
width: 100%;
height: var(--dock-height, 92px);
height: var(--dock-container-height);
}
#container {
display: flex;
justify-content: center;
margin: auto auto 0;
min-width: var(--dock-min-width, 760px);
min-width: var(--dock-min-width, 100%);
max-width: 100%;
height: var(--dock-height, 92px);
height: var(--dock-container-height);
background-color: rgba(1, 1, 1, 0.5);
}
.dock-child-master {
max-width: var(--dock-grid-width, 92px);
max-height: var(--dock-grid-width, 92px);
flex: 1;
}
@ -615,8 +704,10 @@ export default class StarDock extends LitElement {
top: 0;
left: 0;
z-index: 0;
width: var(--icon-size);
height: var(--icon-size);
}
.dock-child-container:not(.dragging) {
.dock-child-container:not(.dragging):not(.added) {
transition: transform 0.2s;
}
.dock-child-container.dragging {

View File

@ -7,9 +7,10 @@ export default css`
/** dock 与屏幕底部距离 */
--dock-margin-bottom: var(--dock-bottom, 30px);
/** dock 最小宽度 */
--dock-min-width: 760px;
// --dock-min-width: 760px;
--dock-min-width: 100%;
/** dock 高度 */
--dock-height: 140px;
--dock-container-height: var(--dock-height, 140px);
/** dock 图标宽度 */
--dock-grid-width: var(--icon-size, 100px);
}

View File

@ -55,12 +55,12 @@ const DND_THRESHOLD = 5
/**
*
*/
const MERGE_FORDER_TIME = 700
// const MERGE_FORDER_TIME = 700
/**
*
*/
const FORDER_OPERATE_TIME = 500
// const FORDER_OPERATE_TIME = 500
/**
*
@ -91,9 +91,9 @@ const cubic_bezier = (
)
}
@customElement('star-container')
@customElement('gaia-container')
class GaiaContainer extends LitElement {
name: string = 'star-container'
name: string = 'gaia-container'
row: number = 6
column: number = 4
_frozen: boolean = false
@ -275,7 +275,9 @@ class GaiaContainer extends LitElement {
}
}
}
window.addEventListener('resize', this.changeLayout)
window.addEventListener('resize', () => {
this.firstUpdated().then(this.changeLayout)
})
this.dndObserver = new MutationObserver(dndObserverCallback)
dndObserverCallback()
@ -354,7 +356,7 @@ class GaiaContainer extends LitElement {
// 同时触发划动时,不同触发原因的优先级顺序,越后越优先
typeIndex = ['reset', 'swipe', 'mouseup', 'touchend']
// 划动计时器
timer: number | undefined = undefined
timer: Function | undefined = undefined
// 触发划动的原因类型
slideType: string = ''
@ -362,20 +364,29 @@ class GaiaContainer extends LitElement {
if (
!element ||
this.typeIndex.indexOf(this.slideType) > this.typeIndex.indexOf(type)
)
) {
return
}
if (this.timer) {
clearInterval(this.timer)
this.slideType = ''
}
const targetPagination = this.pages.indexOf(element)
const animateTime = 450
const target = -element.offsetLeft
const origin = this.offsetX
// 移动总距离
const distance = target - origin
const startTime = new Date().getTime()
this.dispatchEvent(
new CustomEvent('pagination-change', {
detail: targetPagination,
composed: true,
})
)
this.slideType = type
this.timer = setInterval(() => {
this.timer = () => {
requestAnimationFrame(() => {
const cur = new Date().getTime()
const t = (cur - startTime) / animateTime
@ -394,7 +405,7 @@ class GaiaContainer extends LitElement {
this._dnd.top +
'px)'
// 控制与文件夹交互时的拖拽操作
const child = this._dnd.child as unknown as GaiaContainerChild
// const child = this._dnd.child as unknown as GaiaContainerChild
// if (child) {
// child.container.style.setProperty(
// '--offset-position-left',
@ -408,7 +419,7 @@ class GaiaContainer extends LitElement {
}
if (ratio >= 1) {
clearInterval(this.timer)
// clearInterval(this.timer)
this.timer = undefined
this.slideType = ''
this.offsetX = target
@ -420,9 +431,12 @@ class GaiaContainer extends LitElement {
this.status &= ~STATUS.SWIPE
// 如果处于拖拽状态则更新一次落点位置
this.continueDrag()
} else if (this.timer) {
this.timer()
}
})
}, 10)
}
this.timer()
}
destroyFolder = (evt: Event) => {
@ -439,6 +453,12 @@ class GaiaContainer extends LitElement {
return this.shadowRoot && this.shadowRoot.querySelector('style')
}
get elements() {
return this._children.map((child) => {
return child.element
})
}
get offsetX() {
return this._offsetX
}
@ -595,6 +615,7 @@ class GaiaContainer extends LitElement {
getFolderGridIdByCoordinate(x: number, y: number, pagination: number = 0) {
if (!this.openedFolder || !this.openedFolder._status) return -1
let page = this.pages[this.openedFolder.pagination]
pagination
x += page.scrollLeft - page.offsetLeft - this.openedFolder._lastMasterLeft!
y += page.scrollTop - page.offsetTop - this.openedFolder._lastMasterTop!
@ -663,7 +684,6 @@ class GaiaContainer extends LitElement {
}
} else {
// 不是拖拽中的元素,放入下一页
this.realRemoveChild(element)
this.realAppendChild(++pagination, element)
}
}
@ -734,7 +754,7 @@ class GaiaContainer extends LitElement {
// 越界后重新拿出来放入下一个页面
if (!this._dnd.child && pagination) {
// 非拖拽元素导致的越界,后插元素直接加入下一个页面
return this.realAppendChild(+pagination + 1, arguments[0])
return this.realAppendChild(+pagination + 1, args[0])
}
// 同时记录被后插元素为无法交换元素
@ -785,14 +805,8 @@ class GaiaContainer extends LitElement {
removeContainerChild(element: HTMLElement, callback?: Function) {
let children = this._children
let childToRemove: GaiaContainerChild | null = null
for (let child of children) {
if (child.element === element) {
childToRemove = child
break
}
}
let childToRemove: GaiaContainerChild | null =
this.getChildByElement(element)
if (childToRemove === null) {
throw 'removeChild called on unknown child'
@ -1012,11 +1026,9 @@ class GaiaContainer extends LitElement {
let referenceIndex = -1
if (reference !== null) {
for (
let i = 0, child = children[i], iLen = children.length;
i < iLen;
i++
) {
for (let i = 0, iLen = children.length; i < iLen; i++) {
const child = children[i]
if (child.element === reference) {
referenceIndex = i
break
@ -1036,12 +1048,11 @@ class GaiaContainer extends LitElement {
}
}
children.push(childToInsert)
if (folderName && this.folders[folderName]) {
// 属于文件夹内的图标
children.push(childToInsert)
this.folders[folderName].addAppIcon(childToInsert.master)
} else if (referenceIndex === -1) {
children.push(childToInsert)
this.realAppendChild(pagination ?? 0, childToInsert.master)
} else {
const referenceNode = children[referenceIndex].folderName
@ -1053,21 +1064,25 @@ class GaiaContainer extends LitElement {
if (folderName && !this.folders[folderName]) {
// 有文件夹名但不存在该文件夹,则将该图标转化为文件夹
this.appToFolder(childToInsert.master)
// this.appToFolder(childToInsert.master)
}
childToInsert.isWidget && (childToInsert.isStatic = true)
this.changeState(childToInsert, 'added', callback)
// this.synchronise()
childToInsert.synchroniseContainer()
const setGridId = () => {
if (anchorCoordinate) {
childToInsert.anchor()
} else {
childToInsert.gridId = this.getGridIdByCoordinate(
childToInsert._lastMasterLeft!,
childToInsert._lastMasterTop!,
childToInsert.master.offsetLeft,
childToInsert.master.offsetTop,
childToInsert.pagination
)
}
childToInsert.synchroniseContainer()
}
if (this.ready) {
setGridId()
} else {
@ -1190,7 +1205,8 @@ class GaiaContainer extends LitElement {
// TODO还需要考虑跨文件夹移动
if (
typeof child?.pagination == 'number' &&
child.pagination !== this._dnd.child.pagination
child.pagination !== this._dnd.child.pagination &&
this._dnd.child
) {
// 当被选中元素与被移动元素页码不一致时,+
this._dnd.isSpanning = true
@ -1282,7 +1298,7 @@ class GaiaContainer extends LitElement {
this._dnd.child.container.classList.add('dragging')
// this._dnd.child.isStatic = false
// this._dnd.child.container.style.position = "fixed";
let rect = this.getBoundingClientRect()
// let rect = this.getBoundingClientRect()
// this._dnd.child.container.style.top = rect.top + 'px'
// this._dnd.child.container.style.left = rect.left - this.offsetX + 'px'
this._dnd.pagination = this.pagination
@ -1375,28 +1391,24 @@ class GaiaContainer extends LitElement {
const distanceY =
gridY ??
this._dnd.gridPosition.y + this._dnd.last.pageY - this._dnd.start.pageY
let {dropTarget, dropChild, isPage, pagination, gridId} =
this.getChildFromPoint(distanceX, distanceY)
let {dropTarget, dropChild, isPage, gridId} = this.getChildFromPoint(
distanceX,
distanceY
)
const child = this._dnd.child
let dropStatus = false
// while (gridId < 0) {
// gridId += this.column
// }
// while (gridId > 23) {
// gridId -= this.column
// }
if (
this._staticElements.includes(dropTarget) &&
(child !== dropChild || (gridId == dropChild.gridId && mode == 'delay'))
) {
// @ts-ignore
return
}
this._dnd.dropTarget = dropTarget
// 拖拽元素悬浮页面默认为当前页面
const suspendingPage = isPage ? dropTarget! : this.pages[this.pagination]
// const suspendingPage = isPage ? dropTarget! : this.pages[this.pagination]
if (child.isTail && isPage) {
if (this._dnd.lastDropChild) {
this._dnd.lastDropChild.master.classList.remove('merging')
@ -1624,6 +1636,8 @@ class GaiaContainer extends LitElement {
centerY: number
row: number
column: number
gridHeight: number
gridWidth: number
}
) {
this.dragInType = DragInType.PAGES
@ -1646,20 +1660,13 @@ class GaiaContainer extends LitElement {
this._dnd.child.container.classList.add('dragging')
}
centerX =
centerX -
(this._dnd.child.column / 2 + 0.25) * this.gridWidth +
this.left +
page.offsetLeft
centerY =
centerY -
(this._dnd.child.row / 2 + 0.5) * this.gridHeight +
this.top +
page.offsetTop
gridX = centerX + this.gridWidth / 2
gridY = centerY + this.gridHeight / 2
let containerX = centerX - this.gridWidth / 2 - this.left
let containerY = centerY - this.gridHeight / 2 - this.top
this._dnd.child.container.style.transform = `translate(${centerX}px, ${centerY}px)`
gridX = containerX + this.gridWidth / 2
gridY = containerY + this.gridHeight / 2
this._dnd.child.container.style.transform = `translate(${containerX}px, ${containerY}px)`
}
this.dropElement(mode, true, gridX, gridY)
}
@ -1772,6 +1779,17 @@ class GaiaContainer extends LitElement {
endDrag(event: Event) {
if (this._dnd.active) {
const centerX =
this.left +
this._dnd.center.x +
(this._dnd.last.pageX - this._dnd.start.pageX)
const centerY =
this.top +
this._dnd.center.y +
(this._dnd.last.pageY - this._dnd.start.pageY)
const elementHeight = this._dnd.child.element.offsetHeight
const elementWidth = this._dnd.child.element.offsetWidth
this.dropElement('immediately')
this.dispatchEvent(
new CustomEvent('drag-end', {
@ -1783,13 +1801,24 @@ class GaiaContainer extends LitElement {
pageY: this._dnd.last.pageY,
clientX: this._dnd.last.clientX,
clientY: this._dnd.last.clientY,
centerX,
centerY,
gridHeight: elementHeight,
gridWidth: elementWidth,
},
composed: true,
})
)
} else if (this._dnd.timeout !== null && this._dnd.child?.isFolder) {
this._dnd.child.open()
} else if (this._dnd.timeout !== null && this._dnd.child?.element) {
} else if (
!(
this.status & // 不处于四种状态的任何一种时,才可以发送 activate 事件
(STATUS.DRAG | STATUS.SWIPE | STATUS.TURN | STATUS.SORT)
) &&
this._dnd.timeout !== null &&
this._dnd.child?.element
) {
let handled = !this.dispatchEvent(
new CustomEvent('activate', {
cancelable: true,
@ -1834,7 +1863,6 @@ class GaiaContainer extends LitElement {
case 'mousedown':
this.istouching = true
if (this.timer) {
clearInterval(this.timer)
this.timer = undefined
this.slideType = ''
}
@ -1944,15 +1972,17 @@ class GaiaContainer extends LitElement {
}
if (this._dnd.child?.priority == 1 && !this._dnd.child.isFolder) {
const {top, left} = this.getBoundingClientRect()
const centerX =
left +
this.left +
this._dnd.center.x +
(this._dnd.last.pageX - this._dnd.start.pageX)
const centerY =
top +
this.top +
this._dnd.center.y +
(this._dnd.last.pageY - this._dnd.start.pageY)
const elementHeight = this._dnd.child.element.offsetHeight
const elementWidth = this._dnd.child.element.offsetWidth
if (centerY > this.offsetHeight) {
this.dragPosition = 'outter'
if (!this.throttle) {
@ -1961,8 +1991,8 @@ class GaiaContainer extends LitElement {
new CustomEvent('out-of-container', {
detail: {
element: this._dnd.child?.element,
gridHeight: this.gridHeight,
gridWidth: this.gridWidth,
gridHeight: elementHeight,
gridWidth: elementWidth,
centerX: centerX,
centerY: centerY,
},
@ -2181,7 +2211,7 @@ class GaiaContainer extends LitElement {
declare global {
interface HTMLElementTagNameMap {
'star-container': GaiaContainer
'gaia-container': GaiaContainer
}
}

View File

@ -333,7 +333,7 @@ export default class ExchangeStrategy {
if (gridId === undefined) {
ergodic(-1 * direction)
}
return gridId !== undefined ? [[gridId]] : gridId
return gridId !== undefined ? [[gridId]] : undefined
}
const place = () => {
@ -529,6 +529,7 @@ export default class ExchangeStrategy {
placedRecorder?: PlacedRecorder
childCoordinate?: GaiaContainer['childCoordinate']
} {
forceDrop
const {column: mColumn} = this.manager
// 子节点右下角单元格所在的网格ID
const edge_bottom_right =
@ -546,7 +547,7 @@ export default class ExchangeStrategy {
this.placedRecorder = {}
this.placedChild.add(child)
this.pickChild(child, recorder)
const dropChild = recorder[gridId]
// const dropChild = recorder[gridId]
// 获取被挤占位置的元素,按照优先级排序
const pickChildren = this.getChildrenByGridArea(
gridId,

View File

@ -32,7 +32,7 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
gridWidth: number = 0
constructor(manager: GaiaContainer, name?: string) {
super(null, 1, 1, undefined, manager)
super(undefined, 1, 1, undefined, manager)
this.name = this.checkAndGetFolderName(name)
this._id = `folder-${new Date().getTime()}`
this.init()
@ -375,6 +375,7 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
handleEvent(evt: TouchEvent) {
switch (evt.type) {
// @ts-ignore
case 'touchend':
if (this._status && evt.target === this.container) {
this.close()

View File

@ -66,6 +66,7 @@ class GaiaContainerPage {
div.className = `gaia-container-page`
div.style.setProperty('--pagination', pagination)
// 影子页面,用于检测旋转屏幕后能否仍容纳当前页面的子节点
const shadowPage = div.cloneNode() as HTMLElement
shadowPage.classList.add('shadow')
this._shadowPagesMap.set(div, shadowPage)
@ -75,6 +76,14 @@ class GaiaContainerPage {
this.observe(div)
this._manager.childCoordinate[this._pages.length - 1] = {}
this._manager.dispatchEvent(
new CustomEvent('page-change', {
detail: {
addIndex: this._pages.indexOf(div),
},
composed: true,
})
)
return {
page: div,
shadowPage,
@ -103,6 +112,14 @@ class GaiaContainerPage {
return
}
delete this._pages[index]
this._manager.dispatchEvent(
new CustomEvent('page-change', {
detail: {
deleteIndex: index,
},
composed: true,
})
)
if (index > -1) {
page?.remove?.()
let flag = false
@ -130,6 +147,10 @@ class GaiaContainerPage {
callback(this._pages[i], i, this._pages)
}
}
indexOf = (page: HTMLElement) => {
return this._pages.indexOf(page)
}
}
export default GaiaContainerPage

View File

@ -2,7 +2,5 @@ import {css} from 'lit'
export default css`
:host {
/* 图标大小 */
--icon-size: 92px;
}
`

View File

@ -1,17 +1,20 @@
import {html, css, LitElement, TemplateResult} from 'lit'
import {customElement, property, query, state} from 'lit/decorators.js'
import {html, css, LitElement} from 'lit'
import {customElement, query, state} from 'lit/decorators.js'
import GaiaContainer from '../../../components/grid-container/container'
import GaiaContainerChild from '../../../components/grid-container/gaia-container-child'
import homescreenStyle from './homescreen-style'
import StarDock from '../../../components/dock/dock'
import {IndicatorPagePoint} from '../../../components/indicator/indicator-page-point'
import './icon'
import SiteIcon from './icon'
@customElement('panel-homescreen')
export class PanelContainer extends LitElement {
container!: GaiaContainer
dock!: StarDock
icons: {[prop: string]: GaiaContainerChild} = {}
pageIndicator!: IndicatorPagePoint
@query('.reset') resetBtn!: HTMLElement
@query('#row') rowInput!: HTMLInputElement
@query('#column') columnInput!: HTMLInputElement
@ -59,32 +62,71 @@ export class PanelContainer extends LitElement {
this.shadowRoot?.appendChild(this.container)
this.container.sortMode = true
// 页面指示器
this.pageIndicator = new IndicatorPagePoint()
this.pageIndicator.edit = true
this.shadowRoot?.appendChild(this.pageIndicator)
// dock
this.dock = new StarDock()
this.shadowRoot?.appendChild(this.dock)
this.dock.sortMode = true
//
// 存储全局变量
;(window as any).dock = this.dock
;(window as any).container = this.container
;(window as any).home = this
// this.addAppIcon(2, 2)
// container 相关事件
this.addEventListener('out-of-container', this)
this.addEventListener('drag-return-container', this)
this.addEventListener('out-of-dock', this)
this.addEventListener('drag-start', this)
this.addEventListener('drag-move', this)
this.addEventListener('drag-end', this)
this.addEventListener('drag-and-drop', this)
this.addEventListener('page-change', this)
this.addEventListener('pagination-change', this)
// dock 相关事件
this.addEventListener('out-of-dock', this)
this.addEventListener('dock-drag-start', this)
this.addEventListener('dock-drag-move', this)
this.addEventListener('dock-drag-end', this)
this.parentElement!.addEventListener('animationend', () => {
this.container.changeLayout()
let containerOrderString = localStorage.getItem('containerOrder')
let dockOrderString = localStorage.getItem('dockOrder')
if (containerOrderString && dockOrderString) {
let containerOrder: {[prop: string]: any}[] =
JSON.parse(containerOrderString)
let dockOrder: {[prop: string]: any}[] = JSON.parse(dockOrderString)
containerOrder.forEach((record) => {
const icon = document.createElement('site-icon')
icon.setAttribute('color', record.color)
icon.name = record.name
this.container.appendContainerChild(icon, {
row: record.row,
column: record.column,
anchorCoordinate: record.anchorCoordinate,
pagination: record.pagination,
folderName: record.folderName,
})
})
dockOrder.forEach((record) => {
const icon = document.createElement('site-icon')
icon.setAttribute('color', record.color)
icon.name = record.name
this.dock.appendContainerChild(icon, record.order)
})
} else {
this.addAppIcon(1, 1)
let promise = new Promise((res) => {
res(undefined)
@ -97,7 +139,9 @@ export class PanelContainer extends LitElement {
})
})
}
}
this.container.synchronise()
this.dock.synchronise()
})
}
@ -111,11 +155,15 @@ export class PanelContainer extends LitElement {
handleEvent(evt: CustomEvent) {
switch (evt.type) {
case 'out-of-container':
// @ts-ignore
if (!this.prepareToTransfer) return
const outPage = this.container.pages[this.container.pagination]
this.dragInDock = this.dock.dragIn({
element: this.elementPlaceholder,
centerX: evt.detail.centerX,
centerY: evt.detail.centerY,
centerX: evt.detail.centerX - outPage.offsetLeft,
centerY: evt.detail.centerY - outPage.offsetTop,
gridHeight: evt.detail.gridHeight,
gridWidth: evt.detail.gridWidth,
})
break
case 'drag-return-container':
@ -125,14 +173,17 @@ export class PanelContainer extends LitElement {
case 'out-of-dock':
this.prepareToTransfer = true
const {target, centerX, centerY} = evt.detail
const {centerX, centerY, gridHeight, gridWidth} = evt.detail
this.container._dnd.active = true
const page = this.container.pages[this.container.pagination]
this.container.dragIn('delay', {
element: this.elementPlaceholder,
centerX,
centerY,
centerX: centerX + page.offsetLeft,
centerY: centerY + page.offsetTop,
row: 1,
column: 1,
gridHeight,
gridWidth,
})
break
case 'drag-and-drop':
@ -158,24 +209,28 @@ export class PanelContainer extends LitElement {
this.container.removeContainerChild(evt.detail.target)
}
setTimeout(() => {
// this.dock.removeContainerChild(this.elementPlaceholder)
this.dock.synchronise()
this.container._dnd.active = false
}, 10)
break
case 'dock-drag-end':
// @ts-ignore
if (!this.prepareToTransfer) return
if (!this.dragInContainer) {
// 此时拖拽进 container 标志未显示为 true 也不意味着放置失败,
// 可能是尚未进行尝试,尝试立即放置拖拽中的图标
const {target, centerX, centerY} = evt.detail
const {centerX, centerY, gridHeight, gridWidth} = evt.detail
const page = this.container.pages[this.container.pagination]
this.container.dragIn('immediately', {
element: this.elementPlaceholder,
centerX,
centerY,
centerX: centerX + page.offsetLeft,
centerY: centerY + page.offsetTop,
row: 1,
column: 1,
gridHeight,
gridWidth,
})
}
@ -186,7 +241,6 @@ export class PanelContainer extends LitElement {
this.container._dnd.child.container.classList.remove('dragging')
this.container._dnd.child = undefined
this.container.synchronise()
this.container._dnd.active = false
}
this.dock.removeContainerChild(evt.detail.target)
@ -196,14 +250,57 @@ export class PanelContainer extends LitElement {
this.dragInContainer = false
this.prepareToTransfer = false
this.container._dnd.child = null
this.container._dnd.active = false
this.dock.synchronise()
break
case 'page-change':
this.pageIndicator.total = this.container.pages.length
this.pageIndicator.index = this.container.pagination + 1
break
case 'pagination-change':
this.pageIndicator.index = this.container.pagination + 1
break
}
return false
}
@state() test!: string
storeAppOrder() {
let containerOrders: {[prop: string]: any} = []
let dockOrders: {[prop: string]: any} = []
let containerChildren = this.container._children
let dockChildren = this.dock._children
containerChildren.forEach((child) => {
const info = {
name: (child.element as SiteIcon).name,
color: (child.element as SiteIcon).color,
row: child.row,
column: child.column,
pagination: child.pagination,
anchorCoordinate: child.anchorCoordinate,
forderName: child.folderName,
}
containerOrders.push(info)
})
dockChildren.forEach((child) => {
const info = {
name: (child.element as SiteIcon).name,
color: (child.element as SiteIcon).color,
order: child.order,
}
dockOrders.push(info)
})
localStorage.setItem('containerOrder', JSON.stringify(containerOrders))
localStorage.setItem('dockOrder', JSON.stringify(dockOrders))
}
@state()
protected styles: {[subModule: string]: string} = new Proxy(
{},
@ -258,8 +355,9 @@ export class PanelContainer extends LitElement {
--container-margin-left: 20px;
/** 控制 dock 距离屏幕底部的距离 */
--dock-bottom: 10px;
--icon-size: 92px;
}
star-container {
gaia-container {
height: 80vh;
width: 100vw;
}
@ -299,6 +397,17 @@ export class PanelContainer extends LitElement {
border: 1px solid black;
box-sizing: border-box;
}
.gaia-container-child::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
display: block;
width: 4px;
height: 2px;
background-color: pink;
}
`,
homescreenStyle,
]

View File

@ -2,7 +2,7 @@ import {css} from 'lit'
export default css`
/* 图标归位时的动画 */
star-container:not(.loading)
gaia-container:not(.loading)
.gaia-container-child:not(.added):not(.dragging) {
transition: transform 0.2s;
}

View File

@ -15,8 +15,9 @@ export default css`
opacity: 0;
}
:host() #subtitle {
#subtitle {
transition: opacity 0.2s;
display: none;
}
#image-container {

View File

@ -1,4 +1,4 @@
import {html, css, LitElement} from 'lit'
import {html, LitElement} from 'lit'
import {customElement, property, query} from 'lit/decorators.js'
import style from './icon-style'

View File

@ -19,6 +19,7 @@ export class PanelIndicators extends LitElement {
@queryAll('.myindicator') private myindicators!: IndicatorPagePoint
handleEvent(evt: Event) {
this.myindicators
switch (evt.type) {
case 'click':
evt.preventDefault() // iOS上不禁止按钮默认行为将会双击按钮强制放缩屏幕