feffery_utils_components 0.2.0-rc8 → 0.2.0-rc9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5262,6 +5262,31 @@
5262
5262
  "required": false,
5263
5263
  "description": "\u8bbe\u7f6e\u5f53\u524d\u7ec4\u4ef6\u5c3a\u5bf8\u8c03\u6574\u53ca\u62d6\u62fd\u7684\u5916\u8fb9\u754c\u7c7b\u578b\uff0c\u53ef\u9009\u7684\u6709'window'\u3001'parent'\r\n\u9ed8\u8ba4\u4e3a'window'"
5264
5264
  },
5265
+ "selected": {
5266
+ "type": {
5267
+ "name": "bool"
5268
+ },
5269
+ "required": false,
5270
+ "description": "\u8bbe\u7f6e\u6216\u76d1\u542c\u5f53\u524d\u7ec4\u4ef6\u662f\u5426\u5904\u4e8e\u9009\u62e9\u72b6\u6001\r\n\u9ed8\u8ba4\uff1afalse",
5271
+ "defaultValue": {
5272
+ "value": "false",
5273
+ "computed": false
5274
+ }
5275
+ },
5276
+ "selectedStyle": {
5277
+ "type": {
5278
+ "name": "object"
5279
+ },
5280
+ "required": false,
5281
+ "description": "\u8bbe\u7f6e\u5f53\u524d\u7ec4\u4ef6\u5728\u9009\u4e2d\u72b6\u6001\u4e0b\u7684css\u6837\u5f0f"
5282
+ },
5283
+ "selectedClassName": {
5284
+ "type": {
5285
+ "name": "string"
5286
+ },
5287
+ "required": false,
5288
+ "description": "\u914d\u7f6e\u5f53\u524d\u7ec4\u4ef6\u5728\u9009\u4e2d\u72b6\u6001\u4e0b\u7684css\u7c7b\u540d"
5289
+ },
5265
5290
  "setProps": {
5266
5291
  "type": {
5267
5292
  "name": "func"
@@ -7123,7 +7148,7 @@
7123
7148
  }
7124
7149
  },
