Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
quantum-blocks
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ui
quantum-blocks
Commits
2170c951
Commit
2170c951
authored
Mar 01, 2021
by
郝聪敏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feature: 项目拆分
parent
91acb5e9
Changes
34
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
57 additions
and
2490 deletions
+57
-2490
README.md
README.md
+2
-2
activity.ts
app/controller/activity.ts
+0
-8
router.ts
app/router.ts
+0
-1
favicon.ico
app/web/asset/images/favicon.ico
+0
-0
loading.gif
app/web/asset/images/loading.gif
+0
-0
logo.png
app/web/asset/images/logo.png
+0
-0
index.ts
app/web/component/layout/activity/index.ts
+0
-30
index.vue
app/web/component/layout/activity/index.vue
+0
-33
dev.config.ts
app/web/config/dev.config.ts
+3
-1
prod.config.ts
app/web/config/prod.config.ts
+1
-0
test.config.ts
app/web/config/test.config.ts
+1
-0
index.vue
app/web/lib/GuideCube/index.vue
+4
-4
config.ts
app/web/lib/config.ts
+0
-6
index.ts
app/web/page/activity/component/FreedomContainer/index.ts
+0
-27
index.vue
app/web/page/activity/component/FreedomContainer/index.vue
+0
-30
GridItem.vue
app/web/page/activity/component/VueGridLayout/GridItem.vue
+0
-869
GridLayout.vue
app/web/page/activity/component/VueGridLayout/GridLayout.vue
+0
-465
DOM.ts
app/web/page/activity/component/VueGridLayout/helpers/DOM.ts
+0
-47
draggableUtils.ts
...ctivity/component/VueGridLayout/helpers/draggableUtils.ts
+0
-44
responsiveUtils.ts
...tivity/component/VueGridLayout/helpers/responsiveUtils.ts
+0
-108
utils.ts
...eb/page/activity/component/VueGridLayout/helpers/utils.ts
+0
-586
index.ts
app/web/page/activity/index.ts
+0
-6
index.ts
app/web/page/activity/router/index.ts
+0
-18
index.ts
app/web/page/activity/view/activity/index.ts
+0
-92
index.vue
app/web/page/activity/view/activity/index.vue
+0
-63
index.ts
app/web/page/activity/view/home/index.ts
+0
-9
index.vue
app/web/page/activity/view/home/index.vue
+0
-6
index.vue
.../component/DynamicForm/component/ColumnSelector/index.vue
+1
-1
index.ts
app/web/page/editor/view/dashboard/index.ts
+1
-1
http.service.ts
app/web/service/http.service.ts
+1
-1
qg.service.ts
app/web/service/qg.service.ts
+38
-29
config.default.ts
config/config.default.ts
+5
-0
index.d.ts
typings/app/controller/index.d.ts
+0
-2
webpack.config.js
webpack.config.js
+0
-1
No files found.
README.md
View file @
2170c951
#
egg-vue-typescript-boilerplate
#
quantum-blocks
基于 Egg + Vue + Webpack SSR 服务端渲染和 CSR 前端渲染工程
骨架
项目。
基于 Egg + Vue + Webpack SSR 服务端渲染和 CSR 前端渲染工程项目。
Single Page Application Isomorphic Example for Egg + Vue, Front-End and Node of The Application are Written in TypeScript.
Single Page Application Isomorphic Example for Egg + Vue, Front-End and Node of The Application are Written in TypeScript.
...
...
app/controller/activity.ts
deleted
100644 → 0
View file @
91acb5e9
import
{
Controller
,
Context
}
from
'
egg
'
;
export
default
class
ActivityController
extends
Controller
{
public
async
home
(
ctx
:
Context
)
{
await
ctx
.
render
(
'
activity.js
'
,
{
url
:
ctx
.
url
});
}
}
\ No newline at end of file
app/router.ts
View file @
2170c951
...
@@ -18,5 +18,4 @@ export default (application: Application) => {
...
@@ -18,5 +18,4 @@ export default (application: Application) => {
router
.
get
(
'
/editor/login
'
,
controller
.
editor
.
login
);
router
.
get
(
'
/editor/login
'
,
controller
.
editor
.
login
);
router
.
get
(
'
/editor
'
,
controller
.
editor
.
home
);
router
.
get
(
'
/editor
'
,
controller
.
editor
.
home
);
router
.
get
(
'
/editor/*
'
,
controller
.
editor
.
home
);
router
.
get
(
'
/editor/*
'
,
controller
.
editor
.
home
);
router
.
get
(
'
/activity/:id
'
,
controller
.
activity
.
home
);
};
};
\ No newline at end of file
app/web/asset/images/favicon.ico
View replaced file @
91acb5e9
View file @
2170c951
4.19 KB
|
W:
|
H:
2.3 KB
|
W:
|
H:
2-up
Swipe
Onion skin
app/web/asset/images/loading.gif
deleted
100755 → 0
View file @
91acb5e9
6.58 KB
app/web/asset/images/logo.png
deleted
100644 → 0
View file @
91acb5e9
35.2 KB
app/web/component/layout/activity/index.ts
deleted
100644 → 0
View file @
91acb5e9
import
{
Vue
,
Component
,
Prop
}
from
'
vue-property-decorator
'
;
import
Raven
from
'
raven-js
'
;
import
RavenVue
from
'
raven-js/plugins/vue
'
;
import
{
release
}
from
'
@/.sentryclirc
'
;
import
'
@/service/qg.service
'
;
// 初始化sentry
if
(
process
.
env
.
SENTRY_ENV
!==
'
test
'
&&
process
.
env
.
NODE_ENV
===
'
production
'
)
{
Raven
.
config
(
'
http://0785298052fd46128e201f30ca649102@sentry.q-gp.com/64
'
,
{
release
,
environment
:
'
production
'
})
.
addPlugin
(
RavenVue
,
Vue
)
.
install
();
}
@
Component
({
name
:
'
Layout
'
})
export
default
class
Layout
extends
Vue
{
@
Prop
({
type
:
String
,
default
:
'
egg
'
})
title
?:
string
;
@
Prop
({
type
:
String
,
default
:
'
Vue TypeScript Framework, Server Side Render
'
})
description
?:
string
;
@
Prop
({
type
:
String
,
default
:
'
Vue,TypeScript,Isomorphic
'
})
keywords
?:
string
;
isNode
:
boolean
=
EASY_ENV_IS_NODE
;
created
()
{
console
.
log
(
'
>>EASY_ENV_IS_NODE create
'
,
EASY_ENV_IS_NODE
);
}
}
\ No newline at end of file
app/web/component/layout/activity/index.vue
deleted
100644 → 0
View file @
91acb5e9
<
template
>
<html
v-if=
"isNode"
>
<head>
<title>
{{
title
}}
</title>
<meta
name=
"keywords"
:content=
"keywords"
>
<meta
name=
"description"
:content=
"description"
>
<meta
http-equiv=
"content-type"
content=
"text/html;charset=utf-8"
>
<meta
name=
"viewport"
content=
"initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"
>
<link
rel=
"shortcut icon"
href=
"/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"stylesheet"
href=
"/public/asset/css/reset.css"
>
</head>
<body>
<div
id=
"app"
><slot></slot></div>
</body>
</html>
<div
v-else-if=
"!isNode"
id=
"app"
><slot></slot></div>
</
template
>
<
style
>
html
{
font-size
:
10vw
!important
;
}
@media
screen
and
(
min-width
:
768
Px
)
{
html
{
font-size
:
37.5
Px
!important
;
}
body
{
max-width
:
375
Px
;
max-height
:
667
Px
;
margin
:
0
auto
!important
;
}
}
</
style
>
<
script
lang=
"ts"
src=
"./index.ts"
></
script
>
\ No newline at end of file
app/web/config/dev.config.ts
View file @
2170c951
const
protocol
=
EASY_ENV_IS_BROWSER
?
window
.
location
.
protocol
:
'
http
'
;
const
protocol
=
EASY_ENV_IS_BROWSER
?
window
.
location
.
protocol
:
'
http
'
;
export
default
{
export
default
{
apiHost
:
`http://localhost:700
1
/`
,
apiHost
:
`http://localhost:700
2
/`
,
// apiHost: `http://192.168.28.199:7001/`,
// apiHost: `http://192.168.28.199:7001/`,
// apiHost: 'https://quantum-vcc2.liangkebang.net/',
h5Host
:
'
https://quantum-h5-vcc2.liangkebang.net/
'
,
qiniuHost
:
`https://appsync.lkbang.net/`
,
qiniuHost
:
`https://appsync.lkbang.net/`
,
shenceUrl
:
`
${
protocol
}
//bn.xyqb.com/sa?project=default`
,
shenceUrl
:
`
${
protocol
}
//bn.xyqb.com/sa?project=default`
,
opapiHost
:
`https://opapi-vcc2.liangkebang.net`
,
opapiHost
:
`https://opapi-vcc2.liangkebang.net`
,
...
...
app/web/config/prod.config.ts
View file @
2170c951
...
@@ -2,6 +2,7 @@ const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'https';
...
@@ -2,6 +2,7 @@ const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'https';
export
default
{
export
default
{
apiHost
:
`https://quantum-blocks.q-gp.com/`
,
apiHost
:
`https://quantum-blocks.q-gp.com/`
,
h5Host
:
'
https://quantum-h5-vcc2.liangkebang.net/
'
,
qiniuHost
:
`https://appsync.lkbang.net/`
,
qiniuHost
:
`https://appsync.lkbang.net/`
,
shenceUrl
:
`
${
protocol
}
//bn.xyqb.com/sa?project=production`
,
shenceUrl
:
`
${
protocol
}
//bn.xyqb.com/sa?project=production`
,
opapiHost
:
`
${
protocol
}
//opapi.q-gp.com`
,
opapiHost
:
`
${
protocol
}
//opapi.q-gp.com`
,
...
...
app/web/config/test.config.ts
View file @
2170c951
...
@@ -2,6 +2,7 @@ const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'https';
...
@@ -2,6 +2,7 @@ const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'https';
export
default
{
export
default
{
apiHost
:
`https://quantum-vcc2.liangkebang.net/`
,
apiHost
:
`https://quantum-vcc2.liangkebang.net/`
,
h5Host
:
'
https://quantum-h5-vcc2.liangkebang.net/
'
,
opapiHost
:
'
https://opapi-vcc2.liangkebang.net
'
,
opapiHost
:
'
https://opapi-vcc2.liangkebang.net
'
,
qiniuHost
:
`https://appsync.lkbang.net/`
,
qiniuHost
:
`https://appsync.lkbang.net/`
,
shenceUrl
:
`
${
protocol
}
//bn.xyqb.com/sa?project=production`
,
shenceUrl
:
`
${
protocol
}
//bn.xyqb.com/sa?project=production`
,
...
...
app/web/lib/GuideCube/index.vue
View file @
2170c951
...
@@ -29,7 +29,6 @@
...
@@ -29,7 +29,6 @@
type
:
Number
,
type
:
Number
,
default
:
1
default
:
1
},
},
loop
:
Boolean
,
autoplay
:
Boolean
,
autoplay
:
Boolean
,
animation
:
Boolean
animation
:
Boolean
},
},
...
@@ -40,9 +39,8 @@
...
@@ -40,9 +39,8 @@
let
lastProgress
=
0
;
let
lastProgress
=
0
;
return
{
return
{
showSwiper
:
true
,
showSwiper
:
true
,
// style: {},
swiperOptions
:
{
swiperOptions
:
{
loop
:
this
.
loop
,
loop
:
this
.
slidesPerColumn
===
1
?
true
:
false
,
slidesPerView
:
4
,
slidesPerView
:
4
,
slidesPerColumn
:
this
.
slidesPerColumn
,
slidesPerColumn
:
this
.
slidesPerColumn
,
spaceBetween
:
8
,
spaceBetween
:
8
,
...
@@ -117,8 +115,10 @@
...
@@ -117,8 +115,10 @@
return
new
Array
(
length
).
fill
(
0
).
map
((
v
,
i
)
=>
[
0
,
3
].
includes
(
i
%
baseNumber
)
?
1
:
0
);
return
new
Array
(
length
).
fill
(
0
).
map
((
v
,
i
)
=>
[
0
,
3
].
includes
(
i
%
baseNumber
)
?
1
:
0
);
},
},
style
()
{
style
()
{
return
{
return
this
.
animation
?
{
transition
:
'
all .2s cubic-bezier(.4, 0, .2, 1)
'
transition
:
'
all .2s cubic-bezier(.4, 0, .2, 1)
'
}
:
{
transform
:
'
none
'
};
};
}
}
},
},
...
...
app/web/lib/config.ts
View file @
2170c951
...
@@ -550,11 +550,6 @@ export const businessComponents = [
...
@@ -550,11 +550,6 @@ export const businessComponents = [
name
:
'
行数
'
,
name
:
'
行数
'
,
type
:
'
InputNumber
'
type
:
'
InputNumber
'
},
},
{
key
:
'
loop
'
,
name
:
'
循环
'
,
type
:
'
checkbox
'
},
{
{
key
:
'
autoplay
'
,
key
:
'
autoplay
'
,
name
:
'
自动播放
'
,
name
:
'
自动播放
'
,
...
@@ -622,7 +617,6 @@ export const businessComponents = [
...
@@ -622,7 +617,6 @@ export const businessComponents = [
link
:
'
http://activitystatic.q-gp.com
'
link
:
'
http://activitystatic.q-gp.com
'
}],
}],
slidesPerColumn
:
1
,
slidesPerColumn
:
1
,
loop
:
false
,
autoplay
:
false
,
autoplay
:
false
,
animation
:
false
animation
:
false
}
}
...
...
app/web/page/activity/component/FreedomContainer/index.ts
deleted
100644 → 0
View file @
91acb5e9
import
{
cloneDeep
}
from
'
lodash
'
;
import
{
Component
,
Prop
,
Vue
,
Mixins
}
from
'
vue-property-decorator
'
;
import
{
Action
,
Mutation
,
State
,
Getter
}
from
'
vuex-class
'
;
import
TransformStyleMixin
from
'
@/page/mixins/transformStyle.mixin
'
;
import
CustomMarquee
from
'
@/lib/Marquee/index.vue
'
;
import
{
resizeDiv
}
from
'
@/service/utils.service
'
;
@
Component
({
components
:
{
CustomMarquee
},
name
:
'
FreedomContainer
'
})
export
default
class
FreedomContainer
extends
Mixins
(
TransformStyleMixin
)
{
@
Getter
(
'
pageData
'
)
pageData
;
@
State
(
state
=>
state
.
editor
.
curChildIndex
)
curChildIndex
;
@
Mutation
(
'
UPDATE_PAGE_INFO
'
)
updatePageInfo
;
@
Prop
({
type
:
Object
,
default
:
()
=>
({
child
:
[]
})})
childItem
;
@
Prop
(
String
)
backgroundImage
;
mounted
()
{
// 根据背景图设置元素高度
const
index
=
this
.
pageData
?.
elements
?.
findIndex
(
v
=>
v
.
point
?.
responsive
);
const
{
props
:
{
backgroundImage
},
point
}
=
this
.
pageData
?.
elements
[
index
]
||
{
props
:
{}};
if
(
backgroundImage
)
{
resizeDiv
(
backgroundImage
,
null
,
null
,
(
height
)
=>
{
this
.
updatePageInfo
({
containerIndex
:
index
,
data
:
{
...
this
.
pageData
?.
elements
[
index
],
point
:
{
...
point
,
h
:
height
??
point
.
h
}
}
});
});
}
}
}
\ No newline at end of file
app/web/page/activity/component/FreedomContainer/index.vue
deleted
100644 → 0
View file @
91acb5e9
<
template
>
<div
class=
"freedom"
>
<div
class=
"freedom-body"
>
<component
:class=
"['freedom-body-item',
{ 'Fb-item_selected': curChildIndex === index }]" v-for="(item, index) in childItem.child" :style="transformStyle(item.commonStyle, 'container')" :is="item.name" :key="index" v-bind="item.props">
</component>
</div>
</div>
</
template
>
<
script
lang=
"ts"
src=
"./index.ts"
></
script
>
<
style
lang=
"less"
scoped
>
.freedom {
height: 100%;
width: 100%;
header {
width: 100%;
height: 48px;
line-height: 48px;
text-align: center;
border-bottom: 1px solid #f0f0f0;
}
&-body {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
&_full {
height: calc(100% - 48px);
}
}
}
</
style
>
\ No newline at end of file
app/web/page/activity/component/VueGridLayout/GridItem.vue
deleted
100644 → 0
View file @
91acb5e9
<
template
>
<div
ref=
"item"
class=
"vue-grid-item"
:class=
"classObj"
:style=
"style"
>
<slot></slot>
<span
v-if=
"resizableAndNotStatic"
ref=
"handle"
:class=
"resizableHandleClass"
></span>
<!--
<span
v-if=
"draggable"
ref=
"dragHandle"
class=
"vue-draggable-handle"
></span>
-->
</div>
</
template
>
<
style
>
.vue-grid-item
{
transition
:
all
200ms
ease
;
transition-property
:
left
,
top
,
right
;
/* add right for rtl */
}
.vue-grid-item.no-touch
{
-ms-touch-action
:
none
;
touch-action
:
none
;
}
.vue-grid-item.cssTransforms
{
transition-property
:
transform
;
left
:
0
;
right
:
auto
;
}
.vue-grid-item.cssTransforms.render-rtl
{
left
:
auto
;
right
:
0
;
}
.vue-grid-item.resizing
{
opacity
:
0.6
;
z-index
:
3
;
}
.vue-grid-item.vue-draggable-dragging
{
transition
:
none
;
z-index
:
3
;
}
.vue-grid-item.vue-grid-placeholder
{
background
:
red
;
opacity
:
0.2
;
transition-duration
:
100ms
;
z-index
:
2
;
-webkit-user-select
:
none
;
-moz-user-select
:
none
;
-ms-user-select
:
none
;
-o-user-select
:
none
;
user-select
:
none
;
}
.vue-grid-item
>
.vue-resizable-handle
{
position
:
absolute
;
width
:
20px
;
height
:
20px
;
bottom
:
0
;
right
:
0
;
background
:
url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=')
;
background-position
:
bottom
right
;
padding
:
0
3px
3px
0
;
background-repeat
:
no-repeat
;
background-origin
:
content-box
;
box-sizing
:
border-box
;
cursor
:
se-resize
;
}
.vue-grid-item
>
.vue-rtl-resizable-handle
{
bottom
:
0
;
left
:
0
;
background
:
url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAuMDAwMDAwMDAwMDAwMDAyIiBoZWlnaHQ9IjEwLjAwMDAwMDAwMDAwMDAwMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDwhLS0gQ3JlYXRlZCB3aXRoIE1ldGhvZCBEcmF3IC0gaHR0cDovL2dpdGh1Yi5jb20vZHVvcGl4ZWwvTWV0aG9kLURyYXcvIC0tPgogPGc+CiAgPHRpdGxlPmJhY2tncm91bmQ8L3RpdGxlPgogIDxyZWN0IGZpbGw9Im5vbmUiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgaGVpZ2h0PSIxMiIgd2lkdGg9IjEyIiB5PSItMSIgeD0iLTEiLz4KICA8ZyBkaXNwbGF5PSJub25lIiBvdmVyZmxvdz0idmlzaWJsZSIgeT0iMCIgeD0iMCIgaGVpZ2h0PSIxMDAlIiB3aWR0aD0iMTAwJSIgaWQ9ImNhbnZhc0dyaWQiPgogICA8cmVjdCBmaWxsPSJ1cmwoI2dyaWRwYXR0ZXJuKSIgc3Ryb2tlLXdpZHRoPSIwIiB5PSIwIiB4PSIwIiBoZWlnaHQ9IjEwMCUiIHdpZHRoPSIxMDAlIi8+CiAgPC9nPgogPC9nPgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxsaW5lIGNhbnZhcz0iI2ZmZmZmZiIgY2FudmFzLW9wYWNpdHk9IjEiIHN0cm9rZS1saW5lY2FwPSJ1bmRlZmluZWQiIHN0cm9rZS1saW5lam9pbj0idW5kZWZpbmVkIiBpZD0ic3ZnXzEiIHkyPSItNzAuMTc4NDA3IiB4Mj0iMTI0LjQ2NDE3NSIgeTE9Ii0zOC4zOTI3MzciIHgxPSIxNDQuODIxMjg5IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSJub25lIi8+CiAgPGxpbmUgc3Ryb2tlPSIjNjY2NjY2IiBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z181IiB5Mj0iOS4xMDY5NTciIHgyPSIwLjk0NzI0NyIgeTE9Ii0wLjAxODEyOCIgeDE9IjAuOTQ3MjQ3IiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz4KICA8bGluZSBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z183IiB5Mj0iOSIgeDI9IjEwLjA3MzUyOSIgeTE9IjkiIHgxPSItMC42NTU2NCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM2NjY2NjYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+)
;
background-position
:
bottom
left
;
padding-left
:
3px
;
background-repeat
:
no-repeat
;
background-origin
:
content-box
;
cursor
:
sw-resize
;
right
:
auto
;
}
.vue-grid-item.disable-userselect
{
user-select
:
none
;
}
</
style
>
<
script
>
import
{
setTopLeft
,
setTopRight
,
setTransformRtl
,
setTransform
}
from
'
./helpers/utils
'
;
import
{
getControlPosition
,
createCoreData
}
from
'
./helpers/draggableUtils
'
;
import
{
getColsFromBreakpoint
}
from
'
./helpers/responsiveUtils
'
;
import
{
getDocumentDir
}
from
"
./helpers/DOM
"
;
// var eventBus = require('./eventBus');
import
'
@interactjs/auto-start
'
import
'
@interactjs/actions/drag
'
import
'
@interactjs/actions/resize
'
import
'
@interactjs/modifiers
'
import
'
@interactjs/dev-tools
'
import
interact
from
'
@interactjs/interact
'
export
default
{
name
:
"
GridItem
"
,
props
:
{
/*cols: {
type: Number,
required: true
},*/
/*containerWidth: {
type: Number,
required: true
},
rowHeight: {
type: Number,
required: true
},
margin: {
type: Array,
required: true
},
maxRows: {
type: Number,
required: true
},*/
isDraggable
:
{
type
:
Boolean
,
required
:
false
,
default
:
null
},
isResizable
:
{
type
:
Boolean
,
required
:
false
,
default
:
null
},
/*useCssTransforms: {
type: Boolean,
required: true
},
*/
static
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
},
minH
:
{
type
:
Number
,
required
:
false
,
default
:
1
},
minW
:
{
type
:
Number
,
required
:
false
,
default
:
1
},
maxH
:
{
type
:
Number
,
required
:
false
,
default
:
Infinity
},
maxW
:
{
type
:
Number
,
required
:
false
,
default
:
Infinity
},
x
:
{
type
:
Number
,
required
:
true
},
y
:
{
type
:
Number
,
required
:
true
},
w
:
{
type
:
Number
,
required
:
true
},
h
:
{
type
:
Number
,
required
:
true
},
i
:
{
required
:
true
},
dragIgnoreFrom
:
{
type
:
String
,
required
:
false
,
default
:
'
a, button
'
},
dragAllowFrom
:
{
type
:
String
,
required
:
false
,
default
:
null
},
resizeIgnoreFrom
:
{
type
:
String
,
required
:
false
,
default
:
'
a, button
'
},
},
inject
:
[
"
eventBus
"
,
"
layout
"
],
data
:
function
()
{
return
{
cols
:
1
,
containerWidth
:
100
,
rowHeight
:
30
,
margin
:
[
10
,
10
],
maxRows
:
Infinity
,
draggable
:
null
,
resizable
:
null
,
useCssTransforms
:
true
,
useStyleCursor
:
true
,
isDragging
:
false
,
dragging
:
null
,
isResizing
:
false
,
resizing
:
null
,
lastX
:
NaN
,
lastY
:
NaN
,
lastW
:
NaN
,
lastH
:
NaN
,
style
:
{},
rtl
:
false
,
dragEventSet
:
false
,
resizeEventSet
:
false
,
previousW
:
null
,
previousH
:
null
,
previousX
:
null
,
previousY
:
null
,
innerX
:
this
.
x
,
innerY
:
this
.
y
,
innerW
:
this
.
w
,
innerH
:
this
.
h
}
},
created
()
{
let
self
=
this
;
// Accessible refernces of functions for removing in beforeDestroy
self
.
updateWidthHandler
=
function
(
width
)
{
self
.
updateWidth
(
width
);
};
self
.
compactHandler
=
function
(
layout
)
{
self
.
compact
(
layout
);
};
self
.
setDraggableHandler
=
function
(
isDraggable
)
{
if
(
self
.
isDraggable
===
null
)
{
self
.
draggable
=
isDraggable
;
}
};
self
.
setResizableHandler
=
function
(
isResizable
)
{
if
(
self
.
isResizable
===
null
)
{
self
.
resizable
=
isResizable
;
}
};
self
.
setRowHeightHandler
=
function
(
rowHeight
)
{
self
.
rowHeight
=
rowHeight
;
};
self
.
setMaxRowsHandler
=
function
(
maxRows
)
{
self
.
maxRows
=
maxRows
;
};
self
.
directionchangeHandler
=
()
=>
{
this
.
rtl
=
getDocumentDir
()
===
'
rtl
'
;
this
.
compact
();
};
self
.
setColNum
=
(
colNum
)
=>
{
self
.
cols
=
parseInt
(
colNum
);
}
this
.
eventBus
.
$on
(
'
updateWidth
'
,
self
.
updateWidthHandler
);
this
.
eventBus
.
$on
(
'
compact
'
,
self
.
compactHandler
);
this
.
eventBus
.
$on
(
'
setDraggable
'
,
self
.
setDraggableHandler
);
this
.
eventBus
.
$on
(
'
setResizable
'
,
self
.
setResizableHandler
);
this
.
eventBus
.
$on
(
'
setRowHeight
'
,
self
.
setRowHeightHandler
);
this
.
eventBus
.
$on
(
'
setMaxRows
'
,
self
.
setMaxRowsHandler
);
this
.
eventBus
.
$on
(
'
directionchange
'
,
self
.
directionchangeHandler
);
this
.
eventBus
.
$on
(
'
setColNum
'
,
self
.
setColNum
)
this
.
rtl
=
getDocumentDir
()
===
'
rtl
'
;
},
beforeDestroy
:
function
(){
let
self
=
this
;
//Remove listeners
this
.
eventBus
.
$off
(
'
updateWidth
'
,
self
.
updateWidthHandler
);
this
.
eventBus
.
$off
(
'
compact
'
,
self
.
compactHandler
);
this
.
eventBus
.
$off
(
'
setDraggable
'
,
self
.
setDraggableHandler
);
this
.
eventBus
.
$off
(
'
setResizable
'
,
self
.
setResizableHandler
);
this
.
eventBus
.
$off
(
'
setRowHeight
'
,
self
.
setRowHeightHandler
);
this
.
eventBus
.
$off
(
'
setMaxRows
'
,
self
.
setMaxRowsHandler
);
this
.
eventBus
.
$off
(
'
directionchange
'
,
self
.
directionchangeHandler
);
this
.
eventBus
.
$off
(
'
setColNum
'
,
self
.
setColNum
);
if
(
this
.
interactObj
)
{
this
.
interactObj
.
unset
()
// destroy interact intance
}
},
mounted
:
function
()
{
if
(
this
.
layout
.
responsive
&&
this
.
layout
.
lastBreakpoint
)
{
this
.
cols
=
getColsFromBreakpoint
(
this
.
layout
.
lastBreakpoint
,
this
.
layout
.
cols
);
}
else
{
this
.
cols
=
this
.
layout
.
colNum
;
}
this
.
rowHeight
=
this
.
layout
.
rowHeight
;
this
.
containerWidth
=
this
.
layout
.
width
!==
null
?
this
.
layout
.
width
:
100
;
this
.
margin
=
this
.
layout
.
margin
!==
undefined
?
this
.
layout
.
margin
:
[
10
,
10
];
this
.
maxRows
=
this
.
layout
.
maxRows
;
if
(
this
.
isDraggable
===
null
)
{
this
.
draggable
=
this
.
layout
.
isDraggable
;
}
else
{
this
.
draggable
=
this
.
isDraggable
;
}
if
(
this
.
isResizable
===
null
)
{
this
.
resizable
=
this
.
layout
.
isResizable
;
}
else
{
this
.
resizable
=
this
.
isResizable
;
}
this
.
useCssTransforms
=
this
.
layout
.
useCssTransforms
;
this
.
useStyleCursor
=
this
.
layout
.
useStyleCursor
;
this
.
createStyle
();
},
watch
:
{
isDraggable
:
function
()
{
this
.
draggable
=
this
.
isDraggable
;
},
static
:
function
()
{
this
.
tryMakeDraggable
();
this
.
tryMakeResizable
();
},
draggable
:
function
()
{
this
.
tryMakeDraggable
();
},
isResizable
:
function
()
{
this
.
resizable
=
this
.
isResizable
;
},
resizable
:
function
()
{
this
.
tryMakeResizable
();
},
rowHeight
:
function
()
{
this
.
createStyle
();
this
.
emitContainerResized
();
},
cols
:
function
()
{
this
.
tryMakeResizable
();
this
.
createStyle
();
this
.
emitContainerResized
();
},
containerWidth
:
function
()
{
this
.
tryMakeResizable
();
this
.
createStyle
();
this
.
emitContainerResized
();
},
x
:
function
(
newVal
)
{
this
.
innerX
=
newVal
;
this
.
createStyle
();
},
y
:
function
(
newVal
)
{
this
.
innerY
=
newVal
;
this
.
createStyle
();
},
h
:
function
(
newVal
)
{
this
.
innerH
=
newVal
this
.
createStyle
();
// this.emitContainerResized();
},
w
:
function
(
newVal
)
{
this
.
innerW
=
newVal
;
this
.
createStyle
();
// this.emitContainerResized();
},
renderRtl
:
function
()
{
// console.log("### renderRtl");
this
.
tryMakeResizable
();
this
.
createStyle
();
},
minH
:
function
()
{
this
.
tryMakeResizable
();
},
maxH
:
function
()
{
this
.
tryMakeResizable
();
},
minW
:
function
()
{
this
.
tryMakeResizable
();
},
maxW
:
function
()
{
this
.
tryMakeResizable
();
},
"
$parent.margin
"
:
function
(
margin
)
{
if
(
!
margin
||
(
margin
[
0
]
==
this
.
margin
[
0
]
&&
margin
[
1
]
==
this
.
margin
[
1
]))
{
return
;
}
this
.
margin
=
margin
.
map
(
m
=>
Number
(
m
));
this
.
createStyle
();
this
.
emitContainerResized
();
},
},
computed
:
{
classObj
()
{
return
{
'
vue-resizable
'
:
this
.
resizableAndNotStatic
,
'
static
'
:
this
.
static
,
'
resizing
'
:
this
.
isResizing
,
'
vue-draggable-dragging
'
:
this
.
isDragging
,
'
cssTransforms
'
:
this
.
useCssTransforms
,
'
render-rtl
'
:
this
.
renderRtl
,
'
disable-userselect
'
:
this
.
isDragging
,
'
no-touch
'
:
this
.
isAndroid
&&
this
.
draggableOrResizableAndNotStatic
}
},
resizableAndNotStatic
(){
return
this
.
resizable
&&
!
this
.
static
;
},
draggableOrResizableAndNotStatic
(){
return
(
this
.
draggable
||
this
.
resizable
)
&&
!
this
.
static
;
},
isAndroid
()
{
return
true
;
// return navigator.userAgent.toLowerCase().indexOf("android") !== -1;
},
renderRtl
()
{
return
(
this
.
layout
.
isMirrored
)
?
!
this
.
rtl
:
this
.
rtl
;
},
resizableHandleClass
()
{
if
(
this
.
renderRtl
)
{
return
'
vue-resizable-handle vue-rtl-resizable-handle
'
;
}
else
{
return
'
vue-resizable-handle
'
;
}
}
},
methods
:
{
createStyle
:
function
()
{
if
(
this
.
x
+
this
.
w
>
this
.
cols
)
{
this
.
innerX
=
0
;
this
.
innerW
=
(
this
.
w
>
this
.
cols
)
?
this
.
cols
:
this
.
w
}
else
{
this
.
innerX
=
this
.
x
;
this
.
innerW
=
this
.
w
;
}
let
pos
=
this
.
calcPosition
(
this
.
innerX
,
this
.
innerY
,
this
.
innerW
,
this
.
innerH
);
if
(
this
.
isDragging
)
{
pos
.
top
=
this
.
dragging
.
top
;
// Add rtl support
if
(
this
.
renderRtl
)
{
pos
.
right
=
this
.
dragging
.
left
;
}
else
{
pos
.
left
=
this
.
dragging
.
left
;
}
}
if
(
this
.
isResizing
)
{
pos
.
width
=
this
.
resizing
.
width
;
pos
.
height
=
this
.
resizing
.
height
;
}
let
style
;
// CSS Transforms support (default)
if
(
this
.
useCssTransforms
)
{
// Add rtl support
if
(
this
.
renderRtl
)
{
style
=
setTransformRtl
(
pos
.
top
,
pos
.
right
,
pos
.
width
,
pos
.
height
);
}
else
{
style
=
setTransform
(
pos
.
top
,
pos
.
left
,
pos
.
width
,
pos
.
height
);
}
}
else
{
// top,left (slow)
// Add rtl support
if
(
this
.
renderRtl
)
{
style
=
setTopRight
(
pos
.
top
,
pos
.
right
,
pos
.
width
,
pos
.
height
);
}
else
{
style
=
setTopLeft
(
pos
.
top
,
pos
.
left
,
pos
.
width
,
pos
.
height
);
}
}
this
.
style
=
style
;
},
emitContainerResized
()
{
// this.style has width and height with trailing 'px'. The
// resized event is without them
let
styleProps
=
{};
for
(
let
prop
of
[
'
width
'
,
'
height
'
])
{
let
val
=
this
.
style
[
prop
];
let
matches
=
val
.
match
(
/^
(\d
+
)
px$/
);
if
(
!
matches
)
return
;
styleProps
[
prop
]
=
matches
[
1
];
}
this
.
$emit
(
"
container-resized
"
,
this
.
i
,
this
.
h
,
this
.
w
,
styleProps
.
height
,
styleProps
.
width
);
},
handleResize
:
function
(
event
)
{
if
(
this
.
static
)
return
;
const
position
=
getControlPosition
(
event
);
// Get the current drag point from the event. This is used as the offset.
if
(
position
==
null
)
return
;
// not possible but satisfies flow
const
{
x
,
y
}
=
position
;
const
newSize
=
{
width
:
0
,
height
:
0
};
let
pos
;
switch
(
event
.
type
)
{
case
"
resizestart
"
:
{
this
.
previousW
=
this
.
innerW
;
this
.
previousH
=
this
.
innerH
;
pos
=
this
.
calcPosition
(
this
.
innerX
,
this
.
innerY
,
this
.
innerW
,
this
.
innerH
);
newSize
.
width
=
pos
.
width
;
newSize
.
height
=
pos
.
height
;
this
.
resizing
=
newSize
;
this
.
isResizing
=
true
;
break
;
}
case
"
resizemove
"
:
{
// console.log("### resize => " + event.type + ", lastW=" + this.lastW + ", lastH=" + this.lastH);
const
coreEvent
=
createCoreData
(
this
.
lastW
,
this
.
lastH
,
x
,
y
);
if
(
this
.
renderRtl
)
{
newSize
.
width
=
this
.
resizing
.
width
-
coreEvent
.
deltaX
;
}
else
{
newSize
.
width
=
this
.
resizing
.
width
+
coreEvent
.
deltaX
;
}
newSize
.
height
=
this
.
resizing
.
height
+
coreEvent
.
deltaY
;
///console.log("### resize => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
this
.
resizing
=
newSize
;
break
;
}
case
"
resizeend
"
:
{
//console.log("### resize end => x=" +this.innerX + " y=" + this.innerY + " w=" + this.innerW + " h=" + this.innerH);
pos
=
this
.
calcPosition
(
this
.
innerX
,
this
.
innerY
,
this
.
innerW
,
this
.
innerH
);
newSize
.
width
=
pos
.
width
;
newSize
.
height
=
pos
.
height
;
// console.log("### resize end => " + JSON.stringify(newSize));
this
.
resizing
=
null
;
this
.
isResizing
=
false
;
break
;
}
}
// Get new WH
pos
=
this
.
calcWH
(
newSize
.
height
,
newSize
.
width
);
if
(
pos
.
w
<
this
.
minW
)
{
pos
.
w
=
this
.
minW
;
}
if
(
pos
.
w
>
this
.
maxW
)
{
pos
.
w
=
this
.
maxW
;
}
if
(
pos
.
h
<
this
.
minH
)
{
pos
.
h
=
this
.
minH
;
}
if
(
pos
.
h
>
this
.
maxH
)
{
pos
.
h
=
this
.
maxH
;
}
if
(
pos
.
h
<
1
)
{
pos
.
h
=
1
;
}
if
(
pos
.
w
<
1
)
{
pos
.
w
=
1
;
}
this
.
lastW
=
x
;
this
.
lastH
=
y
;
if
(
this
.
innerW
!==
pos
.
w
||
this
.
innerH
!==
pos
.
h
)
{
this
.
$emit
(
"
resize
"
,
this
.
i
,
pos
.
h
,
pos
.
w
,
newSize
.
height
,
newSize
.
width
);
}
if
(
event
.
type
===
"
resizeend
"
&&
(
this
.
previousW
!==
this
.
innerW
||
this
.
previousH
!==
this
.
innerH
))
{
this
.
$emit
(
"
resized
"
,
this
.
i
,
pos
.
h
,
pos
.
w
,
newSize
.
height
,
newSize
.
width
);
}
this
.
eventBus
.
$emit
(
"
resizeEvent
"
,
event
.
type
,
this
.
i
,
this
.
innerX
,
this
.
innerY
,
pos
.
h
,
pos
.
w
);
},
handleDrag
(
event
)
{
if
(
this
.
static
)
return
;
if
(
this
.
isResizing
)
return
;
const
position
=
getControlPosition
(
event
);
// Get the current drag point from the event. This is used as the offset.
if
(
position
===
null
)
return
;
// not possible but satisfies flow
const
{
x
,
y
}
=
position
;
// let shouldUpdate = false;
let
newPosition
=
{
top
:
0
,
left
:
0
};
switch
(
event
.
type
)
{
case
"
dragstart
"
:
{
this
.
previousX
=
this
.
innerX
;
this
.
previousY
=
this
.
innerY
;
let
parentRect
=
event
.
target
.
offsetParent
.
getBoundingClientRect
();
let
clientRect
=
event
.
target
.
getBoundingClientRect
();
if
(
this
.
renderRtl
)
{
newPosition
.
left
=
(
clientRect
.
right
-
parentRect
.
right
)
*
-
1
;
}
else
{
newPosition
.
left
=
clientRect
.
left
-
parentRect
.
left
;
}
newPosition
.
top
=
clientRect
.
top
-
parentRect
.
top
;
this
.
dragging
=
newPosition
;
this
.
isDragging
=
true
;
break
;
}
case
"
dragend
"
:
{
if
(
!
this
.
isDragging
)
return
;
let
parentRect
=
event
.
target
.
offsetParent
.
getBoundingClientRect
();
let
clientRect
=
event
.
target
.
getBoundingClientRect
();
// Add rtl support
if
(
this
.
renderRtl
)
{
newPosition
.
left
=
(
clientRect
.
right
-
parentRect
.
right
)
*
-
1
;
}
else
{
newPosition
.
left
=
clientRect
.
left
-
parentRect
.
left
;
}
newPosition
.
top
=
clientRect
.
top
-
parentRect
.
top
;
// console.log("### drag end => " + JSON.stringify(newPosition));
// console.log("### DROP: " + JSON.stringify(newPosition));
this
.
dragging
=
null
;
this
.
isDragging
=
false
;
// shouldUpdate = true;
break
;
}
case
"
dragmove
"
:
{
const
coreEvent
=
createCoreData
(
this
.
lastX
,
this
.
lastY
,
x
,
y
);
// Add rtl support
if
(
this
.
renderRtl
)
{
newPosition
.
left
=
this
.
dragging
.
left
-
coreEvent
.
deltaX
;
}
else
{
newPosition
.
left
=
this
.
dragging
.
left
+
coreEvent
.
deltaX
;
}
newPosition
.
top
=
this
.
dragging
.
top
+
coreEvent
.
deltaY
;
// console.log("### drag => " + event.type + ", x=" + x + ", y=" + y);
// console.log("### drag => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
// console.log("### drag end => " + JSON.stringify(newPosition));
this
.
dragging
=
newPosition
;
break
;
}
}
// Get new XY
let
pos
;
if
(
this
.
renderRtl
)
{
pos
=
this
.
calcXY
(
newPosition
.
top
,
newPosition
.
left
);
}
else
{
pos
=
this
.
calcXY
(
newPosition
.
top
,
newPosition
.
left
);
}
this
.
lastX
=
x
;
this
.
lastY
=
y
;
if
(
this
.
innerX
!==
pos
.
x
||
this
.
innerY
!==
pos
.
y
)
{
this
.
$emit
(
"
move
"
,
this
.
i
,
pos
.
x
,
pos
.
y
);
}
if
(
event
.
type
===
"
dragend
"
&&
(
this
.
previousX
!==
this
.
innerX
||
this
.
previousY
!==
this
.
innerY
))
{
this
.
$emit
(
"
moved
"
,
this
.
i
,
pos
.
x
,
pos
.
y
);
}
this
.
eventBus
.
$emit
(
"
dragEvent
"
,
event
.
type
,
this
.
i
,
pos
.
x
,
pos
.
y
,
this
.
innerH
,
this
.
innerW
);
},
calcPosition
:
function
(
x
,
y
,
w
,
h
)
{
const
colWidth
=
this
.
calcColWidth
();
// add rtl support
let
out
;
if
(
this
.
renderRtl
)
{
out
=
{
right
:
Math
.
round
(
colWidth
*
x
+
(
x
+
1
)
*
this
.
margin
[
0
]),
top
:
Math
.
round
(
this
.
rowHeight
*
y
+
(
y
+
1
)
*
this
.
margin
[
1
]),
// 0 * Infinity === NaN, which causes problems with resize constriants;
// Fix this if it occurs.
// Note we do it here rather than later because Math.round(Infinity) causes deopt
width
:
w
===
Infinity
?
w
:
Math
.
round
(
colWidth
*
w
+
Math
.
max
(
0
,
w
-
1
)
*
this
.
margin
[
0
]),
height
:
h
===
Infinity
?
h
:
Math
.
round
(
this
.
rowHeight
*
h
+
Math
.
max
(
0
,
h
-
1
)
*
this
.
margin
[
1
])
};
}
else
{
out
=
{
left
:
Math
.
round
(
colWidth
*
x
+
(
x
+
1
)
*
this
.
margin
[
0
]),
top
:
Math
.
round
(
this
.
rowHeight
*
y
+
(
y
+
1
)
*
this
.
margin
[
1
]),
// 0 * Infinity === NaN, which causes problems with resize constriants;
// Fix this if it occurs.
// Note we do it here rather than later because Math.round(Infinity) causes deopt
width
:
w
===
Infinity
?
w
:
Math
.
round
(
colWidth
*
w
+
Math
.
max
(
0
,
w
-
1
)
*
this
.
margin
[
0
]),
height
:
h
===
Infinity
?
h
:
Math
.
round
(
this
.
rowHeight
*
h
+
Math
.
max
(
0
,
h
-
1
)
*
this
.
margin
[
1
])
};
}
return
out
;
},
/**
* Translate x and y coordinates from pixels to grid units.
* @param {Number} top Top position (relative to parent) in pixels.
* @param {Number} left Left position (relative to parent) in pixels.
* @return {Object} x and y in grid units.
*/
// TODO check if this function needs change in order to support rtl.
calcXY
(
top
,
left
)
{
const
colWidth
=
this
.
calcColWidth
();
// left = colWidth * x + margin * (x + 1)
// l = cx + m(x+1)
// l = cx + mx + m
// l - m = cx + mx
// l - m = x(c + m)
// (l - m) / (c + m) = x
// x = (left - margin) / (coldWidth + margin)
let
x
=
Math
.
round
((
left
-
this
.
margin
[
0
])
/
(
colWidth
+
this
.
margin
[
0
]));
let
y
=
Math
.
round
((
top
-
this
.
margin
[
1
])
/
(
this
.
rowHeight
+
this
.
margin
[
1
]));
// Capping
x
=
Math
.
max
(
Math
.
min
(
x
,
this
.
cols
-
this
.
innerW
),
0
);
y
=
Math
.
max
(
Math
.
min
(
y
,
this
.
maxRows
-
this
.
innerH
),
0
);
return
{
x
,
y
};
},
// Helper for generating column width
calcColWidth
()
{
const
colWidth
=
(
this
.
containerWidth
-
(
this
.
margin
[
0
]
*
(
this
.
cols
+
1
)))
/
this
.
cols
;
// console.log("### COLS=" + this.cols + " COL WIDTH=" + colWidth + " MARGIN " + this.margin[0]);
return
colWidth
;
},
/**
* Given a height and width in pixel values, calculate grid units.
* @param {Number} height Height in pixels.
* @param {Number} width Width in pixels.
* @return {Object} w, h as grid units.
*/
calcWH
(
height
,
width
)
{
const
colWidth
=
this
.
calcColWidth
();
// width = colWidth * w - (margin * (w - 1))
// ...
// w = (width + margin) / (colWidth + margin)
let
w
=
Math
.
round
((
width
+
this
.
margin
[
0
])
/
(
colWidth
+
this
.
margin
[
0
]));
let
h
=
Math
.
round
((
height
+
this
.
margin
[
1
])
/
(
this
.
rowHeight
+
this
.
margin
[
1
]));
// Capping
w
=
Math
.
max
(
Math
.
min
(
w
,
this
.
cols
-
this
.
innerX
),
0
);
h
=
Math
.
max
(
Math
.
min
(
h
,
this
.
maxRows
-
this
.
innerY
),
0
);
return
{
w
,
h
};
},
updateWidth
:
function
(
width
,
colNum
)
{
this
.
containerWidth
=
width
;
if
(
colNum
!==
undefined
&&
colNum
!==
null
)
{
this
.
cols
=
colNum
;
}
},
compact
:
function
()
{
this
.
createStyle
();
},
tryMakeDraggable
:
function
(){
const
self
=
this
;
if
(
this
.
interactObj
===
null
||
this
.
interactObj
===
undefined
)
{
this
.
interactObj
=
interact
(
this
.
$refs
.
item
);
if
(
!
this
.
useStyleCursor
)
{
this
.
interactObj
.
styleCursor
(
false
);
}
}
if
(
this
.
draggable
&&
!
this
.
static
)
{
const
opts
=
{
ignoreFrom
:
this
.
dragIgnoreFrom
,
allowFrom
:
this
.
dragAllowFrom
};
this
.
interactObj
.
draggable
(
opts
);
/*this.interactObj.draggable({allowFrom: '.vue-draggable-handle'});*/
if
(
!
this
.
dragEventSet
)
{
this
.
dragEventSet
=
true
;
this
.
interactObj
.
on
(
'
dragstart dragmove dragend
'
,
function
(
event
)
{
self
.
handleDrag
(
event
);
});
}
}
else
{
this
.
interactObj
.
draggable
({
enabled
:
false
});
}
},
tryMakeResizable
:
function
(){
const
self
=
this
;
if
(
this
.
interactObj
===
null
||
this
.
interactObj
===
undefined
)
{
this
.
interactObj
=
interact
(
this
.
$refs
.
item
);
if
(
!
this
.
useStyleCursor
)
{
this
.
interactObj
.
styleCursor
(
false
);
}
}
if
(
this
.
resizable
&&
!
this
.
static
)
{
let
maximum
=
this
.
calcPosition
(
0
,
0
,
this
.
maxW
,
this
.
maxH
);
let
minimum
=
this
.
calcPosition
(
0
,
0
,
this
.
minW
,
this
.
minH
);
// console.log("### MAX " + JSON.stringify(maximum));
// console.log("### MIN " + JSON.stringify(minimum));
const
opts
=
{
preserveAspectRatio
:
true
,
// allowFrom: "." + this.resizableHandleClass.trim().replace(" ", "."),
edges
:
{
left
:
false
,
right
:
"
.
"
+
this
.
resizableHandleClass
.
trim
().
replace
(
"
"
,
"
.
"
),
bottom
:
"
.
"
+
this
.
resizableHandleClass
.
trim
().
replace
(
"
"
,
"
.
"
),
top
:
false
},
ignoreFrom
:
this
.
resizeIgnoreFrom
,
restrictSize
:
{
min
:
{
height
:
minimum
.
height
,
width
:
minimum
.
width
},
max
:
{
height
:
maximum
.
height
,
width
:
maximum
.
width
}
}
};
this
.
interactObj
.
resizable
(
opts
);
if
(
!
this
.
resizeEventSet
)
{
this
.
resizeEventSet
=
true
;
this
.
interactObj
.
on
(
'
resizestart resizemove resizeend
'
,
function
(
event
)
{
self
.
handleResize
(
event
);
});
}
}
else
{
this
.
interactObj
.
resizable
({
enabled
:
false
});
}
},
autoSize
:
function
()
{
// ok here we want to calculate if a resize is needed
this
.
previousW
=
this
.
innerW
;
this
.
previousH
=
this
.
innerH
;
let
newSize
=
this
.
$slots
.
default
[
0
].
elm
.
getBoundingClientRect
();
let
pos
=
this
.
calcWH
(
newSize
.
height
,
newSize
.
width
);
if
(
pos
.
w
<
this
.
minW
)
{
pos
.
w
=
this
.
minW
;
}
if
(
pos
.
w
>
this
.
maxW
)
{
pos
.
w
=
this
.
maxW
;
}
if
(
pos
.
h
<
this
.
minH
)
{
pos
.
h
=
this
.
minH
;
}
if
(
pos
.
h
>
this
.
maxH
)
{
pos
.
h
=
this
.
maxH
;
}
if
(
pos
.
h
<
1
)
{
pos
.
h
=
1
;
}
if
(
pos
.
w
<
1
)
{
pos
.
w
=
1
;
}
// this.lastW = x; // basically, this is copied from resizehandler, but shouldn't be needed
// this.lastH = y;
if
(
this
.
innerW
!==
pos
.
w
||
this
.
innerH
!==
pos
.
h
)
{
this
.
$emit
(
"
resize
"
,
this
.
i
,
pos
.
h
,
pos
.
w
,
newSize
.
height
,
newSize
.
width
);
}
if
(
this
.
previousW
!==
pos
.
w
||
this
.
previousH
!==
pos
.
h
)
{
this
.
$emit
(
"
resized
"
,
this
.
i
,
pos
.
h
,
pos
.
w
,
newSize
.
height
,
newSize
.
width
);
this
.
eventBus
.
$emit
(
"
resizeEvent
"
,
"
resizeend
"
,
this
.
i
,
this
.
innerX
,
this
.
innerY
,
pos
.
h
,
pos
.
w
);
}
}
},
}
</
script
>
app/web/page/activity/component/VueGridLayout/GridLayout.vue
deleted
100644 → 0
View file @
91acb5e9
<
template
>
<div
ref=
"item"
class=
"vue-grid-layout"
:style=
"mergedStyle"
>
<slot></slot>
<grid-item
class=
"vue-grid-placeholder"
v-show=
"isDragging"
:x=
"placeholder.x"
:y=
"placeholder.y"
:w=
"placeholder.w"
:h=
"placeholder.h"
:i=
"placeholder.i"
></grid-item>
</div>
</
template
>
<
style
>
.vue-grid-layout
{
position
:
relative
;
transition
:
height
200ms
ease
;
}
</
style
>
<
script
>
import
Vue
from
'
vue
'
;
const
elementResizeDetectorMaker
=
require
(
"
element-resize-detector
"
);
import
{
bottom
,
compact
,
getLayoutItem
,
moveElement
,
validateLayout
,
cloneLayout
,
getAllCollisions
}
from
'
./helpers/utils
'
;
import
{
getBreakpointFromWidth
,
getColsFromBreakpoint
,
findOrGenerateResponsiveLayout
}
from
"
./helpers/responsiveUtils
"
;
//var eventBus = require('./eventBus');
import
GridItem
from
'
./GridItem.vue
'
import
{
addWindowEventListener
,
removeWindowEventListener
}
from
"
./helpers/DOM
"
;
export
default
{
name
:
"
GridLayout
"
,
provide
()
{
return
{
eventBus
:
null
,
layout
:
this
}
},
components
:
{
GridItem
,
},
props
:
{
// If true, the container height swells and contracts to fit contents
autoSize
:
{
type
:
Boolean
,
default
:
true
},
colNum
:
{
type
:
Number
,
default
:
12
},
rowHeight
:
{
type
:
Number
,
default
:
150
},
maxRows
:
{
type
:
Number
,
default
:
Infinity
},
margin
:
{
type
:
Array
,
default
:
function
()
{
return
[
10
,
10
];
}
},
isDraggable
:
{
type
:
Boolean
,
default
:
true
},
isResizable
:
{
type
:
Boolean
,
default
:
true
},
isMirrored
:
{
type
:
Boolean
,
default
:
false
},
useCssTransforms
:
{
type
:
Boolean
,
default
:
true
},
verticalCompact
:
{
type
:
Boolean
,
default
:
true
},
layout
:
{
type
:
Array
,
required
:
true
,
},
responsive
:
{
type
:
Boolean
,
default
:
false
},
responsiveLayouts
:
{
type
:
Object
,
default
:
function
()
{
return
{};
}
},
breakpoints
:{
type
:
Object
,
default
:
function
(){
return
{
lg
:
1200
,
md
:
996
,
sm
:
768
,
xs
:
480
,
xxs
:
0
}}
},
cols
:{
type
:
Object
,
default
:
function
(){
return
{
lg
:
12
,
md
:
10
,
sm
:
6
,
xs
:
4
,
xxs
:
2
}},
},
preventCollision
:
{
type
:
Boolean
,
default
:
false
},
useStyleCursor
:
{
type
:
Boolean
,
default
:
true
}
},
data
:
function
()
{
return
{
width
:
null
,
mergedStyle
:
{},
lastLayoutLength
:
0
,
isDragging
:
false
,
placeholder
:
{
x
:
0
,
y
:
0
,
w
:
0
,
h
:
0
,
i
:
-
1
},
layouts
:
{},
// array to store all layouts from different breakpoints
lastBreakpoint
:
null
,
// store last active breakpoint
originalLayout
:
null
,
// store original Layout
};
},
created
()
{
const
self
=
this
;
// Accessible refernces of functions for removing in beforeDestroy
self
.
resizeEventHandler
=
function
(
eventType
,
i
,
x
,
y
,
h
,
w
)
{
self
.
resizeEvent
(
eventType
,
i
,
x
,
y
,
h
,
w
);
};
self
.
dragEventHandler
=
function
(
eventType
,
i
,
x
,
y
,
h
,
w
)
{
self
.
dragEvent
(
eventType
,
i
,
x
,
y
,
h
,
w
);
};
self
.
_provided
.
eventBus
=
new
Vue
();
self
.
eventBus
=
self
.
_provided
.
eventBus
;
self
.
eventBus
.
$on
(
'
resizeEvent
'
,
self
.
resizeEventHandler
);
self
.
eventBus
.
$on
(
'
dragEvent
'
,
self
.
dragEventHandler
);
self
.
$emit
(
'
layout-created
'
,
self
.
layout
);
},
beforeDestroy
:
function
(){
//Remove listeners
this
.
eventBus
.
$off
(
'
resizeEvent
'
,
this
.
resizeEventHandler
);
this
.
eventBus
.
$off
(
'
dragEvent
'
,
this
.
dragEventHandler
);
this
.
eventBus
.
$destroy
();
removeWindowEventListener
(
"
resize
"
,
this
.
onWindowResize
);
this
.
erd
.
uninstall
(
this
.
$refs
.
item
);
},
beforeMount
:
function
()
{
this
.
$emit
(
'
layout-before-mount
'
,
this
.
layout
);
},
mounted
:
function
()
{
this
.
$emit
(
'
layout-mounted
'
,
this
.
layout
);
this
.
$nextTick
(
function
()
{
validateLayout
(
this
.
layout
);
this
.
originalLayout
=
this
.
layout
;
const
self
=
this
;
this
.
$nextTick
(
function
()
{
self
.
onWindowResize
();
self
.
initResponsiveFeatures
();
//self.width = self.$el.offsetWidth;
addWindowEventListener
(
'
resize
'
,
self
.
onWindowResize
);
compact
(
self
.
layout
,
self
.
verticalCompact
);
self
.
$emit
(
'
layout-updated
'
,
self
.
layout
)
self
.
updateHeight
();
self
.
$nextTick
(
function
()
{
this
.
erd
=
elementResizeDetectorMaker
({
strategy
:
"
scroll
"
,
//
<-
For
ultra
performance
.
// See https://github.com/wnr/element-resize-detector/issues/110 about callOnAdd.
callOnAdd
:
false
,
});
this
.
erd
.
listenTo
(
self
.
$refs
.
item
,
function
()
{
self
.
onWindowResize
();
});
});
});
});
},
watch
:
{
width
:
function
(
newval
,
oldval
)
{
const
self
=
this
;
this
.
$nextTick
(
function
()
{
//this.$broadcast("updateWidth", this.width);
this
.
eventBus
.
$emit
(
"
updateWidth
"
,
this
.
width
);
if
(
oldval
===
null
)
{
/*
If oldval == null is when the width has never been
set before. That only occurs when mouting is
finished, and onWindowResize has been called and
this.width has been changed the first time after it
got set to null in the constructor. It is now time
to issue layout-ready events as the GridItems have
their sizes configured properly.
The reason for emitting the layout-ready events on
the next tick is to allow for the newly-emitted
updateWidth event (above) to have reached the
children GridItem-s and had their effect, so we're
sure that they have the final size before we emit
layout-ready (for this GridLayout) and
item-layout-ready (for the GridItem-s).
This way any client event handlers can reliably
invistigate stable sizes of GridItem-s.
*/
this
.
$nextTick
(()
=>
{
this
.
$emit
(
'
layout-ready
'
,
self
.
layout
);
});
}
this
.
updateHeight
();
});
},
layout
:
function
()
{
this
.
layoutUpdate
();
},
colNum
:
function
(
val
)
{
this
.
eventBus
.
$emit
(
"
setColNum
"
,
val
);
},
rowHeight
:
function
()
{
this
.
eventBus
.
$emit
(
"
setRowHeight
"
,
this
.
rowHeight
);
},
isDraggable
:
function
()
{
this
.
eventBus
.
$emit
(
"
setDraggable
"
,
this
.
isDraggable
);
},
isResizable
:
function
()
{
this
.
eventBus
.
$emit
(
"
setResizable
"
,
this
.
isResizable
);
},
responsive
()
{
if
(
!
this
.
responsive
)
{
this
.
$emit
(
'
update:layout
'
,
this
.
originalLayout
);
this
.
eventBus
.
$emit
(
"
setColNum
"
,
this
.
colNum
);
}
this
.
onWindowResize
();
},
maxRows
:
function
()
{
this
.
eventBus
.
$emit
(
"
setMaxRows
"
,
this
.
maxRows
);
},
margin
()
{
this
.
updateHeight
();
}
},
methods
:
{
layoutUpdate
()
{
if
(
this
.
layout
!==
undefined
&&
this
.
originalLayout
!==
null
)
{
if
(
this
.
layout
.
length
!==
this
.
originalLayout
.
length
)
{
// console.log("### LAYOUT UPDATE!", this.layout.length, this.originalLayout.length);
let
diff
=
this
.
findDifference
(
this
.
layout
,
this
.
originalLayout
);
if
(
diff
.
length
>
0
){
// console.log(diff);
if
(
this
.
layout
.
length
>
this
.
originalLayout
.
length
)
{
this
.
originalLayout
=
this
.
originalLayout
.
concat
(
diff
);
}
else
{
this
.
originalLayout
=
this
.
originalLayout
.
filter
(
obj
=>
{
return
!
diff
.
some
(
obj2
=>
{
return
obj
.
i
===
obj2
.
i
;
});
});
}
}
this
.
lastLayoutLength
=
this
.
layout
.
length
;
this
.
initResponsiveFeatures
();
}
compact
(
this
.
layout
,
this
.
verticalCompact
);
this
.
eventBus
.
$emit
(
"
updateWidth
"
,
this
.
width
);
this
.
updateHeight
();
this
.
$emit
(
'
layout-updated
'
,
this
.
layout
)
}
},
updateHeight
:
function
()
{
this
.
mergedStyle
=
{
height
:
this
.
containerHeight
()
};
},
onWindowResize
:
function
()
{
if
(
this
.
$refs
!==
null
&&
this
.
$refs
.
item
!==
null
&&
this
.
$refs
.
item
!==
undefined
)
{
this
.
width
=
this
.
$refs
.
item
.
offsetWidth
;
}
this
.
eventBus
.
$emit
(
"
resizeEvent
"
);
},
containerHeight
:
function
()
{
if
(
!
this
.
autoSize
)
return
;
// console.log("bottom: " + bottom(this.layout))
// console.log("rowHeight + margins: " + (this.rowHeight + this.margin[1]) + this.margin[1])
const
containerHeight
=
bottom
(
this
.
layout
)
*
(
this
.
rowHeight
+
this
.
margin
[
1
])
+
this
.
margin
[
1
]
+
'
px
'
;
return
containerHeight
;
},
dragEvent
:
function
(
eventName
,
id
,
x
,
y
,
h
,
w
)
{
//console.log(eventName + " id=" + id + ", x=" + x + ", y=" + y);
let
l
=
getLayoutItem
(
this
.
layout
,
id
);
//GetLayoutItem sometimes returns null object
if
(
l
===
undefined
||
l
===
null
){
l
=
{
x
:
0
,
y
:
0
}
}
if
(
eventName
===
"
dragmove
"
||
eventName
===
"
dragstart
"
)
{
this
.
placeholder
.
i
=
id
;
this
.
placeholder
.
x
=
l
.
x
;
this
.
placeholder
.
y
=
l
.
y
;
this
.
placeholder
.
w
=
w
;
this
.
placeholder
.
h
=
h
;
this
.
$nextTick
(
function
()
{
this
.
isDragging
=
true
;
});
//this.$broadcast("updateWidth", this.width);
this
.
eventBus
.
$emit
(
"
updateWidth
"
,
this
.
width
);
}
else
{
this
.
$nextTick
(
function
()
{
this
.
isDragging
=
false
;
});
}
// Move the element to the dragged location.
this
.
layout
=
moveElement
(
this
.
layout
,
l
,
x
,
y
,
true
,
this
.
preventCollision
);
compact
(
this
.
layout
,
this
.
verticalCompact
);
// needed because vue can't detect changes on array element properties
this
.
eventBus
.
$emit
(
"
compact
"
);
this
.
updateHeight
();
if
(
eventName
===
'
dragend
'
)
this
.
$emit
(
'
layout-updated
'
,
this
.
layout
);
},
resizeEvent
:
function
(
eventName
,
id
,
x
,
y
,
h
,
w
)
{
let
l
=
getLayoutItem
(
this
.
layout
,
id
);
//GetLayoutItem sometimes return null object
if
(
l
===
undefined
||
l
===
null
){
l
=
{
h
:
0
,
w
:
0
}
}
let
hasCollisions
;
if
(
this
.
preventCollision
)
{
const
collisions
=
getAllCollisions
(
this
.
layout
,
{
...
l
,
w
,
h
}).
filter
(
layoutItem
=>
layoutItem
.
i
!==
l
.
i
);
hasCollisions
=
collisions
.
length
>
0
;
// If we're colliding, we need adjust the placeholder.
if
(
hasCollisions
)
{
// adjust w && h to maximum allowed space
let
leastX
=
Infinity
,
leastY
=
Infinity
;
collisions
.
forEach
(
layoutItem
=>
{
if
(
layoutItem
.
x
>
l
.
x
)
leastX
=
Math
.
min
(
leastX
,
layoutItem
.
x
);
if
(
layoutItem
.
y
>
l
.
y
)
leastY
=
Math
.
min
(
leastY
,
layoutItem
.
y
);
});
if
(
Number
.
isFinite
(
leastX
))
l
.
w
=
leastX
-
l
.
x
;
if
(
Number
.
isFinite
(
leastY
))
l
.
h
=
leastY
-
l
.
y
;
}
}
if
(
!
hasCollisions
)
{
// Set new width and height.
l
.
w
=
w
;
l
.
h
=
h
;
}
if
(
eventName
===
"
resizestart
"
||
eventName
===
"
resizemove
"
)
{
this
.
placeholder
.
i
=
id
;
this
.
placeholder
.
x
=
x
;
this
.
placeholder
.
y
=
y
;
this
.
placeholder
.
w
=
l
.
w
;
this
.
placeholder
.
h
=
l
.
h
;
this
.
$nextTick
(
function
()
{
this
.
isDragging
=
true
;
});
//this.$broadcast("updateWidth", this.width);
this
.
eventBus
.
$emit
(
"
updateWidth
"
,
this
.
width
);
}
else
{
this
.
$nextTick
(
function
()
{
this
.
isDragging
=
false
;
});
}
if
(
this
.
responsive
)
this
.
responsiveGridLayout
();
compact
(
this
.
layout
,
this
.
verticalCompact
);
this
.
eventBus
.
$emit
(
"
compact
"
);
this
.
updateHeight
();
if
(
eventName
===
'
resizeend
'
)
this
.
$emit
(
'
layout-updated
'
,
this
.
layout
);
},
// finds or generates new layouts for set breakpoints
responsiveGridLayout
(){
let
newBreakpoint
=
getBreakpointFromWidth
(
this
.
breakpoints
,
this
.
width
);
let
newCols
=
getColsFromBreakpoint
(
newBreakpoint
,
this
.
cols
);
// save actual layout in layouts
if
(
this
.
lastBreakpoint
!=
null
&&
!
this
.
layouts
[
this
.
lastBreakpoint
])
this
.
layouts
[
this
.
lastBreakpoint
]
=
cloneLayout
(
this
.
layout
);
// Find or generate a new layout.
let
layout
=
findOrGenerateResponsiveLayout
(
this
.
originalLayout
,
this
.
layouts
,
this
.
breakpoints
,
newBreakpoint
,
this
.
lastBreakpoint
,
newCols
,
this
.
verticalCompact
);
// Store the new layout.
this
.
layouts
[
newBreakpoint
]
=
layout
;
if
(
this
.
lastBreakpoint
!==
newBreakpoint
)
{
this
.
$emit
(
'
breakpoint-changed
'
,
newBreakpoint
,
layout
);
}
// new prop sync
this
.
$emit
(
'
update:layout
'
,
layout
);
this
.
lastBreakpoint
=
newBreakpoint
;
this
.
eventBus
.
$emit
(
"
setColNum
"
,
getColsFromBreakpoint
(
newBreakpoint
,
this
.
cols
));
},
// clear all responsive layouts
initResponsiveFeatures
(){
// clear layouts
this
.
layouts
=
Object
.
assign
({},
this
.
responsiveLayouts
);
},
// find difference in layouts
findDifference
(
layout
,
originalLayout
){
//Find values that are in result1 but not in result2
let
uniqueResultOne
=
layout
.
filter
(
function
(
obj
)
{
return
!
originalLayout
.
some
(
function
(
obj2
)
{
return
obj
.
i
===
obj2
.
i
;
});
});
//Find values that are in result2 but not in result1
let
uniqueResultTwo
=
originalLayout
.
filter
(
function
(
obj
)
{
return
!
layout
.
some
(
function
(
obj2
)
{
return
obj
.
i
===
obj2
.
i
;
});
});
//Combine the two arrays of unique entries#
return
uniqueResultOne
.
concat
(
uniqueResultTwo
);
}
},
}
</
script
>
app/web/page/activity/component/VueGridLayout/helpers/DOM.ts
deleted
100644 → 0
View file @
91acb5e9
let
currentDir
=
'
auto
'
;
// let currentDir = "auto";
function
hasDocument
()
{
return
(
typeof
document
!==
'
undefined
'
);
}
function
hasWindow
()
{
return
(
typeof
window
!==
'
undefined
'
);
}
export
function
getDocumentDir
()
{
if
(
!
hasDocument
())
{
return
currentDir
;
}
const
direction
=
(
typeof
document
.
dir
!==
'
undefined
'
)
?
document
.
dir
:
document
.
getElementsByTagName
(
'
html
'
)[
0
].
getAttribute
(
'
dir
'
);
return
direction
;
}
export
function
setDocumentDir
(
dir
)
{
// export function setDocumentDir(dir){
if
(
!
hasDocument
)
{
currentDir
=
dir
;
return
;
}
const
html
=
document
.
getElementsByTagName
(
'
html
'
)[
0
];
html
.
setAttribute
(
'
dir
'
,
dir
);
}
export
function
addWindowEventListener
(
event
,
callback
)
{
if
(
!
hasWindow
)
{
callback
();
return
;
}
window
.
addEventListener
(
event
,
callback
);
}
export
function
removeWindowEventListener
(
event
,
callback
)
{
if
(
!
hasWindow
)
{
return
;
}
window
.
removeEventListener
(
event
,
callback
);
}
app/web/page/activity/component/VueGridLayout/helpers/draggableUtils.ts
deleted
100644 → 0
View file @
91acb5e9
// Get {x, y} positions from event.
export
function
getControlPosition
(
e
)
{
return
offsetXYFromParentOf
(
e
);
}
// Get from offsetParent
export
function
offsetXYFromParentOf
(
evt
)
{
const
offsetParent
=
evt
.
target
.
offsetParent
||
document
.
body
;
const
offsetParentRect
=
evt
.
offsetParent
===
document
.
body
?
{
left
:
0
,
top
:
0
}
:
offsetParent
.
getBoundingClientRect
();
const
x
=
evt
.
clientX
+
offsetParent
.
scrollLeft
-
offsetParentRect
.
left
;
const
y
=
evt
.
clientY
+
offsetParent
.
scrollTop
-
offsetParentRect
.
top
;
/*const x = Math.round(evt.clientX + offsetParent.scrollLeft - offsetParentRect.left);
const y = Math.round(evt.clientY + offsetParent.scrollTop - offsetParentRect.top);*/
return
{
x
,
y
};
}
// Create an data object exposed by <DraggableCore>'s events
export
function
createCoreData
(
lastX
,
lastY
,
x
,
y
)
{
// State changes are often (but not always!) async. We want the latest value.
const
isStart
=
!
isNum
(
lastX
);
if
(
isStart
)
{
// If this is our first move, use the x and y as last coords.
return
{
deltaX
:
0
,
deltaY
:
0
,
lastX
:
x
,
lastY
:
y
,
x
,
y
};
}
else
{
// Otherwise calculate proper values.
return
{
deltaX
:
x
-
lastX
,
deltaY
:
y
-
lastY
,
lastX
,
lastY
,
x
,
y
};
}
}
function
isNum
(
num
)
{
return
typeof
num
===
'
number
'
&&
!
isNaN
(
num
);
}
app/web/page/activity/component/VueGridLayout/helpers/responsiveUtils.ts
deleted
100644 → 0
View file @
91acb5e9
// @flow
import
{
cloneLayout
,
compact
,
correctBounds
}
from
'
./utils
'
;
// import type {Layout} from './utils';
// export type ResponsiveLayout = {lg, md, sm, xs, xxs};
// type Breakpoint = string;
// type Breakpoints = {lg, md, sm, xs, xxs};
/**
* Given a width, find the highest breakpoint that matches is valid for it (width > breakpoint).
*
* @param {Object} breakpoints Breakpoints object (e.g. {lg: 1200, md: 960, ...})
* @param {Number} width Screen width.
* @return {String} Highest breakpoint that is less than width.
*/
export
function
getBreakpointFromWidth
(
breakpoints
,
width
)
{
const
sorted
=
sortBreakpoints
(
breakpoints
);
let
matching
=
sorted
[
0
];
for
(
let
i
=
1
,
len
=
sorted
.
length
;
i
<
len
;
i
++
)
{
const
breakpointName
=
sorted
[
i
];
if
(
width
>
breakpoints
[
breakpointName
])
{
matching
=
breakpointName
;
}
}
return
matching
;
}
/**
* Given a breakpoint, get the # of cols set for it.
* @param {String} breakpoint Breakpoint name.
* @param {Object} cols Map of breakpoints to cols.
* @return {Number} Number of cols.
*/
export
function
getColsFromBreakpoint
(
breakpoint
,
cols
)
{
if
(
!
cols
[
breakpoint
])
{
throw
new
Error
(
'
ResponsiveGridLayout: `cols` entry for breakpoint
'
+
breakpoint
+
'
is missing!
'
);
}
return
cols
[
breakpoint
];
}
/**
* Given existing layouts and a new breakpoint, find or generate a new layout.
*
* This finds the layout above the new one and generates from it, if it exists.
*
* @param {Array} orgLayout Original layout.
* @param {Object} layouts Existing layouts.
* @param {Array} breakpoints All breakpoints.
* @param {String} breakpoint New breakpoint.
* @param {String} breakpoint Last breakpoint (for fallback).
* @param {Number} cols Column count at new breakpoint.
* @param {Boolean} verticalCompact Whether or not to compact the layout
* vertically.
* @return {Array} New layout.
*/
export
function
findOrGenerateResponsiveLayout
(
orgLayout
,
layouts
,
breakpoints
,
breakpoint
,
lastBreakpoint
,
cols
,
verticalCompact
)
{
// If it already exists, just return it.
if
(
layouts
[
breakpoint
])
{
return
cloneLayout
(
layouts
[
breakpoint
]);
}
// Find or generate the next layout
let
layout
=
orgLayout
;
const
breakpointsSorted
=
sortBreakpoints
(
breakpoints
);
const
breakpointsAbove
=
breakpointsSorted
.
slice
(
breakpointsSorted
.
indexOf
(
breakpoint
));
for
(
let
i
=
0
,
len
=
breakpointsAbove
.
length
;
i
<
len
;
i
++
)
{
const
b
=
breakpointsAbove
[
i
];
if
(
layouts
[
b
])
{
layout
=
layouts
[
b
];
break
;
}
}
layout
=
cloneLayout
(
layout
||
[]);
// clone layout so we don't modify existing items
return
compact
(
correctBounds
(
layout
,
{
cols
}),
verticalCompact
);
}
export
function
generateResponsiveLayout
(
layout
,
breakpoints
,
breakpoint
,
lastBreakpoint
,
cols
,
verticalCompact
)
{
// If it already exists, just return it.
/*if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint]);
// Find or generate the next layout
let layout = layouts[lastBreakpoint];*/
/*const breakpointsSorted = sortBreakpoints(breakpoints);
const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint));
for (let i = 0, len = breakpointsAbove.length; i < len; i++) {
const b = breakpointsAbove[i];
if (layouts[b]) {
layout = layouts[b];
break;
}
}*/
layout
=
cloneLayout
(
layout
||
[]);
// clone layout so we don't modify existing items
return
compact
(
correctBounds
(
layout
,
{
cols
}),
verticalCompact
);
}
/**
* Given breakpoints, return an array of breakpoints sorted by width. This is usually
* e.g. ['xxs', 'xs', 'sm', ...]
*
* @param {Object} breakpoints Key/value pair of breakpoint names to widths.
* @return {Array} Sorted breakpoints.
*/
export
function
sortBreakpoints
(
breakpoints
)
{
const
keys
=
Object
.
keys
(
breakpoints
);
return
keys
.
sort
(
function
(
a
,
b
)
{
return
breakpoints
[
a
]
-
breakpoints
[
b
];
});
}
app/web/page/activity/component/VueGridLayout/helpers/utils.ts
deleted
100644 → 0
View file @
91acb5e9
// tslint:disable
// @flow
// export type LayoutItemRequired = {w, h, x, y, i};
// export type LayoutItem = LayoutItemRequired &
// {minW?, minH?, maxW?, maxH?,
// moved?, static?,
// isDraggable?, isResizable?};
// export type Layout = Array<LayoutItem>;
// export type Position = {left, top, width, height};
/*
export type DragCallbackData = {
node: HTMLElement,
x, y,
deltaX, deltaY,
lastX, lastY
};
*/
// export type DragEvent = {e: Event} & DragCallbackData;
// export type Size = {width, height};
// export type ResizeEvent = {e: Event, node: HTMLElement, size: Size};
// const isProduction = process.env.NODE_ENV === 'production';
/**
* Return the bottom coordinate of the layout.
*
* @param {Array} layout Layout array.
* @return {Number} Bottom coordinate.
*/
export
function
bottom
(
layout
)
{
let
max
=
0
,
bottomY
;
for
(
let
i
=
0
,
len
=
layout
.
length
;
i
<
len
;
i
++
)
{
bottomY
=
layout
[
i
].
y
+
layout
[
i
].
h
;
if
(
bottomY
>
max
)
{
max
=
bottomY
;
}
}
return
max
;
}
export
function
cloneLayout
(
layout
)
{
const
newLayout
=
Array
(
layout
.
length
);
for
(
let
i
=
0
,
len
=
layout
.
length
;
i
<
len
;
i
++
)
{
newLayout
[
i
]
=
cloneLayoutItem
(
layout
[
i
]);
}
return
newLayout
;
}
// Fast path to cloning, since this is monomorphic
export
function
cloneLayoutItem
(
layoutItem
)
{
/*return {
w.w, h.h, x.x, y.y, i.i,
minW.minW, maxW.maxW, minH.minH, maxH.maxH,
moved(layoutItem.moved), static(layoutItem.static),
// These can be null
isDraggable.isDraggable, isResizable.isResizable
};*/
return
JSON
.
parse
(
JSON
.
stringify
(
layoutItem
));
}
/**
* Given two layoutitems, check if they collide.
*
* @return {Boolean} True if colliding.
*/
export
function
collides
(
l1
,
l2
)
{
if
(
l1
===
l2
)
{
return
false
;
}
// same element
if
(
l1
.
x
+
l1
.
w
<=
l2
.
x
)
{
return
false
;
}
// l1 is left of l2
if
(
l1
.
x
>=
l2
.
x
+
l2
.
w
)
{
return
false
;
}
// l1 is right of l2
if
(
l1
.
y
+
l1
.
h
<=
l2
.
y
)
{
return
false
;
}
// l1 is above l2
if
(
l1
.
y
>=
l2
.
y
+
l2
.
h
)
{
return
false
;
}
// l1 is below l2
return
true
;
// boxes overlap
}
/**
* Given a layout, compact it. This involves going down each y coordinate and removing gaps
* between items.
*
* @param {Array} layout Layout.
* @param {Boolean} verticalCompact Whether or not to compact the layout
* vertically.
* @return {Array} Compacted Layout.
*/
export
function
compact
(
layout
,
verticalCompact
)
{
// Statics go in the compareWith array right away so items flow around them.
const
compareWith
=
getStatics
(
layout
);
// We go through the items by row and column.
const
sorted
=
sortLayoutItemsByRowCol
(
layout
);
// Holding for new items.
const
out
=
Array
(
layout
.
length
);
for
(
let
i
=
0
,
len
=
sorted
.
length
;
i
<
len
;
i
++
)
{
let
l
=
sorted
[
i
];
// Don't move static elements
if
(
!
l
.
static
)
{
l
=
compactItem
(
compareWith
,
l
,
verticalCompact
);
// Add to comparison array. We only collide with items before this one.
// Statics are already in this array.
compareWith
.
push
(
l
);
}
// Add to output array to make sure they still come out in the right order.
out
[
layout
.
indexOf
(
l
)]
=
l
;
// Clear moved flag, if it exists.
l
.
moved
=
false
;
}
return
out
;
}
/**
* Compact an item in the layout.
*/
export
function
compactItem
(
compareWith
,
l
,
verticalCompact
)
{
if
(
verticalCompact
)
{
// Move the element up as far as it can go without colliding.
while
(
l
.
y
>
0
&&
!
getFirstCollision
(
compareWith
,
l
))
{
l
.
y
--
;
}
}
// Move it down, and keep moving it down if it's colliding.
let
collides
;
while
((
collides
=
getFirstCollision
(
compareWith
,
l
)))
{
l
.
y
=
collides
.
y
+
collides
.
h
;
}
return
l
;
}
/**
* Given a layout, make sure all elements fit within its bounds.
*
* @param {Array} layout Layout array.
* @param {Number} bounds Number of columns.
*/
export
function
correctBounds
(
layout
,
bounds
)
{
const
collidesWith
=
getStatics
(
layout
);
for
(
let
i
=
0
,
len
=
layout
.
length
;
i
<
len
;
i
++
)
{
const
l
=
layout
[
i
];
// Overflows right
if
(
l
.
x
+
l
.
w
>
bounds
.
cols
)
{
l
.
x
=
bounds
.
cols
-
l
.
w
;
}
// Overflows left
if
(
l
.
x
<
0
)
{
l
.
x
=
0
;
l
.
w
=
bounds
.
cols
;
}
if
(
!
l
.
static
)
{
collidesWith
.
push
(
l
);
}
else
{
// If this is static and collides with other statics, we must move it down.
// We have to do something nicer than just letting them overlap.
while
(
getFirstCollision
(
collidesWith
,
l
))
{
l
.
y
++
;
}
}
}
return
layout
;
}
/**
* Get a layout item by ID. Used so we can override later on if necessary.
*
* @param {Array} layout Layout array.
* @param {String} id ID
* @return {LayoutItem} Item at ID.
*/
export
function
getLayoutItem
(
layout
,
id
)
{
for
(
let
i
=
0
,
len
=
layout
.
length
;
i
<
len
;
i
++
)
{
if
(
layout
[
i
].
i
===
id
)
{
return
layout
[
i
];
}
}
}
/**
* Returns the first item this layout collides with.
* It doesn't appear to matter which order we approach this from, although
* perhaps that is the wrong thing to do.
*
* @param {Object} layoutItem Layout item.
* @return {Object|undefined} A colliding layout item, or undefined.
*/
export
function
getFirstCollision
(
layout
,
layoutItem
)
{
for
(
let
i
=
0
,
len
=
layout
.
length
;
i
<
len
;
i
++
)
{
if
(
collides
(
layout
[
i
],
layoutItem
))
{
return
layout
[
i
];
}
}
}
export
function
getAllCollisions
(
layout
,
layoutItem
)
{
return
layout
.
filter
((
l
)
=>
collides
(
l
,
layoutItem
));
}
/**
* Get all static elements.
* @param {Array} layout Array of layout objects.
* @return {Array} Array of static layout items..
*/
export
function
getStatics
(
layout
)
{
// return [];
return
layout
.
filter
((
l
)
=>
l
.
static
);
}
/**
* Move an element. Responsible for doing cascading movements of other elements.
*
* @param {Array} layout Full layout to modify.
* @param {LayoutItem} l element to move.
* @param {Number} [x] X position in grid units.
* @param {Number} [y] Y position in grid units.
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is
* being dragged/resized by th euser.
*/
export
function
moveElement
(
layout
,
l
,
x
,
y
,
isUserAction
,
preventCollision
)
{
if
(
l
.
static
)
{
return
layout
;
}
// Short-circuit if nothing to do.
// if (l.y === y && l.x === x) return layout;
const
oldX
=
l
.
x
;
const
oldY
=
l
.
y
;
const
movingUp
=
y
&&
l
.
y
>
y
;
// This is quite a bit faster than extending the object
if
(
typeof
x
===
'
number
'
)
{
l
.
x
=
x
;
}
if
(
typeof
y
===
'
number
'
)
{
l
.
y
=
y
;
}
l
.
moved
=
true
;
// If this collides with anything, move it.
// When doing this comparison, we have to sort the items we compare with
// to ensure, in the case of multiple collisions, that we're getting the
// nearest collision.
let
sorted
=
sortLayoutItemsByRowCol
(
layout
);
if
(
movingUp
)
{
sorted
=
sorted
.
reverse
();
}
const
collisions
=
getAllCollisions
(
sorted
,
l
);
if
(
preventCollision
&&
collisions
.
length
)
{
l
.
x
=
oldX
;
l
.
y
=
oldY
;
l
.
moved
=
false
;
return
layout
;
}
// Move each item that collides away from this element.
for
(
let
i
=
0
,
len
=
collisions
.
length
;
i
<
len
;
i
++
)
{
const
collision
=
collisions
[
i
];
// console.log('resolving collision between', l.i, 'at', l.y, 'and', collision.i, 'at', collision.y);
// Short circuit so we can't infinite loop
if
(
collision
.
moved
)
{
continue
;
}
// This makes it feel a bit more precise by waiting to swap for just a bit when moving up.
if
(
l
.
y
>
collision
.
y
&&
l
.
y
-
collision
.
y
>
collision
.
h
/
4
)
{
continue
;
}
// Don't move static items - we have to move *this* element away
if
(
collision
.
static
)
{
layout
=
moveElementAwayFromCollision
(
layout
,
collision
,
l
,
isUserAction
);
}
else
{
layout
=
moveElementAwayFromCollision
(
layout
,
l
,
collision
,
isUserAction
);
}
}
return
layout
;
}
/**
* This is where the magic needs to happen - given a collision, move an element away from the collision.
* We attempt to move it up if there's room, otherwise it goes below.
*
* @param {Array} layout Full layout to modify.
* @param {LayoutItem} collidesWith Layout item we're colliding with.
* @param {LayoutItem} itemToMove Layout item we're moving.
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is being dragged/resized
* by the user.
*/
export
function
moveElementAwayFromCollision
(
layout
,
collidesWith
,
itemToMove
,
isUserAction
)
{
const
preventCollision
=
false
;
// we're already colliding
// If there is enough space above the collision to put this element, move it there.
// We only do this on the main collision as this can get funky in cascades and cause
// unwanted swapping behavior.
if
(
isUserAction
)
{
// Make a mock item so we don't modify the item here, only modify in moveElement.
const
fakeItem
=
{
x
:
itemToMove
.
x
,
y
:
itemToMove
.
y
,
w
:
itemToMove
.
w
,
h
:
itemToMove
.
h
,
i
:
'
-1
'
};
fakeItem
.
y
=
Math
.
max
(
collidesWith
.
y
-
itemToMove
.
h
,
0
);
if
(
!
getFirstCollision
(
layout
,
fakeItem
))
{
return
moveElement
(
layout
,
itemToMove
,
undefined
,
fakeItem
.
y
,
preventCollision
);
}
}
// Previously this was optimized to move below the collision directly, but this can cause problems
// with cascading moves, as an item may actually leapflog a collision and cause a reversal in order.
return
moveElement
(
layout
,
itemToMove
,
undefined
,
itemToMove
.
y
+
1
,
preventCollision
);
}
/**
* Helper to convert a number to a percentage string.
*
* @param {Number} num Any number
* @return {String} That number as a percentage.
*/
export
function
perc
(
num
)
{
return
num
*
100
+
'
%
'
;
}
export
function
setTransform
(
top
,
left
,
width
,
height
)
{
// Replace unitless items with px
const
translate
=
'
translate3d(
'
+
left
+
'
px,
'
+
top
+
'
px, 0)
'
;
return
{
transform
:
translate
,
WebkitTransform
:
translate
,
MozTransform
:
translate
,
msTransform
:
translate
,
OTransform
:
translate
,
width
:
width
+
'
px
'
,
height
:
height
+
'
px
'
,
position
:
'
absolute
'
};
}
/**
* Just like the setTransform method, but instead it will return a negative value of right.
*
* @param top
* @param right
* @param width
* @param height
* @returns {{transform, WebkitTransform, MozTransform, msTransform, OTransform, width, height, position}}
*/
export
function
setTransformRtl
(
top
,
right
,
width
,
height
)
{
// Replace unitless items with px
const
translate
=
'
translate3d(
'
+
right
*
-
1
+
'
px,
'
+
top
+
'
px, 0)
'
;
return
{
transform
:
translate
,
WebkitTransform
:
translate
,
MozTransform
:
translate
,
msTransform
:
translate
,
OTransform
:
translate
,
width
:
width
+
'
px
'
,
height
:
height
+
'
px
'
,
position
:
'
absolute
'
};
}
export
function
setTopLeft
(
top
,
left
,
width
,
height
)
{
return
{
top
:
top
+
'
px
'
,
left
:
left
+
'
px
'
,
width
:
width
+
'
px
'
,
height
:
height
+
'
px
'
,
position
:
'
absolute
'
};
}
/**
* Just like the setTopLeft method, but instead, it will return a right property instead of left.
*
* @param top
* @param right
* @param width
* @param height
* @returns {{top, right, width, height, position}}
*/
export
function
setTopRight
(
top
,
right
,
width
,
height
)
{
return
{
top
:
top
+
'
px
'
,
right
:
right
+
'
px
'
,
width
:
width
+
'
px
'
,
height
:
height
+
'
px
'
,
position
:
'
absolute
'
};
}
/**
* Get layout items sorted from top left to right and down.
*
* @return {Array} Array of layout objects.
* @return {Array} Layout, sorted static items first.
*/
export
function
sortLayoutItemsByRowCol
(
layout
)
{
return
[].
concat
(
layout
).
sort
(
function
(
a
,
b
)
{
if
(
a
.
y
===
b
.
y
&&
a
.
x
===
b
.
x
)
{
return
0
;
}
if
(
a
.
y
>
b
.
y
||
(
a
.
y
===
b
.
y
&&
a
.
x
>
b
.
x
))
{
return
1
;
}
return
-
1
;
});
}
/**
* Generate a layout using the initialLayout and children as a template.
* Missing entries will be added, extraneous ones will be truncated.
*
* @param {Array} initialLayout Layout passed in through props.
* @param {String} breakpoint Current responsive breakpoint.
* @param {Boolean} verticalCompact Whether or not to compact the layout vertically.
* @return {Array} Working layout.
*/
/*
export function synchronizeLayoutWithChildren(initialLayout, children: Array<React.Element>|React.Element,
cols, verticalCompact) {
// ensure 'children' is always an array
if (!Array.isArray(children)) {
children = [children];
}
initialLayout = initialLayout || [];
// Generate one layout item per child.
let layout = [];
for (let i = 0, len = children.length; i < len; i++) {
let newItem;
const child = children[i];
// Don't overwrite if it already exists.
const exists = getLayoutItem(initialLayout, child.key || "1" /!* FIXME satisfies Flow *!/);
if (exists) {
newItem = exists;
} else {
const g = child.props._grid;
// Hey, this item has a _grid property, use it.
if (g) {
if (!isProduction) {
validateLayout([g], 'ReactGridLayout.children');
}
// Validated; add it to the layout. Bottom 'y' possible is the bottom of the layout.
// This allows you to do nice stuff like specify {y: Infinity}
if (verticalCompact) {
newItem = cloneLayoutItem({...g, y: Math.min(bottom(layout), g.y), i: child.key});
} else {
newItem = cloneLayoutItem({...g, y: g.y, i: child.key});
}
}
// Nothing provided: ensure this is added to the bottom
else {
newItem = cloneLayoutItem({w: 1, h: 1, x: 0, y: bottom(layout), i: child.key || "1"});
}
}
layout[i] = newItem;
}
// Correct the layout.
layout = correctBounds(layout, {cols: cols});
layout = compact(layout, verticalCompact);
return layout;
}
*/
/**
* Validate a layout. Throws errors.
*
* @param {Array} layout Array of layout items.
* @param {String} [contextName] Context name for errors.
* @throw {Error} Validation error.
*/
export
function
validateLayout
(
layout
,
contextName
)
{
contextName
=
contextName
||
'
Layout
'
;
const
subProps
=
[
'
x
'
,
'
y
'
,
'
w
'
,
'
h
'
];
if
(
!
Array
.
isArray
(
layout
))
{
throw
new
Error
(
contextName
+
'
must be an array!
'
);
}
for
(
let
i
=
0
,
len
=
layout
.
length
;
i
<
len
;
i
++
)
{
const
item
=
layout
[
i
];
for
(
let
j
=
0
;
j
<
subProps
.
length
;
j
++
)
{
if
(
typeof
item
[
subProps
[
j
]]
!==
'
number
'
)
{
throw
new
Error
(
'
VueGridLayout:
'
+
contextName
+
'
[
'
+
i
+
'
].
'
+
subProps
[
j
]
+
'
must be a number!
'
);
}
}
if
(
item
.
i
&&
typeof
item
.
i
!==
'
string
'
)
{
// number is also ok, so comment the error
// TODO confirm if commenting the line below doesn't cause unexpected problems
// throw new Error('VueGridLayout: ' + contextName + '[' + i + '].i must be a string!');
}
if
(
item
.
static
!==
undefined
&&
typeof
item
.
static
!==
'
boolean
'
)
{
throw
new
Error
(
'
VueGridLayout:
'
+
contextName
+
'
[
'
+
i
+
'
].static must be a boolean!
'
);
}
}
}
// Flow can't really figure this out, so we just use Object
export
function
autoBindHandlers
(
el
,
fns
)
{
fns
.
forEach
((
key
)
=>
el
[
key
]
=
el
[
key
].
bind
(
el
));
}
/**
* Convert a JS object to CSS string. Similar to React's output of CSS.
* @param obj
* @returns {string}
*/
export
function
createMarkup
(
obj
)
{
const
keys
=
Object
.
keys
(
obj
);
if
(
!
keys
.
length
)
{
return
''
;
}
let
i
,
len
=
keys
.
length
;
let
result
=
''
;
for
(
i
=
0
;
i
<
len
;
i
++
)
{
const
key
=
keys
[
i
];
const
val
=
obj
[
key
];
result
+=
hyphenate
(
key
)
+
'
:
'
+
addPx
(
key
,
val
)
+
'
;
'
;
}
return
result
;
}
/* The following list is defined in React's core */
export
let
IS_UNITLESS
=
{
animationIterationCount
:
true
,
boxFlex
:
true
,
boxFlexGroup
:
true
,
boxOrdinalGroup
:
true
,
columnCount
:
true
,
flex
:
true
,
flexGrow
:
true
,
flexPositive
:
true
,
flexShrink
:
true
,
flexNegative
:
true
,
flexOrder
:
true
,
gridRow
:
true
,
gridColumn
:
true
,
fontWeight
:
true
,
lineClamp
:
true
,
lineHeight
:
true
,
opacity
:
true
,
order
:
true
,
orphans
:
true
,
tabSize
:
true
,
widows
:
true
,
zIndex
:
true
,
zoom
:
true
,
// SVG-related properties
fillOpacity
:
true
,
stopOpacity
:
true
,
strokeDashoffset
:
true
,
strokeOpacity
:
true
,
strokeWidth
:
true
};
/**
* Will add px to the end of style values which are Numbers.
* @param name
* @param value
* @returns {*}
*/
export
function
addPx
(
name
,
value
)
{
if
(
typeof
value
===
'
number
'
&&
!
IS_UNITLESS
[
name
])
{
return
value
+
'
px
'
;
}
else
{
return
value
;
}
}
/**
* Hyphenate a camelCase string.
*
* @param {String} str
* @return {String}
*/
export
let
hyphenateRE
=
/
([
a-z
\d])([
A-Z
])
/g
;
export
function
hyphenate
(
str
)
{
return
str
.
replace
(
hyphenateRE
,
'
$1-$2
'
).
toLowerCase
();
}
export
function
findItemInArray
(
array
,
property
,
value
)
{
for
(
let
i
=
0
;
i
<
array
.
length
;
i
++
)
{
if
(
array
[
i
][
property
]
==
value
)
{
return
true
;
}
}
return
false
;
}
export
function
findAndRemove
(
array
,
property
,
value
)
{
array
.
forEach
(
function
(
result
,
index
)
{
if
(
result
[
property
]
===
value
)
{
// Remove from array
array
.
splice
(
index
,
1
);
}
});
}
app/web/page/activity/index.ts
deleted
100644 → 0
View file @
91acb5e9
'
use strict
'
;
import
App
from
'
../../framework/app
'
;
import
createStore
from
'
../store/index
'
;
import
createRouter
from
'
./router/index
'
;
import
entry
from
'
./view/home/index.vue
'
;
export
default
new
App
({
entry
,
createStore
,
createRouter
}).
bootstrap
();
\ No newline at end of file
app/web/page/activity/router/index.ts
deleted
100644 → 0
View file @
91acb5e9
import
Vue
from
'
vue
'
;
import
VueRouter
from
'
vue-router
'
;
import
Activity
from
'
../view/activity/index.vue
'
;
Vue
.
use
(
VueRouter
);
export
default
function
createRouter
()
{
return
new
VueRouter
({
mode
:
'
history
'
,
routes
:
[
{
path
:
'
/activity/:pageId
'
,
component
:
Activity
}
]
});
}
app/web/page/activity/view/activity/index.ts
deleted
100644 → 0
View file @
91acb5e9
import
{
kebabCase
}
from
'
lodash
'
;
import
{
Vue
,
Component
,
Watch
,
Provide
,
Mixins
}
from
'
vue-property-decorator
'
;
import
eleConfig
from
'
../../../editor/utils/config
'
;
import
FreedomContainer
from
'
../../component/FreedomContainer/index.vue
'
;
import
GridLayout
from
'
../../component/VueGridLayout/GridLayout.vue
'
;
import
GridItem
from
'
../../component/VueGridLayout/GridItem.vue
'
;
// import LoginForm from '@/lib/Form/index.vue';
import
DownloadGuide
from
'
@/lib/DownloadGuide/index.vue
'
;
import
{
Getter
,
State
,
Mutation
}
from
'
vuex-class
'
;
import
GuideCube
from
'
@/lib/GuideCube/index.vue
'
;
import
GoodsTabs
from
'
@/lib/GoodsTabs/index.vue
'
;
import
Coupon
from
'
@/lib/Coupon/index.vue
'
;
import
Advertisement
from
'
@/lib/Advertisement/index.vue
'
;
import
TransformStyleMixin
from
'
@/page/mixins/transformStyle.mixin
'
;
import
{
getStyle
}
from
'
@/service/utils.service
'
;
@
Component
({
components
:
{
FreedomContainer
,
GridLayout
,
GridItem
,
DownloadGuide
,
GoodsTabs
,
GuideCube
,
Advertisement
,
Coupon
},
name
:
'
Activity
'
})
export
default
class
Activity
extends
Mixins
(
TransformStyleMixin
)
{
@
Getter
(
'
pageData
'
)
pageData
;
@
State
(
state
=>
state
.
editor
.
pageInfo
.
pageName
)
pageName
;
@
State
(
state
=>
state
.
editor
.
gridLayout
.
rowHeight
)
rowHeight
;
@
Provide
(
'
editor
'
);
isLayoutComReady
=
false
;
showBackTop
=
false
;
targetEle
:
HTMLElement
|
null
=
null
;
get
layout
()
{
return
this
.
pageData
&&
this
.
pageData
.
elements
.
map
(
v
=>
v
.
point
)
||
[];
}
@
Watch
(
'
pageName
'
,
{
immediate
:
true
})
onPageNameChange
(
newVal
)
{
if
(
EASY_ENV_IS_BROWSER
&&
newVal
)
{
document
.
title
=
newVal
;
// 如果是 iOS 设备,则使用如下 hack 的写法实现页面标题的更新
if
(
navigator
.
userAgent
.
match
(
/
\(
i
[^
;
]
+;
(
U;
)?
CPU.+Mac OS X/
))
{
const
hackIframe
=
document
.
createElement
(
'
iframe
'
);
hackIframe
.
style
.
display
=
'
none
'
;
hackIframe
.
src
=
'
/public/fixIosTitle.html?r=
'
+
Math
.
random
();
document
.
body
.
appendChild
(
hackIframe
);
setTimeout
(()
=>
{
document
.
body
.
removeChild
(
hackIframe
);
},
300
);
}
}
}
mounted
()
{
this
.
targetEle
=
document
.
querySelector
(
'
body
'
);
this
.
showBackTop
=
true
;
setTimeout
(()
=>
{
this
.
modfiTabsStyle
();
},
300
);
}
fetchApi
(
options
)
{
const
{
store
,
route
}
=
options
;
const
{
pageId
}
=
route
.
params
;
console
.
log
(
'
fetchApi
'
,
route
);
return
store
.
dispatch
(
'
getPageDate
'
,
{
pageId
});
}
createStyle
({
h
})
{
return
EASY_ENV_IS_NODE
?
{
height
:
`
${
h
*
this
.
rowHeight
}
px`
,
}
:
{};
}
modfiTabsStyle
()
{
const
tabsEle
=
document
.
querySelector
(
'
.tabs
'
);
if
(
tabsEle
)
{
const
gridItemEle
=
tabsEle
?.
parentNode
;
if
(
gridItemEle
?.
classList
.
contains
(
'
vue-grid-item
'
))
{
// 处理transform
const
transform
=
getStyle
(
gridItemEle
,
'
transform
'
);
const
transformY
=
transform
.
split
(
'
(
'
)[
1
].
split
(
'
)
'
)[
0
].
split
(
'
,
'
)[
5
];
gridItemEle
.
style
.
transform
=
'
none
'
;
gridItemEle
.
style
.
top
=
`
${
transformY
}
px`
;
// 处理backgroundColor
const
backgroundColor
=
getStyle
(
tabsEle
,
'
backgroundColor
'
);
const
crTabs
=
tabsEle
.
childNodes
[
0
];
crTabs
.
style
.
backgroundColor
=
backgroundColor
;
const
stickyEle
=
crTabs
?.
childNodes
[
0
];
if
(
stickyEle
?.
classList
.
contains
(
'
cr-sticky
'
)
&&
stickyEle
?.
childNodes
)
{
stickyEle
.
childNodes
[
0
]?.
style
.
backgroundColor
=
backgroundColor
;
}
}
}
}
}
\ No newline at end of file
app/web/page/activity/view/activity/index.vue
deleted
100644 → 0
View file @
91acb5e9
<
template
>
<div
class=
"activity"
:style=
"transformStyle(pageData.commonStyle)"
>
<grid-layout
:layout.sync=
"layout"
:isDraggable=
"false"
:isResizable=
"false"
:col-num=
"12"
:row-height=
"rowHeight"
:margin=
"[0, 0]"
:is-draggable=
"true"
:is-resizable=
"true"
:is-mirrored=
"false"
:vertical-compact=
"true"
:use-css-transforms=
"true"
@
layout-ready=
"modfiTabsStyle"
>
<grid-item
:style=
"createStyle(item.point)"
v-for=
"(item, index) in pageData.elements"
:x=
"item.point.x"
:y=
"item.point.y"
:w=
"item.point.w"
:h=
"item.point.h"
:i=
"item.point.i"
:key=
"item.point.i"
>
<component
:style=
"transformStyle(item.commonStyle)"
:data-index=
"index"
:containerIndex=
"index"
:childItem=
"item"
:is=
"item.name"
:key=
"index"
v-bind=
"item.props"
></component>
</grid-item>
</grid-layout>
<cr-back-top
v-if=
"showBackTop && pageData.props.showBackTop"
/>
</div>
</
template
>
<
script
lang=
"ts"
src=
"./index.ts"
></
script
>
<
style
lang=
"less"
scoped
>
html,
body,
#app {
height: 100%;
/deep/ .cr-popup {
.cr-notify {
font-size: 14px;
}
}
}
.activity {
width: 100%;
height: 100%;
min-height: 100%;
background-color: rgb(244, 244, 244);
box-shadow: 2px 0px 10px rgba(0, 0, 0, 0.2);
/deep/ .vue-grid-layout {
min-height: 667px;
// transform: translateY(-10px);
transition-property: none;
.vue-grid-item {
transition-property: none;
display: flex;
justify-content: center;
align-items: center;
&>*:first-child {
height: 100%;
}
}
}
}
</
style
>
app/web/page/activity/view/home/index.ts
deleted
100644 → 0
View file @
91acb5e9
import
{
Vue
,
Component
,
Emit
}
from
'
vue-property-decorator
'
;
import
Layout
from
'
component/layout/activity/index.vue
'
;
@
Component
({
components
:
{
Layout
}
})
export
default
class
Home
extends
Vue
{}
\ No newline at end of file
app/web/page/activity/view/home/index.vue
deleted
100644 → 0
View file @
91acb5e9
<
template
>
<Layout>
<router-view></router-view>
</Layout>
</
template
>
<
script
lang=
"ts"
src=
"./index.ts"
></
script
>
app/web/page/editor/component/DynamicForm/component/ColumnSelector/index.vue
View file @
2170c951
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
<Tooltip
placement=
"top"
content=
"两列"
>
<Tooltip
placement=
"top"
content=
"两列"
>
<Button
type=
"ghost"
icon=
"ios-pause"
@
click=
"select(2)"
></Button>
<Button
type=
"ghost"
icon=
"ios-pause"
@
click=
"select(2)"
></Button>
</Tooltip>
</Tooltip>
<Tooltip
placement=
"top"
content=
"
三
列"
>
<Tooltip
placement=
"top"
content=
"
多
列"
>
<Button
type=
"ghost"
icon=
"navicon-round"
@
click=
"select(3)"
></Button>
<Button
type=
"ghost"
icon=
"navicon-round"
@
click=
"select(3)"
></Button>
</Tooltip>
</Tooltip>
</div>
</div>
...
...
app/web/page/editor/view/dashboard/index.ts
View file @
2170c951
...
@@ -93,7 +93,7 @@ export default class DashBoard extends Mixins(ContextMenuMixin, GoodsTabsMixin,
...
@@ -93,7 +93,7 @@ export default class DashBoard extends Mixins(ContextMenuMixin, GoodsTabsMixin,
await
this
.
savePageData
({
pageInfo
,
pageData
:
this
.
pageData
});
await
this
.
savePageData
({
pageInfo
,
pageData
:
this
.
pageData
});
this
.
showSubmitPopup
=
false
;
this
.
showSubmitPopup
=
false
;
if
(
type
===
'
preview
'
)
{
if
(
type
===
'
preview
'
)
{
window
.
open
(
`
${
config
.
api
Host
}
activity/
${
this
.
pageId
}
`
);
window
.
open
(
`
${
config
.
h5
Host
}
activity/
${
this
.
pageId
}
`
);
}
}
}
}
}
}
...
...
app/web/service/http.service.ts
View file @
2170c951
import
axios
from
'
axios
'
;
import
axios
from
'
axios
'
;
import
basicConfig
from
'
../config
'
;
import
basicConfig
from
'
../config
'
;
import
localStorage
from
'
./localStorage.service
'
;
import
localStorage
from
'
./localStorage.service
'
;
import
{
Notify
}
from
'
@qg/cherry-ui
'
;
import
Notify
from
'
@qg/cherry-ui/src/notify
'
;
const
ERR_MESSAGE_MAP
=
{
const
ERR_MESSAGE_MAP
=
{
status
:
{
status
:
{
...
...
app/web/service/qg.service.ts
View file @
2170c951
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
{
// import {
Button
,
// Button,
Image
,
// Image,
Icon
,
// Icon,
Cell
,
// Cell,
CellGroup
,
// CellGroup,
Row
,
// Row,
Col
,
// Col,
Dialog
,
// Dialog,
Popup
,
// Popup,
Overlay
,
// Overlay,
Divider
,
// Divider,
Loading
,
// Loading,
Picker
,
// Picker,
NavBar
,
// NavBar,
Field
,
// Field,
Checkbox
,
// Checkbox,
CardList
,
// CardList,
List
,
// List,
Form
,
// Form,
Sticky
,
// Sticky,
Tab
,
// Tab,
Tabs
,
// Tabs,
Notify
,
// Notify,
Swipe
,
// Swipe,
SwipeItem
,
// SwipeItem,
Toast
,
// Toast,
BackTop
// BackTop
}
from
'
@qg/cherry-ui
'
;
// } from '@qg/cherry-ui';
import
Button
from
'
@qg/cherry-ui/src/button
'
;
import
Image
from
'
@qg/cherry-ui/src/image
'
;
import
Field
from
'
@qg/cherry-ui/src/field
'
;
import
Form
from
'
@qg/cherry-ui/src/form
'
;
import
Tab
from
'
@qg/cherry-ui/src/tab
'
;
import
Tabs
from
'
@qg/cherry-ui/src/tabs
'
;
import
BackTop
from
'
@qg/cherry-ui/src/back-top
'
;
import
Notify
from
'
@qg/cherry-ui/src/notify
'
;
import
Toast
from
'
@qg/cherry-ui/src/toast
'
;
import
{
KaLoginForm
}
from
'
@qg/citrus-ui
'
;
import
{
KaLoginForm
}
from
'
@qg/citrus-ui
'
;
Vue
.
use
(
Button
);
Vue
.
use
(
Button
);
...
...
config/config.default.ts
View file @
2170c951
...
@@ -36,5 +36,10 @@ export default (appInfo: EggAppConfig) => {
...
@@ -36,5 +36,10 @@ export default (appInfo: EggAppConfig) => {
'
access
'
'
access
'
];
];
config
.
cors
=
{
origin
:
'
*
'
,
allowMethods
:
'
GET,HEAD,PUT,POST,DELETE,PATCH
'
};
return
config
;
return
config
;
};
};
typings/app/controller/index.d.ts
View file @
2170c951
...
@@ -2,12 +2,10 @@
...
@@ -2,12 +2,10 @@
// Do not modify this file!!!!!!!!!
// Do not modify this file!!!!!!!!!
import
'
egg
'
;
import
'
egg
'
;
import
ExportActivity
from
'
../../../app/controller/activity
'
;
import
ExportEditor
from
'
../../../app/controller/editor
'
;
import
ExportEditor
from
'
../../../app/controller/editor
'
;
declare
module
'
egg
'
{
declare
module
'
egg
'
{
interface
IController
{
interface
IController
{
activity
:
ExportActivity
;
editor
:
ExportEditor
;
editor
:
ExportEditor
;
}
}
}
}
webpack.config.js
View file @
2170c951
...
@@ -8,7 +8,6 @@ module.exports = {
...
@@ -8,7 +8,6 @@ module.exports = {
entry
:
{
entry
:
{
'
login
'
:
'
app/web/page/login/index.vue
'
,
'
login
'
:
'
app/web/page/login/index.vue
'
,
'
editor
'
:
'
app/web/page/editor/index.ts
'
,
'
editor
'
:
'
app/web/page/editor/index.ts
'
,
'
activity
'
:
'
app/web/page/activity/index.ts
'
,
},
},
resolve
:
{
resolve
:
{
alias
:{
alias
:{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment