Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
J
jimu-core
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
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
fanxuehui
jimu-core
Commits
e3c9c5df
Commit
e3c9c5df
authored
Jun 28, 2020
by
fanxuehui
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: store
parent
e4ce8b08
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
783 additions
and
40 deletions
+783
-40
.eslintrc.js
.eslintrc.js
+15
-2
extendCanvas.tsx
fixtures/extendCanvas.tsx
+7
-9
extendControls.js
fixtures/extendControls.js
+0
-0
index.less
fixtures/index.less
+5
-0
editor.tsx
fixtures/widgets/demo/editor.tsx
+4
-1
meta.ts
fixtures/widgets/demo/meta.ts
+1
-0
package.json
package.json
+5
-0
index.tsx
src/jimu-editor/components/stage/index.tsx
+21
-1
card.tsx
src/jimu-editor/hoc/card.tsx
+5
-3
index.less
src/jimu-editor/hoc/layer/index.less
+160
-0
index.tsx
src/jimu-editor/hoc/layer/index.tsx
+59
-0
resizer.tsx
src/jimu-editor/hoc/layer/resizer.tsx
+308
-0
global.d.ts
src/jimu-editor/shared/global.d.ts
+7
-0
interfaces.ts
src/jimu-editor/shared/interfaces.ts
+11
-1
stage.ts
src/jimu-editor/store/stage.ts
+101
-22
helper.ts
src/jimu-editor/utils/helper.ts
+74
-1
index.ts
src/jimu-renderer/index.ts
+0
-0
No files found.
.eslintrc.js
View file @
e3c9c5df
module
.
exports
=
{
module
.
exports
=
{
parser
:
'@typescript-eslint/parser'
,
parser
:
'@typescript-eslint/parser'
,
extends
:
[
'plugin:@typescript-eslint/recommended'
],
extends
:
[
'plugin:react/recommended'
,
'plugin:@typescript-eslint/recommended'
,
'prettier/@typescript-eslint'
,
'plugin:prettier/recommended'
,
],
plugins
:
[
'@typescript-eslint'
],
plugins
:
[
'@typescript-eslint'
],
env
:
{
env
:
{
browser
:
true
,
browser
:
true
,
node
:
true
,
node
:
true
,
},
},
settings
:
{
react
:
{
pragma
:
'React'
,
version
:
'detect'
,
},
},
parserOptions
:
{
parserOptions
:
{
ecmaVersion
:
2019
,
ecmaVersion
:
2019
,
sourceType
:
'module'
,
sourceType
:
'module'
,
ecmaFeatures
:
{
jsx
:
true
,
},
},
},
rules
:
{
rules
:
{
'@typescript-eslint/interface-name-prefix'
:
[
'never'
],
'@typescript-eslint/interface-name-prefix'
:
[
'never'
],
...
...
fixtures/extendCanvas.tsx
View file @
e3c9c5df
import
React
from
'react'
;
import
React
from
'react'
;
import
{
toJS
}
from
'mobx'
;
import
{
toJS
}
from
'mobx'
;
import
withLayer
from
'@hoc/layer'
;
import
styles
from
'./index.less'
;
/*
/*
* todo
* todo
* 和数据流解耦
* 和数据流解耦
*/
*/
function
Canvas
({
stageStore
})
{
function
Canvas
({
stageStore
})
{
return
(
return
(
<
div
>
<
div
className=
{
styles
.
extend_canvas
}
>
<
p
>
extended Canvas
</
p
>
<
p
>
extended Canvas
</
p
>
{
stageStore
.
data
.
widgetList
.
map
((
wrappedWidget
)
=>
(
{
stageStore
.
targetPage
.
widgetList
.
map
((
wrappedWidget
)
=>
{
<
wrappedWidget
.
widget
.
layer
const
WrappedLayer
=
withLayer
(
wrappedWidget
);
key=
{
wrappedWidget
.
id
}
return
<
WrappedLayer
key=
{
wrappedWidget
.
id
}
></
WrappedLayer
>;
setFocus=
{
()
=>
stageStore
.
setFocus
(
wrappedWidget
.
id
)
}
})
}
style=
{
toJS
(
wrappedWidget
.
style
)
}
></
wrappedWidget
.
widget
.
layer
>
))
}
</
div
>
</
div
>
);
);
}
}
...
...
fixtures/extendControls.js
0 → 100644
View file @
e3c9c5df
fixtures/index.less
0 → 100644
View file @
e3c9c5df
.extend_canvas {
position: relative;
width: 375px;
margin: auto;
}
fixtures/widgets/demo/editor.tsx
View file @
e3c9c5df
...
@@ -10,7 +10,10 @@ const Editor: ComponentType<WidgetEditorProps> = function Editor({
...
@@ -10,7 +10,10 @@ const Editor: ComponentType<WidgetEditorProps> = function Editor({
<
p
>
num:
{
target
.
id
}
</
p
>
<
p
>
num:
{
target
.
id
}
</
p
>
<
button
<
button
onClick=
{
()
=>
{
onClick=
{
()
=>
{
changeTargetProps
({
backgroundColor
:
target
.
style
.
backgroundColor
===
'red'
?
'blue'
:
'red'
});
changeTargetProps
({
'style.backgroundColor'
:
target
.
style
.
backgroundColor
===
'red'
?
'blue'
:
'red'
,
});
}
}
}
}
>
>
change color
change color
...
...
fixtures/widgets/demo/meta.ts
View file @
e3c9c5df
...
@@ -6,4 +6,5 @@ export default {
...
@@ -6,4 +6,5 @@ export default {
data
:
{
data
:
{
bg
:
{
type
:
WidgetDataTypes
.
Text
,
value
:
''
},
bg
:
{
type
:
WidgetDataTypes
.
Text
,
value
:
''
},
},
},
style
:
{
width
:
'100px'
,
height
:
'100px'
},
};
};
package.json
View file @
e3c9c5df
...
@@ -33,6 +33,7 @@
...
@@ -33,6 +33,7 @@
"author"
:
"Dec-F"
,
"author"
:
"Dec-F"
,
"license"
:
"
MIT
"
,
"license"
:
"
MIT
"
,
"devDependencies"
:
{
"devDependencies"
:
{
"
@types/lodash-es
"
:
"
^4.17.3
"
,
"
@types/react
"
:
"
^16.9.35
"
,
"
@types/react
"
:
"
^16.9.35
"
,
"
@types/react-dnd
"
:
"
^3.0.2
"
,
"
@types/react-dnd
"
:
"
^3.0.2
"
,
"
@types/react-dom
"
:
"
^16.9.8
"
,
"
@types/react-dom
"
:
"
^16.9.8
"
,
...
@@ -44,6 +45,8 @@
...
@@ -44,6 +45,8 @@
"
clean-webpack-plugin
"
:
"
^3.0.0
"
,
"
clean-webpack-plugin
"
:
"
^3.0.0
"
,
"
css-loader
"
:
"
^3.5.3
"
,
"
css-loader
"
:
"
^3.5.3
"
,
"
eslint
"
:
"
^5.16.0
"
,
"
eslint
"
:
"
^5.16.0
"
,
"
eslint-config-prettier
"
:
"
^6.11.0
"
,
"
eslint-plugin-prettier
"
:
"
^3.1.4
"
,
"
html-loader
"
:
"
^1.1.0
"
,
"
html-loader
"
:
"
^1.1.0
"
,
"
html-webpack-plugin
"
:
"
^4.3.0
"
,
"
html-webpack-plugin
"
:
"
^4.3.0
"
,
"
less-loader
"
:
"
^6.1.0
"
,
"
less-loader
"
:
"
^6.1.0
"
,
...
@@ -51,6 +54,7 @@
...
@@ -51,6 +54,7 @@
"
postcss-loader
"
:
"
^3.0.0
"
,
"
postcss-loader
"
:
"
^3.0.0
"
,
"
postcss-pxtorem
"
:
"
^5.1.1
"
,
"
postcss-pxtorem
"
:
"
^5.1.1
"
,
"
precss
"
:
"
^4.0.0
"
,
"
precss
"
:
"
^4.0.0
"
,
"
prettier
"
:
"
^2.0.5
"
,
"
standard-version
"
:
"
^7.0.1
"
,
"
standard-version
"
:
"
^7.0.1
"
,
"
style-loader
"
:
"
^1.2.1
"
,
"
style-loader
"
:
"
^1.2.1
"
,
"
ts-loader
"
:
"
^7.0.4
"
,
"
ts-loader
"
:
"
^7.0.4
"
,
...
@@ -63,6 +67,7 @@
...
@@ -63,6 +67,7 @@
},
},
"dependencies"
:
{
"dependencies"
:
{
"
antd
"
:
"
^4.2.4
"
,
"
antd
"
:
"
^4.2.4
"
,
"
lodash-es
"
:
"
^4.17.15
"
,
"
mobx
"
:
"
^5.15.4
"
,
"
mobx
"
:
"
^5.15.4
"
,
"
mobx-react
"
:
"
^6.2.2
"
,
"
mobx-react
"
:
"
^6.2.2
"
,
"
react
"
:
"
^16.13.1
"
,
"
react
"
:
"
^16.13.1
"
,
...
...
src/jimu-editor/components/stage/index.tsx
View file @
e3c9c5df
...
@@ -11,8 +11,28 @@ function Stage() {
...
@@ -11,8 +11,28 @@ function Stage() {
const
{
scopeStore
,
stageStore
}
=
useStore
();
const
{
scopeStore
,
stageStore
}
=
useStore
();
const
{
canvas
}
=
scopeStore
;
const
{
canvas
}
=
scopeStore
;
const
handleClick
=
()
=>
{};
const
handleClick
=
()
=>
{};
function
handleMouseMove
(
e
)
{
e
.
preventDefault
();
typeof
window
.
__widgetMouseMove__
===
'function'
&&
window
.
__widgetMouseMove__
(
e
.
clientX
,
e
.
clientY
);
typeof
window
.
__widgetResizerMouseMove__
===
'function'
&&
window
.
__widgetResizerMouseMove__
(
e
);
}
function
handleMouseUp
(
e
)
{
if
(
typeof
window
.
__widgetMouseUp__
===
'function'
)
{
window
.
__widgetMouseUp__
(
e
.
clientX
,
e
.
clientY
);
}
else
if
(
typeof
window
.
__widgetResizerMouseMUp__
===
'function'
)
{
window
.
__widgetResizerMouseMUp__
(
e
);
}
else
{
}
}
return
(
return
(
<
div
onClick=
{
handleClick
}
className=
{
styles
.
stage
}
>
<
div
onClick=
{
handleClick
}
className=
{
styles
.
stage
}
onMouseMoveCapture=
{
handleMouseMove
}
onMouseUp=
{
handleMouseUp
}
>
<
CardHost
></
CardHost
>
<
CardHost
></
CardHost
>
<
CanvasHost
></
CanvasHost
>
<
CanvasHost
></
CanvasHost
>
<
ExtendedConcrols
></
ExtendedConcrols
>
<
ExtendedConcrols
></
ExtendedConcrols
>
...
...
src/jimu-editor/hoc/card.tsx
View file @
e3c9c5df
...
@@ -7,10 +7,10 @@ import { CanvasWrapperUniqueId } from '@config';
...
@@ -7,10 +7,10 @@ import { CanvasWrapperUniqueId } from '@config';
import
{
IWidget
}
from
'@shared/interfaces'
;
import
{
IWidget
}
from
'@shared/interfaces'
;
function
withCardItem
(
function
withCardItem
(
Widget
:
IWidget
,
Widget
:
IWidget
,
type
,
type
:
string
,
defaultAttrs
=
{
style
:
{
width
:
'100px'
}
}
defaultAttrs
=
{
style
:
{
width
:
'100px'
}
}
)
{
)
{
function
wrappe
d
()
{
function
wrappe
r
()
{
const
{
scopeStore
,
stageStore
}
=
useStore
();
const
{
scopeStore
,
stageStore
}
=
useStore
();
const
[
collectedProps
,
drag
]
=
useDrag
({
const
[
collectedProps
,
drag
]
=
useDrag
({
item
:
{
id
:
''
,
type
},
item
:
{
id
:
''
,
type
},
...
@@ -85,6 +85,7 @@ function withCardItem(
...
@@ -85,6 +85,7 @@ function withCardItem(
id
,
id
,
...
defaultAttrs
,
...
defaultAttrs
,
...
attrs
,
...
attrs
,
...
Widget
.
meta
,
widget
:
Widget
,
widget
:
Widget
,
});
});
}
}
...
@@ -98,6 +99,7 @@ function withCardItem(
...
@@ -98,6 +99,7 @@ function withCardItem(
id
:
uuidGen
(),
id
:
uuidGen
(),
...
defaultAttrs
,
...
defaultAttrs
,
...
attrs
,
...
attrs
,
...
Widget
.
meta
,
widget
:
Widget
,
widget
:
Widget
,
});
});
}
}
}
}
...
@@ -106,7 +108,7 @@ function withCardItem(
...
@@ -106,7 +108,7 @@ function withCardItem(
);
);
}
}
return
wrappe
d
;
return
wrappe
r
;
}
}
export
default
withCardItem
;
export
default
withCardItem
;
src/jimu-editor/hoc/layer/index.less
0 → 100644
View file @
e3c9c5df
@squareWidth: 8px;
@handlerWidth: 16px;
@rotateOffsetY: 50px;
@borderColor: rgb(255, 82, 82);
.widget_layer_wrapper {
box-sizing: border-box;
position: absolute;
user-select: none;
cursor: normal;
// Resizer Styles
.resizerTop,
.resizerBottom,
.resizerLeft,
.resizerRight {
position: absolute;
div {
position: absolute;
border: 1px solid @borderColor;
width: @squareWidth;
height: @squareWidth;
user-select: none;
background-color: #fff;
z-index: 2;
}
[class*='handler'] {
border: none;
width: @handlerWidth;
height: @handlerWidth;
opacity: 0;
z-index: 2;
user-select: none;
}
}
.resizerTop {
border-top: 1px solid @borderColor;
top: 0;
left: 0;
right: 0;
z-index: 3;
.lt {
left: -@squareWidth / 2;
top: -@squareWidth / 2;
}
.rt {
right: -@squareWidth / 2;
top: -@squareWidth / 2;
}
.ct {
left: calc(-@squareWidth / 2 + 50%);
top: -@squareWidth / 2;
}
.lt-handler {
left: -@handlerWidth / 2;
top: -@handlerWidth / 2;
cursor: nw-resize;
}
.rt-handler {
right: -@handlerWidth / 2;
top: -@handlerWidth / 2;
cursor: ne-resize;
}
.ct-handler {
left: calc(-@handlerWidth / 2 + 50%);
top: -@handlerWidth / 2;
cursor: n-resize;
}
.ro {
left: calc(-@squareWidth / 2 + 50%);
top: calc(-@squareWidth / 2 - @rotateOffsetY);
}
.ro-handler {
left: calc(-@handlerWidth / 2 + 50%);
top: calc(-@handlerWidth / 2 - @rotateOffsetY);
cursor: move;
}
}
.resizerBottom {
border-bottom: 1px solid @borderColor;
bottom: 0;
left: 0;
right: 0;
z-index: 3;
.rb {
right: -@squareWidth / 2;
bottom: -@squareWidth / 2;
}
.lb {
left: -@squareWidth / 2;
bottom: -@squareWidth / 2;
}
.cb {
left: calc(-@squareWidth / 2 + 50%);
bottom: -@squareWidth / 2;
}
.rb-handler {
right: -@handlerWidth / 2;
bottom: -@handlerWidth / 2;
cursor: se-resize;
}
.lb-handler {
left: -@handlerWidth / 2;
bottom: -@handlerWidth / 2;
cursor: sw-resize;
}
.cb-handler {
left: calc(-@handlerWidth / 2 + 50%);
bottom: -@handlerWidth / 2;
cursor: s-resize;
}
}
.resizerLeft {
border-left: 1px solid @borderColor;
top: 0;
left: 0;
bottom: 0;
z-index: 2;
.lm {
left: -@squareWidth / 2 - 1px;
top: calc(-@squareWidth / 2 + 50%);
}
.lm-handler {
left: -@handlerWidth / 2;
top: calc(-@handlerWidth / 2 + 50%);
cursor: w-resize;
}
}
.resizerRight {
border-right: 1px solid @borderColor;
top: 0;
bottom: 0;
right: 0;
z-index: 2;
.rm {
right: -@squareWidth / 2 - 1px;
top: calc(-@squareWidth / 2 + 50%);
}
.rm-handler {
right: -@handlerWidth / 2;
top: calc(-@handlerWidth / 2 + 50%);
cursor: e-resize;
}
}
// Indicator Styles
.indicatorV,
.indicatorH {
position: absolute;
z-index: 200;
}
.indicatorH {
top: 50%;
left: 0;
right: 0;
}
.indicatorV {
left: 50%;
top: 0;
bottom: 0;
}
}
src/jimu-editor/hoc/layer/index.tsx
0 → 100644
View file @
e3c9c5df
import
React
,
{
useRef
}
from
'react'
;
import
{
getNumber
}
from
'@utils/helper'
;
import
{
useStore
}
from
'@hooks/use-store'
;
import
{
CanvasWrapperUniqueId
}
from
'@config'
;
import
{
IWidget
}
from
'@shared/interfaces'
;
import
{
toJS
}
from
'mobx'
;
import
Resizer
from
'./resizer'
;
import
{
observer
}
from
'mobx-react'
;
import
styles
from
'./index.less'
;
function
withLayer
(
wrappedWidget
)
{
function
Wrapper
()
{
const
{
scopeStore
,
stageStore
}
=
useStore
();
const
wrapperRef
=
useRef
<
HTMLDivElement
>
();
const
handleReszie
=
({
width
,
height
,
left
,
top
,
deg
})
=>
{
const
widgetDom
=
document
.
querySelector
<
HTMLDivElement
>
(
`[wid="
${
wrappedWidget
.
id
}
"`
);
wrapperRef
.
current
.
style
.
left
=
left
+
'px'
;
wrapperRef
.
current
.
style
.
top
=
top
+
'px'
;
wrapperRef
.
current
.
style
.
width
=
width
+
'px'
;
wrapperRef
.
current
.
style
.
height
=
height
+
'px'
;
wrapperRef
.
current
.
style
.
transform
=
`rotate(
${
deg
}
deg)`
;
};
const
hanldeReszieComplete
=
({
width
,
height
,
left
,
top
,
deg
})
=>
{
const
dpr
=
1
;
stageStore
.
changeTargetProps
({
'style.width'
:
width
*
dpr
+
'px'
,
'style.height'
:
height
*
dpr
+
'px'
,
'style.left'
:
left
*
dpr
+
'px'
,
'style.top'
:
top
*
dpr
+
'px'
,
'style.transform'
:
`rotate(
${
deg
}
deg)`
,
});
};
return
(
<
div
className=
{
styles
.
widget_layer_wrapper
}
id=
{
wrappedWidget
.
id
}
ref=
{
wrapperRef
}
>
{
stageStore
.
targetWidgetId
===
wrappedWidget
.
id
?
(
<
Resizer
onReszie=
{
handleReszie
}
onReszieComplete=
{
hanldeReszieComplete
}
targetStyle=
{
wrappedWidget
.
style
}
></
Resizer
>
)
:
(
''
)
}
<
wrappedWidget
.
widget
.
layer
setFocus=
{
()
=>
stageStore
.
chooseWidget
(
wrappedWidget
.
id
)
}
{
...
toJS
(
wrappedWidget
)}
></
wrappedWidget
.
widget
.
layer
>
</
div
>
);
}
return
observer
(
Wrapper
);
}
export
default
withLayer
;
src/jimu-editor/hoc/layer/resizer.tsx
0 → 100644
View file @
e3c9c5df
import
React
,
{
useCallback
,
useState
,
useEffect
,
CSSProperties
}
from
'react'
;
import
{
useSetState
}
from
'react-use'
;
import
{
getNumber
,
getDeg
}
from
'@utils/helper'
;
import
{
CanvasWrapperUniqueId
}
from
'@config'
;
import
styles
from
'./index.less'
;
type
IState
=
{
mouseInitPos
:
{
x
:
number
;
y
:
number
;
};
type
:
string
;
initTarget
:
CSSProperties
;
};
function
Resizer
(
props
)
{
const
[
state
,
setState
]
=
useSetState
<
IState
>
({
mouseInitPos
:
{
x
:
NaN
,
y
:
NaN
,
},
type
:
''
,
initTarget
:
{},
});
const
calc
=
useCallback
(
(
type
,
e
)
=>
{
if
(
type
===
'lt'
)
{
const
offsetX
=
e
.
clientX
-
state
.
mouseInitPos
.
x
;
const
offsetY
=
e
.
clientY
-
state
.
mouseInitPos
.
y
;
const
width
=
getNumber
(
state
.
initTarget
.
width
)
+
-
offsetX
;
const
height
=
getNumber
(
state
.
initTarget
.
height
)
+
-
offsetY
;
const
left
=
getNumber
(
state
.
initTarget
.
left
)
+
offsetX
;
const
top
=
getNumber
(
state
.
initTarget
.
top
)
+
offsetY
;
const
deg
=
getNumber
(
state
.
initTarget
.
transform
);
return
{
width
,
height
,
left
,
top
,
deg
,
};
}
else
if
(
type
===
'ro'
)
{
const
{
left
,
top
,
width
,
height
}
=
state
.
initTarget
;
const
sceneInnerRect
=
document
.
getElementById
(
CanvasWrapperUniqueId
)
.
getBoundingClientRect
();
const
deg
=
getDeg
(
sceneInnerRect
.
left
+
getNumber
(
left
)
+
getNumber
(
width
)
/
2
,
sceneInnerRect
.
top
+
getNumber
(
top
)
+
getNumber
(
height
)
/
2
,
e
.
clientX
,
e
.
clientY
);
return
{
left
:
getNumber
(
left
),
top
:
getNumber
(
top
),
width
:
getNumber
(
width
),
height
:
getNumber
(
height
),
deg
,
};
}
else
if
(
type
===
'ct'
)
{
const
{
left
,
top
,
width
,
height
,
transform
}
=
state
.
initTarget
;
const
offsetY
=
e
.
clientY
-
state
.
mouseInitPos
.
y
;
return
{
width
:
getNumber
(
width
),
height
:
getNumber
(
height
)
+
-
offsetY
,
left
:
getNumber
(
left
),
top
:
getNumber
(
top
)
+
offsetY
,
deg
:
getNumber
(
transform
),
};
}
else
if
(
type
===
'cb'
)
{
const
{
left
,
top
,
width
,
height
,
transform
}
=
state
.
initTarget
;
const
offsetY
=
e
.
clientY
-
state
.
mouseInitPos
.
y
;
return
{
width
:
getNumber
(
width
),
height
:
getNumber
(
height
)
+
offsetY
,
left
:
getNumber
(
left
),
top
:
getNumber
(
top
),
deg
:
getNumber
(
transform
),
};
}
else
if
(
type
===
'lm'
)
{
const
{
left
,
top
,
width
,
height
,
transform
}
=
state
.
initTarget
;
const
offsetX
=
e
.
clientX
-
state
.
mouseInitPos
.
x
;
return
{
width
:
getNumber
(
width
)
+
-
offsetX
,
height
:
getNumber
(
height
),
left
:
getNumber
(
left
)
+
offsetX
,
top
:
getNumber
(
top
),
deg
:
getNumber
(
transform
),
};
}
else
if
(
type
===
'rm'
)
{
const
{
left
,
top
,
width
,
height
,
transform
}
=
state
.
initTarget
;
const
offsetX
=
e
.
clientX
-
state
.
mouseInitPos
.
x
;
return
{
width
:
getNumber
(
width
)
+
offsetX
,
height
:
getNumber
(
height
),
left
:
getNumber
(
left
),
top
:
getNumber
(
top
),
deg
:
getNumber
(
transform
),
};
}
else
if
(
type
===
'rt'
)
{
const
offsetX
=
e
.
clientX
-
state
.
mouseInitPos
.
x
;
const
offsetY
=
e
.
clientY
-
state
.
mouseInitPos
.
y
;
const
width
=
getNumber
(
state
.
initTarget
.
width
)
+
offsetX
;
const
height
=
getNumber
(
state
.
initTarget
.
height
)
+
-
offsetY
;
const
left
=
getNumber
(
state
.
initTarget
.
left
);
const
top
=
getNumber
(
state
.
initTarget
.
top
)
+
offsetY
;
const
deg
=
getNumber
(
state
.
initTarget
.
transform
);
return
{
width
,
height
,
left
,
top
,
deg
,
};
}
else
if
(
type
===
'rb'
)
{
const
offsetX
=
e
.
clientX
-
state
.
mouseInitPos
.
x
;
const
offsetY
=
e
.
clientY
-
state
.
mouseInitPos
.
y
;
const
width
=
getNumber
(
state
.
initTarget
.
width
)
+
offsetX
;
const
height
=
getNumber
(
state
.
initTarget
.
height
)
+
offsetY
;
const
left
=
getNumber
(
state
.
initTarget
.
left
);
const
top
=
getNumber
(
state
.
initTarget
.
top
);
const
deg
=
getNumber
(
state
.
initTarget
.
transform
);
return
{
width
,
height
,
left
,
top
,
deg
,
};
}
else
if
(
type
===
'lb'
)
{
const
offsetX
=
e
.
clientX
-
state
.
mouseInitPos
.
x
;
const
offsetY
=
e
.
clientY
-
state
.
mouseInitPos
.
y
;
const
width
=
getNumber
(
state
.
initTarget
.
width
)
+
-
offsetX
;
const
height
=
getNumber
(
state
.
initTarget
.
height
)
+
offsetY
;
const
left
=
getNumber
(
state
.
initTarget
.
left
)
+
offsetX
;
const
top
=
getNumber
(
state
.
initTarget
.
top
);
const
deg
=
getNumber
(
state
.
initTarget
.
transform
);
return
{
width
,
height
,
left
,
top
,
deg
,
};
}
},
[
state
]
);
const
handleMouseDown
=
(
type
,
e
)
=>
{
e
.
persist
();
e
.
stopPropagation
();
setState
({
mouseInitPos
:
{
x
:
e
.
clientX
,
y
:
e
.
clientY
,
},
type
,
initTarget
:
{
...
props
.
targetStyle
},
});
};
const
__handleMouseMove__
=
useCallback
(
(
e
)
=>
{
e
.
persist
();
e
.
stopPropagation
();
const
result
=
calc
(
state
.
type
,
e
);
console
.
log
(
result
,
state
.
type
,
e
);
props
.
onReszie
(
result
);
},
[
state
]
);
const
__handleMouseUp__
=
useCallback
(
(
e
)
=>
{
e
.
stopPropagation
();
const
result
=
calc
(
state
.
type
,
e
);
props
.
onReszieComplete
(
result
);
setState
({
initTarget
:
result
,
type
:
''
,
});
},
[
state
]
);
useEffect
(()
=>
{
if
(
state
.
type
)
{
window
.
__widgetResizerMouseMove__
=
__handleMouseMove__
;
window
.
__widgetResizerMouseMUp__
=
__handleMouseUp__
;
}
else
{
window
.
__widgetResizerMouseMove__
=
null
;
window
.
__widgetResizerMouseMUp__
=
null
;
}
},
[
__handleMouseMove__
,
__handleMouseUp__
,
state
]);
let
{
options
}
=
props
;
options
||
(
options
=
{});
const
{
lt
,
rt
,
ct
,
ro
,
rm
,
rb
,
lb
,
cb
,
lm
}
=
options
;
return
(
<
React
.
Fragment
>
<
div
className=
{
styles
[
'resizerTop'
]
}
>
<
div
className=
{
styles
[
'lt'
]
}
{
...
generateProps
(
lt
)}
/>
<
div
className=
{
styles
[
'rt'
]
}
{
...
generateProps
(
rt
)}
/>
<
div
className=
{
styles
[
'ct'
]
}
{
...
generateProps
(
ct
)}
/>
<
div
className=
{
styles
[
'ro'
]
}
{
...
generateProps
(
ro
)}
/>
<
div
className=
{
styles
[
'lt-handler'
]
}
{
...
generateProps
(
lt
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
lt
)
&&
handleMouseDown
(
'lt'
,
e
)
}
/>
<
div
className=
{
styles
[
'rt-handler'
]
}
{
...
generateProps
(
rt
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
rt
)
&&
handleMouseDown
(
'rt'
,
e
)
}
/>
<
div
className=
{
styles
[
'ct-handler'
]
}
{
...
generateProps
(
ct
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
ct
)
&&
handleMouseDown
(
'ct'
,
e
)
}
/>
<
div
className=
{
styles
[
'ro-handler'
]
}
{
...
generateProps
(
ro
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
ro
)
&&
handleMouseDown
(
'ro'
,
e
)
}
/>
</
div
>
<
div
className=
{
styles
[
'resizerRight'
]
}
>
<
div
className=
{
styles
[
'rm'
]
}
{
...
generateProps
(
rm
)}
/>
<
div
className=
{
styles
[
'rm-handler'
]
}
{
...
generateProps
(
rm
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
rm
)
&&
handleMouseDown
(
'rm'
,
e
)
}
/>
</
div
>
<
div
className=
{
styles
[
'resizerBottom'
]
}
>
<
div
className=
{
styles
[
'rb'
]
}
{
...
generateProps
(
rb
)}
/>
<
div
className=
{
styles
[
'lb'
]
}
{
...
generateProps
(
lb
)}
/>
<
div
className=
{
styles
[
'cb'
]
}
{
...
generateProps
(
cb
)}
/>
<
div
className=
{
styles
[
'rb-handler'
]
}
{
...
generateProps
(
rb
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
rb
)
&&
handleMouseDown
(
'rb'
,
e
)
}
/>
<
div
className=
{
styles
[
'lb-handler'
]
}
{
...
generateProps
(
lb
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
lb
)
&&
handleMouseDown
(
'lb'
,
e
)
}
/>
<
div
className=
{
styles
[
'cb-handler'
]
}
{
...
generateProps
(
cb
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
cb
)
&&
handleMouseDown
(
'cb'
,
e
)
}
/>
</
div
>
<
div
className=
{
styles
[
'resizerLeft'
]
}
>
<
div
className=
{
styles
[
'lm'
]
}
{
...
generateProps
(
lm
)}
/>
<
div
className=
{
styles
[
'lm-handler'
]
}
{
...
generateProps
(
lm
)}
onMouseDown=
{
(
e
)
=>
controlBehavior
(
e
,
lm
)
&&
handleMouseDown
(
'lm'
,
e
)
}
/>
</
div
>
</
React
.
Fragment
>
);
}
export
default
Resizer
;
// visible disabled hidden
function
generateProps
(
type
=
'visible'
)
{
if
(
type
===
'hidden'
)
{
return
{
style
:
{
display
:
'none'
,
},
};
}
else
if
(
type
===
'disabled'
)
{
return
{
style
:
{
borderColor
:
'#999'
,
cursor
:
'not-allowed'
,
},
};
}
else
{
return
{};
}
}
// visible disabled hidden
function
controlBehavior
(
e
,
type
=
'visible'
)
{
if
(
type
===
'disabled'
||
type
===
'hidden'
)
{
return
e
.
stopPropagation
();
}
else
{
return
true
;
}
}
src/jimu-editor/shared/global.d.ts
View file @
e3c9c5df
...
@@ -4,3 +4,10 @@ declare module '*.less' {
...
@@ -4,3 +4,10 @@ declare module '*.less' {
const
classes
:
{
[
key
:
string
]:
string
};
const
classes
:
{
[
key
:
string
]:
string
};
export
default
classes
;
export
default
classes
;
}
}
interface
Window
{
__widgetResizerMouseMUp__
:
function
;
__widgetResizerMouseMove__
:
function
;
__widgetMouseUp__
:
function
;
__widgetMouseMove__
:
function
;
}
src/jimu-editor/shared/interfaces.ts
View file @
e3c9c5df
...
@@ -36,6 +36,7 @@ export interface IWidget {
...
@@ -36,6 +36,7 @@ export interface IWidget {
}
}
export
interface
IWidgetAttrs
{
export
interface
IWidgetAttrs
{
id
:
string
;
id
:
string
;
name
?:
string
;
style
:
CSSProperties
;
style
:
CSSProperties
;
actionAttrs
?:
{
actionAttrs
?:
{
actionHide
:
IAction
;
actionHide
:
IAction
;
...
@@ -75,9 +76,12 @@ export interface IEvent {
...
@@ -75,9 +76,12 @@ export interface IEvent {
des
:
string
;
des
:
string
;
pub
?:
string
;
pub
?:
string
;
}
}
export
interface
ChangedObj
{
[
key
:
string
]:
any
;
}
export
interface
WidgetEditorProps
{
export
interface
WidgetEditorProps
{
target
:
IWrappedWidget
;
target
:
IWrappedWidget
;
changeTargetProps
:
(
style
:
CSSProperties
)
=>
void
;
changeTargetProps
:
(
obj
:
ChangedObj
)
=>
void
;
}
}
interface
WidgetIconProps
{
interface
WidgetIconProps
{
addSelf
:
(
attrs
:
IWidgetAttrs
)
=>
void
;
addSelf
:
(
attrs
:
IWidgetAttrs
)
=>
void
;
...
@@ -88,6 +92,8 @@ interface WidgetLayerProps {
...
@@ -88,6 +92,8 @@ interface WidgetLayerProps {
// 产出JSON格式
// 产出JSON格式
export
interface
IPage
{
export
interface
IPage
{
id
:
string
;
name
:
string
;
attr
:
IPageAttr
;
attr
:
IPageAttr
;
widgetList
:
IWrappedWidget
[];
widgetList
:
IWrappedWidget
[];
}
}
...
@@ -95,4 +101,8 @@ export interface IPageAttr {
...
@@ -95,4 +101,8 @@ export interface IPageAttr {
style
:
CSSProperties
;
style
:
CSSProperties
;
name
:
string
;
name
:
string
;
id
:
string
;
id
:
string
;
layout
:
IPageLayoutTypes
;
}
export
enum
IPageLayoutTypes
{
Absolute
=
'ABSOLUTE'
,
}
}
src/jimu-editor/store/stage.ts
View file @
e3c9c5df
// 舞台数据
// 舞台数据
import
{
observable
,
action
,
computed
}
from
'mobx'
;
import
{
observable
,
action
,
computed
}
from
'mobx'
;
import
{
IPage
,
IWidget
,
IWrappedWidget
}
from
'../shared/interfaces'
;
import
{
IPage
,
IWidget
,
IWrappedWidget
,
IPageLayoutTypes
,
ChangedObj
,
}
from
'../shared/interfaces'
;
import
{
CSSProperties
}
from
'react'
;
import
{
CSSProperties
}
from
'react'
;
import
{
set
,
cloneDeep
}
from
'lodash-es'
;
import
{
pageGen
,
getNumber
,
mutateValueByPath
}
from
'@utils/helper'
;
import
{
uuidGen
}
from
'@utils/uuid'
;
export
class
StageStore
{
export
class
StageStore
{
@
observable
@
observable
data
:
IPage
=
{
data
:
IPage
[]
=
[
pageGen
()];
attr
:
{
style
:
{
width
:
100
},
id
:
''
,
name
:
''
,
},
widgetList
:
[],
};
// 当前编辑的页面
@
observable
targetPageId
:
string
=
''
;
// 当前编辑的组件
// 当前编辑的组件
@
observable
@
observable
targetWidgetId
:
string
=
''
;
targetWidgetId
:
string
=
''
;
@
computed
@
computed
get
targetPage
()
{
return
(
this
.
data
.
find
((
page
:
IPage
)
=>
page
.
id
===
this
.
targetPageId
)
||
this
.
data
[
0
]
);
}
@
computed
get
targetWidget
()
{
get
targetWidget
()
{
return
this
.
data
.
widgetList
.
find
(
return
this
.
targetPage
.
widgetList
.
find
(
(
widget
)
=>
widget
.
id
===
this
.
targetWidgetId
(
widget
:
IWrappedWidget
)
=>
widget
.
id
===
this
.
targetWidgetId
);
);
}
}
// 添加页面
addPage
()
{
let
newPage
=
pageGen
();
this
.
data
.
push
(
newPage
);
this
.
choosePage
(
newPage
.
id
);
}
// 修改页面属性
@
action
changePageProps
=
(
obj
:
ChangedObj
)
=>
{
mutateValueByPath
(
this
.
targetPage
.
attr
,
obj
);
};
// 修改当前组件属性
// 修改当前组件属性
@
action
@
action
changeTargetProps
=
(
style
:
CSSProperties
)
=>
{
changeTargetProps
=
(
obj
:
ChangedObj
)
=>
{
this
.
data
.
widgetList
.
find
(
mutateValueByPath
(
this
.
targetWidget
,
obj
);
(
widget
)
=>
widget
.
id
===
this
.
targetWidgetId
).
style
=
style
;
console
.
log
(
this
.
data
.
widgetList
.
find
((
widget
)
=>
widget
.
id
===
this
.
targetWidgetId
)
);
};
};
// 根据id修改组件属性
changePagePropsById
(
pageId
:
string
,
obj
:
ChangedObj
)
{}
// 根据id修改组件属性
changeWidgetPropsById
(
widgetId
:
string
,
obj
:
ChangedObj
,
pageId
?:
string
)
{
let
targetPage
=
pageId
?
this
.
data
.
find
((
page
)
=>
page
.
id
==
pageId
)
:
this
.
targetPage
;
let
targetWidget
=
targetPage
.
widgetList
.
find
(
(
widget
)
=>
widget
.
id
===
widgetId
);
mutateValueByPath
(
targetWidget
,
obj
);
}
// 复制组件
@
action
copyWidget
(
targetId
:
string
):
void
{
let
newWidget
=
cloneDeep
(
this
.
targetWidget
);
// 重新更改id
newWidget
.
id
=
uuidGen
();
// 重命名
newWidget
.
name
+=
'-copy'
;
// 重新设置位置
newWidget
.
style
.
top
=
getNumber
(
newWidget
.
style
.
top
)
+
20
+
'px'
;
newWidget
.
style
.
left
=
getNumber
(
newWidget
.
style
.
left
)
+
20
+
'px'
;
this
.
addWidget
(
newWidget
);
}
// 添加组件
// 添加组件
@
action
@
action
addWidget
(
wrappedWidget
:
IWrappedWidget
):
void
{
addWidget
(
wrappedWidget
:
IWrappedWidget
):
void
{
this
.
data
.
widgetList
.
push
(
wrappedWidget
);
this
.
targetPage
.
widgetList
.
push
(
wrappedWidget
);
}
// 删除当前选中组件
@
action
removeCurrWidget
()
{
const
index
=
this
.
targetPage
.
widgetList
.
findIndex
(
(
widget
)
=>
widget
.
id
===
this
.
targetWidgetId
);
if
(
index
!==
-
1
)
{
this
.
targetWidgetId
=
''
;
this
.
targetPage
.
widgetList
.
splice
(
index
,
1
);
}
}
}
// 设置焦点
// 根据targetId删除组件
@
action
removeItemById
=
(
widgetId
:
string
)
=>
{
const
index
=
this
.
targetPage
.
widgetList
.
findIndex
(
(
widget
)
=>
widget
.
id
===
widgetId
);
if
(
index
!==
-
1
)
{
this
.
targetWidgetId
=
''
;
this
.
targetPage
.
widgetList
.
splice
(
index
,
1
);
}
};
// 选中页面
@
action
choosePage
=
(
id
:
string
)
=>
{
this
.
targetPageId
=
id
;
};
// 选中组件
@
action
@
action
setFocus
(
id
:
string
):
void
{
chooseWidget
(
id
:
string
):
void
{
this
.
targetWidgetId
=
id
;
this
.
targetWidgetId
=
id
;
}
}
@
action
reset
()
{
this
.
data
=
[
pageGen
()];
this
.
targetWidgetId
=
''
;
}
}
}
src/jimu-editor/utils/helper.ts
View file @
e3c9c5df
import
{
IPage
,
IWidget
,
IWrappedWidget
,
IPageLayoutTypes
,
ChangedObj
,
}
from
'../shared/interfaces'
;
import
{
uuidGen
}
from
'./uuid'
;
import
{
set
}
from
'lodash-es'
;
// 获取字符串中连续的数字串
// 获取字符串中连续的数字串
export
const
getNumber
=
function
getNumber
(
str
)
{
export
const
getNumber
=
function
getNumber
(
str
:
string
|
number
):
number
{
if
(
typeof
str
===
'string'
)
{
if
(
typeof
str
===
'string'
)
{
const
arr
=
str
.
match
(
/
[
-|0-9
][
0-9
]
*/
);
const
arr
=
str
.
match
(
/
[
-|0-9
][
0-9
]
*/
);
return
arr
&&
arr
[
0
]
?
parseInt
(
arr
[
0
],
10
)
:
0
;
return
arr
&&
arr
[
0
]
?
parseInt
(
arr
[
0
],
10
)
:
0
;
...
@@ -7,3 +16,67 @@ export const getNumber = function getNumber(str) {
...
@@ -7,3 +16,67 @@ export const getNumber = function getNumber(str) {
return
str
;
return
str
;
}
}
};
};
export
const
getDeg
=
function
getDeg
(
centerX
:
number
,
centerY
:
number
,
mouseX
:
number
,
mouseY
:
number
,
offsetDeg
=
0
)
{
var
x
=
Math
.
abs
(
centerX
-
mouseX
);
var
y
=
Math
.
abs
(
centerY
-
mouseY
);
var
z
=
Math
.
sqrt
(
Math
.
pow
(
x
,
2
)
+
Math
.
pow
(
y
,
2
));
var
cos
=
y
/
z
;
var
radina
=
Math
.
acos
(
cos
);
// 用反三角函数求弧度
var
deg
=
Math
.
floor
(
180
/
(
Math
.
PI
/
radina
));
// 将弧度转换成角度
if
(
mouseX
>
centerX
&&
mouseY
>
centerY
)
{
// 第四象限
deg
=
180
-
deg
;
}
if
(
mouseX
===
centerX
&&
mouseY
>
centerY
)
{
// y轴负方向
deg
=
180
;
}
if
(
mouseX
>
centerX
&&
mouseY
===
centerY
)
{
// x轴正方向
deg
=
90
;
}
if
(
mouseX
<
centerX
&&
mouseY
>
centerY
)
{
// 第三象限
deg
=
180
+
deg
;
}
if
(
mouseX
<
centerX
&&
mouseY
===
centerY
)
{
// x轴负方向
deg
=
270
;
}
if
(
mouseX
<
centerX
&&
mouseY
<
centerY
)
{
// 第二象限
deg
=
360
-
deg
;
}
return
deg
+
offsetDeg
;
};
export
const
mutateValueByPath
=
(
target
:
any
,
obj
:
ChangedObj
)
=>
{
Object
.
keys
(
obj
).
forEach
((
key
)
=>
{
set
(
target
,
key
,
obj
[
key
]);
});
};
// 初始化page数据
export
const
pageGen
=
(
name
?:
string
):
IPage
=>
{
return
{
id
:
uuidGen
(),
name
:
name
,
attr
:
{
style
:
{
width
:
'100px'
},
id
:
''
,
name
:
''
,
layout
:
IPageLayoutTypes
.
Absolute
,
},
widgetList
:
[],
};
};
src/jimu-renderer/index.ts
0 → 100644
View file @
e3c9c5df
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