7125
7150
  "src/lib/components/FefferyExecuteJs.react.js": {
7126
- "description": "",
7151
+ "description": "js\u76f4\u63a5\u6267\u884c\u7ec4\u4ef6FefferyExecuteJs",
7127
7152
  "displayName": "FefferyExecuteJs",
7128
7153
  "methods": [],
7129
7154
  "props": {
@@ -7141,6 +7166,63 @@
7141
7166
  "required": false,
7142
7167
  "description": "\u8bbe\u7f6e\u8981\u6267\u884c\u7684js\u4ee3\u7801\u5b57\u7b26\u4e32"
7143
7168
  },
7169
+ "mode": {
7170
+ "type": {
7171
+ "name": "enum",
7172
+ "value": [
7173
+ {
7174
+ "value": "'default'",
7175
+ "computed": false
7176
+ },
7177
+ {
7178
+ "value": "'delay'",
7179
+ "computed": false
7180
+ },
7181
+ {
7182
+ "value": "'interval'",
7183
+ "computed": false
7184
+ },
7185
+ {
7186
+ "value": "'wait-until-element-rendered'",
7187
+ "computed": false
7188
+ }
7189
+ ]
7190
+ },
7191
+ "required": false,
7192
+ "description": "\u8bbe\u7f6e\u6267\u884c\u6a21\u5f0f\uff0c\u53ef\u9009\u7684\u6709'default'\uff08\u7acb\u5373\u6267\u884c\uff09\u3001'delay'\uff08\u5ef6\u8fdf\u6267\u884c\uff09\u3001'interval'\uff08\u5b9a\u65f6\u8f6e\u8be2\u6267\u884c\uff09\u3001'wait-until-element-rendered'\uff08\u7b49\u5f85\u76ee\u6807\u5143\u7d20\u6e32\u67d3\u540e\u6267\u884c\uff09\r\n\u9ed8\u8ba4\uff1a'default'",
7193
+ "defaultValue": {
7194
+ "value": "'default'",
7195
+ "computed": false
7196
+ }
7197
+ },
7198
+ "delay": {
7199
+ "type": {
7200
+ "name": "number"
7201
+ },
7202
+ "required": false,
7203
+ "description": "delay\u6a21\u5f0f\u4e0b\uff0c\u8bbe\u7f6e\u5ef6\u65f6\u6267\u884c\u65f6\u957f\uff0c\u5355\u4f4d\uff1a\u6beb\u79d2"
7204
+ },
7205
+ "interval": {
7206
+ "type": {
7207
+ "name": "number"
7208
+ },
7209
+ "required": false,
7210
+ "description": "interval\u6a21\u5f0f\u4e0b\uff0c\u8bbe\u7f6e\u8f6e\u8be2\u6267\u884c\u95f4\u9694\u65f6\u957f\uff0c\u5355\u4f4d\uff1a\u6beb\u79d2"
7211
+ },
7212
+ "targetSelector": {
7213
+ "type": {
7214
+ "name": "string"
7215
+ },
7216
+ "required": false,
7217
+ "description": "wait-until-element-rendered\u6a21\u5f0f\u4e0b\uff0c\u8bbe\u7f6e\u9700\u8981\u7b49\u5f85\u6e32\u67d3\u5b8c\u6210\u7684\u76ee\u6807\u5143\u7d20css\u9009\u62e9\u5668"
7218
+ },
7219
+ "targetWaitTimeout": {
7220
+ "type": {
7221
+ "name": "number"
7222
+ },
7223
+ "required": false,
7224
+ "description": "wait-until-element-rendered\u6a21\u5f0f\u4e0b\uff0c\u8bbe\u7f6e\u76ee\u6807\u5143\u7d20\u6e32\u67d3\u68c0\u6d4b\u6700\u5927\u7b49\u5f85\u65f6\u957f\uff0c\u5355\u4f4d\uff1a\u6beb\u79d2\uff0c\u9ed8\u8ba4\u65e0\u9650\u5236"
7225
+ },
7144
7226
  "loading_state": {
7145
7227
  "type": {
7146
7228
  "name": "shape",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feffery_utils_components",
3
- "version": "0.2.0-rc8",
3
+ "version": "0.2.0-rc9",
4
4
  "description": "Build more utility components for Plotly Dash.",
5
5
  "repository": {
6
6
  "type": "git",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feffery_utils_components",
3
- "version": "0.2.0-rc8",
3
+ "version": "0.2.0-rc9",
4
4
  "description": "Build more utility components for Plotly Dash.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,25 +1,99 @@
1
1
  import { useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
- // 定义js直接执行部件FefferyExecuteJs
4
+ /**
5
+ * js直接执行组件FefferyExecuteJs
6
+ */
5
7
  const FefferyExecuteJs = (props) => {
6
8
  // 取得必要属性或参数
7
9
  const {
8
- id,
9
10
  jsString,
11
+ mode,
12
+ delay,
13
+ interval,
14
+ targetSelector,
15
+ targetWaitTimeout,
10
16
  setProps,
11
17
  loading_state
12
18
  } = props;
13
19
 
14
20
  useEffect(() => {
15
- // 执行原生js程序
16
- if (jsString) {
17
- try { eval(jsString) }
18
- catch (exception) { console.log(exception) }
19
- // 清空jsString
20
- setProps({
21
- jsString: ''
22
- })
21
+ // delay模式
22
+ if (mode === 'delay') {
23
+ // 延时执行原生js程序
24
+ if (jsString) {
25
+ setTimeout(
26
+ () => {
27
+ try { eval(jsString) }
28
+ catch (exception) { console.error(exception) }
29
+ // 重置jsString
30
+ setProps({
31
+ jsString: null
32
+ })
33
+ },
34
+ delay
35
+ )
36
+ }
37
+ } else if (mode === 'interval') {
38
+ // 定时执行原生js程序
39
+ if (jsString) {
40
+ // 创建轮询器
41
+ const _interval = setInterval(
42
+ () => {
43
+ try { eval(jsString) }
44
+ catch (exception) { console.error(exception) }
45
+ },
46
+ interval
47
+ )
48
+ // jsString更新或组件卸载时,销毁轮询器
49
+ return () => clearInterval(_interval);
50
+ }
51
+ } else if (mode === 'wait-until-element-rendered') {
52
+ // 等待目标元素渲染后执行
53
+ if (jsString) {
54
+ // 记录逻辑开始时间戳
55
+ const startTime = Date.now()
56
+
57
+ // 创建定时器
58
+ let _interval = setInterval(
59
+ () => {
60
+ // 若目标元素出现
61
+ if (document.querySelector(targetSelector)) {
62
+ clearInterval(_interval)
63
+ _interval = false;
64
+ try { eval(jsString) }
65
+ catch (exception) { console.error(exception) }
66
+ // 重置jsString
67
+ setProps({
68
+ jsString: null
69
+ })
70
+ }
71
+ // 若距离startTime时长超出targetWaitTimeout限制,则强制终止轮询检查过程
72
+ if (targetWaitTimeout && Date.now() - startTime > targetWaitTimeout) {
73
+ clearInterval(_interval)
74
+ _interval = false;
75
+ // 重置jsString
76
+ setProps({
77
+ jsString: null
78
+ })
79
+ }
80
+ },
81
+ 200 // 默认200毫秒检测一次目标元素出现情况
82
+ )
83
+
84
+ // 若jsString更新或组件卸载时,轮询器仍然未被清除,则进行清除
85
+ return () => !_interval && clearInterval(_interval)
86
+ }
87
+ } else {
88
+ // 执行原生js程序
89
+ if (jsString) {
90
+ try { eval(jsString) }
91
+ catch (exception) { console.error(exception) }
92
+ // 重置jsString
93
+ setProps({
94
+ jsString: null
95
+ })
96
+ }
23
97
  }
24
98
  }, [jsString])
25
99
 
@@ -39,6 +113,32 @@ FefferyExecuteJs.propTypes = {
39
113
  */
40
114
  jsString: PropTypes.string,
41
115
 
116
+ /**
117
+ * 设置执行模式,可选的有'default'(立即执行)、'delay'(延迟执行)、'interval'(定时轮询执行)、'wait-until-element-rendered'(等待目标元素渲染后执行)
118
+ * 默认:'default'
119
+ */
120
+ mode: PropTypes.oneOf(['default', 'delay', 'interval', 'wait-until-element-rendered']),
121
+
122
+ /**
123
+ * delay模式下,设置延时执行时长,单位:毫秒
124
+ */
125
+ delay: PropTypes.number,
126
+
127
+ /**
128
+ * interval模式下,设置轮询执行间隔时长,单位:毫秒
129
+ */
130
+ interval: PropTypes.number,
131
+
132
+ /**
133
+ * wait-until-element-rendered模式下,设置需要等待渲染完成的目标元素css选择器
134
+ */
135
+ targetSelector: PropTypes.string,
136
+
137
+ /**
138
+ * wait-until-element-rendered模式下,设置目标元素渲染检测最大等待时长,单位:毫秒,默认无限制
139
+ */
140
+ targetWaitTimeout: PropTypes.number,
141
+
42
142
  loading_state: PropTypes.shape({
43
143
  /**
44
144
  * Determines if the component is loading or not
@@ -63,6 +163,7 @@ FefferyExecuteJs.propTypes = {
63
163
 
64
164
  // 设置默认参数
65
165
  FefferyExecuteJs.defaultProps = {
166
+ mode: 'default'
66
167
  }
67
168
 
68
169
  export default FefferyExecuteJs;
@@ -191,6 +191,22 @@ FefferyRND.propTypes = {
191
191
  */
192
192
  bounds: PropTypes.oneOf(['window', 'parent']),
193
193
 
194
+ /**
195
+ * 设置或监听当前组件是否处于选择状态
196
+ * 默认:false
197
+ */
198
+ selected: PropTypes.bool,
199
+
200
+ /**
201
+ * 设置当前组件在选中状态下的css样式
202
+ */
203
+ selectedStyle: PropTypes.object,
204
+
205
+ /**
206
+ * 配置当前组件在选中状态下的css类名
207
+ */
208
+ selectedClassName: PropTypes.string,
209
+
194
210
  /**
195
211
  * Dash-assigned callback that should be called to report property changes
196
212
  * to Dash, to make them available for callbacks.
@@ -215,7 +231,8 @@ FefferyRND.propTypes = {
215
231
 
216
232
  // 设置默认参数
217
233
  FefferyRND.defaultProps = {
218
- direction: ['top', 'right', 'bottom', 'left', 'topRight', 'bottomRight', 'bottomLeft', 'topLeft']
234
+ direction: ['top', 'right', 'bottom', 'left', 'topRight', 'bottomRight', 'bottomLeft', 'topLeft'],
235
+ selected: false
219
236
  }
220
237
 
221
238
  export default FefferyRND;
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, useState } from 'react';
2
2
  import { Rnd } from 'react-rnd';
3
3
  import { propTypes, defaultProps } from '../../components/draggable/FefferyRND.react';
4
4
  import { clone } from 'lodash';
@@ -28,10 +28,16 @@ const FefferyRND = (props) => {
28
28
  disableDragging,
29
29
  dragAxis,
30
30
  bounds,
31
+ selected,
32
+ selectedStyle,
33
+ selectedClassName,
31
34
  setProps,
32
35
  loading_state
33
36
  } = props;
34
37
 
38
+ // 记录拖拽或尺寸调整结束事件时间戳
39
+ const [dragOrResizeStopTimestamp, setDragOrResizeStopTimestamp] = useState(0);
40
+
35
41
  useEffect(() => {
36
42
  // size缺省且defaultState.width,defaultState.height有效时,进行赋值
37
43
  if (!size && defaultState.width && defaultState.height) {
@@ -74,8 +80,8 @@ const FefferyRND = (props) => {
74
80
 
75
81
  return (
76
82
  <Rnd id={id}
77
- style={style}
78
- className={className}
83
+ style={{ ...style, ...(selected ? selectedStyle : {}) }}
84
+ className={(selected && selectedClassName) ? className + ' ' + selectedClassName : className}
79
85
  default={defaultState}
80
86
  size={size}
81
87
  position={position}
@@ -92,8 +98,15 @@ const FefferyRND = (props) => {
92
98
  disableDragging={disableDragging}
93
99
  dragAxis={dragAxis}
94
100
  bounds={bounds}
95
- onDragStop={(e, d) => setProps({ position: { x: d.x, y: d.y } })}
101
+ onDragStop={(e, d) => {
102
+ // 若本次拖拽事件导致了有效偏移,则更新拖拽结束事件时间戳
103
+ if (position && (position.x !== d.x || position.y !== d.y)) {
104
+ setDragOrResizeStopTimestamp(new Date().getTime());
105
+ }
106
+ setProps({ position: { x: d.x, y: d.y } })
107
+ }}
96
108
  onResizeStop={(e, direction, ref, delta, p) => {
109
+ setDragOrResizeStopTimestamp(new Date().getTime());
97
110
  setProps({
98
111
  size: {
99
112
  width: ref.style.width,
@@ -105,6 +118,13 @@ const FefferyRND = (props) => {
105
118
  }
106
119
  })
107
120
  }}
121
+ onClick={() => {
122
+ // 若距离最近一次拖拽结束事件时间戳大于200ms,则切换选中状态
123
+ // 从而避免拖拽导致的选中误操作
124
+ if (new Date().getTime() - dragOrResizeStopTimestamp >= 200) {
125
+ setProps({ selected: !selected })
126
+ }
127
+ }}
108
128
  data-dash-is-loading={
109
129
  (loading_state && loading_state.is_loading) || undefined
110
130
  }>
package/usage.py CHANGED
@@ -1,33 +1,63 @@
1
1
  import dash
2
+ import json
2
3
  from dash import html
3
4
  import feffery_utils_components as fuc
5
+ from dash.dependencies import Input, Output
4
6
 
5
- app = dash.Dash(__name__, compress=True)
7
+ app = dash.Dash(__name__)
6
8
 
7
9
  app.layout = html.Div(
8
10
  [
9
- fuc.FefferyAnimatedImage(
10
- src="https://shoelace.style/assets/images/walk.gif",
11
- alt="Animation of untied shoes walking on pavement",
12
- play=True,
13
- style={
14
- 'width': 300
15
- }
11
+ # wait-until-element-rendered模式示例
12
+ html.Button(
13
+ 'wait-until-element-rendered模式',
14
+ id='execute-js'
16
15
  ),
17
- fuc.FefferyAnimatedImage(
18
- src="https://shoelace.style/assets/images/tie.webp",
19
- alt="Animation of a shoe being tied",
20
- play=True,
21
- style={
22
- 'width': 300
23
- }
16
+ # 控制目标元素渲染
17
+ html.Button(
18
+ '渲染目标元素',
19
+ id='render-target-element'
20
+ ),
21
+ html.Div(
22
+ id='execute-js-output'
23
+ ),
24
+ html.Div(
25
+ id='target-element-container'
24
26
  )
25
27
  ],
26
28
  style={
27
- 'padding': '50px 100px'
29
+ 'padding': '50px 50px 0 50px'
28
30
  }
29
31
  )
30
32
 
31
33
 
34
+ @app.callback(
35
+ Output('target-element-container', 'children'),
36
+ Input('render-target-element', 'n_clicks'),
37
+ prevent_initial_call=True
38
+ )
39
+ def render_target_element(n_clicks):
40
+
41
+ return html.Span(
42
+ '我来也!',
43
+ id='target-element'
44
+ )
45
+
46
+
47
+ @app.callback(
48
+ Output('execute-js-output', 'children'),
49
+ Input('execute-js', 'n_clicks'),
50
+ prevent_initial_call=True
51
+ )
52
+ def handle_execute_js(n_clicks):
53
+
54
+ return fuc.FefferyExecuteJs(
55
+ jsString="alert('目标元素出现!')",
56
+ mode='wait-until-element-rendered',
57
+ targetSelector='#target-element',
58
+ targetWaitTimeout=5000 # 至多等待5秒
59
+ )
60
+
61
+
32
62
  if __name__ == '__main__':
33
63
  app.run(debug=True)