plain-design 1.0.0-beta.78 → 1.0.0-beta.79

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.
@@ -0,0 +1,220 @@
1
+ @include comp(dialog) {
2
+ &.#{componentName(search-service)} {
3
+ align-items: flex-start;
4
+ padding: 80px 0;
5
+
6
+ .dialog-content {
7
+ overflow: hidden;
8
+ }
9
+
10
+ @include comp(empty) {
11
+ min-height: 100px;
12
+ }
13
+
14
+ .search-service-input-box {
15
+ @include comp(input) {
16
+ border-color: plv(primary-6) !important;
17
+
18
+ .input-box {
19
+ color: plv(primary-6) !important;
20
+ font-size: 1.2em;
21
+ height: 2.5em !important;
22
+ }
23
+
24
+ .input-prefix-icon {
25
+ width: 2em;
26
+ font-size: 1.6em;
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ color: plv(primary-6);
31
+ position: relative;
32
+ left: 0.15em;
33
+ }
34
+ .input-suffix-icon-wrapper {
35
+ width: 1.5em;
36
+ font-size: 1.6em;
37
+ }
38
+ @include comp(loading) {
39
+ }
40
+ }
41
+ }
42
+
43
+ .search-service-foot {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ font-size: 0.8em;
48
+ color: plv(text-3);
49
+ border-top: solid 1px plv(border-color);
50
+
51
+ & > * + * {
52
+ display: inline-block;
53
+ margin-left: 1em;
54
+ }
55
+
56
+ svg {
57
+ position: relative;
58
+ top: -1px;
59
+ }
60
+ }
61
+
62
+ .search-service-option-virtual-list {
63
+ height: 50vh;
64
+ }
65
+
66
+ .search-service-option-item {
67
+ user-select: none;
68
+
69
+ .search-service-option-item-default {
70
+ text-align: left;
71
+ box-sizing: border-box;
72
+
73
+ &[data-service-item-type="group"] {
74
+ .search-service-option-item-default-title {
75
+ font-weight: 600;
76
+ color: plv(primary-6);
77
+ font-size: 1em;
78
+ }
79
+ }
80
+
81
+ &:not([data-service-item-type="group"]) {
82
+ background-color: plv(bg-4);
83
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.25);
84
+ transition: all ease 150ms;
85
+
86
+ .search-service-option-item-default-box {
87
+ display: flex;
88
+ align-items: center;
89
+
90
+ .search-service-option-item-default-label {
91
+ flex: 1;
92
+ display: flex;
93
+ flex-direction: column;
94
+ }
95
+
96
+ .search-service-option-item-default-button {
97
+ opacity: 0;
98
+ display: flex;
99
+ align-items: center;
100
+ }
101
+ }
102
+ }
103
+
104
+ }
105
+
106
+ &[data-active=true] {
107
+ .search-service-option-item-default {
108
+ &:not([data-service-item-type="group"]) {
109
+ background-color: plv(primary-6);
110
+
111
+ svg, .search-service-option-item-default-label {
112
+ color: plv(pbfc);
113
+
114
+ span {
115
+ color: plv(pbfc) !important;
116
+ }
117
+ }
118
+
119
+ .search-service-option-item-default-button {
120
+ opacity: 1;
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ .search-service-option-item-default-button {
127
+ padding: 0.2em;
128
+
129
+ &[data-search-button="favorite"], &[data-search-button="remove"] {
130
+ &:hover {
131
+ background-color: plv(primary-8);
132
+ border-radius: 99px;
133
+ }
134
+ }
135
+
136
+ &[data-search-button="favorite"] {
137
+ &:hover {
138
+ path {
139
+ fill: plv(pbfc)
140
+ }
141
+ }
142
+ }
143
+
144
+ & + .search-service-option-item-default-button {
145
+ margin-left: 0.1em !important;
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ @include comp(search-service-panel) {
152
+ @include sizeMixin(box, ()) {
153
+ .search-service-input-box {
154
+ padding: $margin;
155
+ @include comp(input) {
156
+ border-radius: calc(#{$border-radius} / 1.5);
157
+ }
158
+ }
159
+ .search-service-foot {
160
+ padding: calc(#{$margin});
161
+ }
162
+
163
+ .search-service-option-item {
164
+ width: calc(100% - (#{$margin} * 2));
165
+ margin-left: $margin;
166
+ padding-bottom: 8px;
167
+
168
+ .search-service-option-item-default {
169
+ border-radius: calc(#{$border-radius} / 1.5);
170
+
171
+ &:not([data-service-item-type="group"]) {
172
+ padding: calc(#{$margin} / 2);
173
+ cursor: pointer;
174
+
175
+ .search-service-option-item-default-box {
176
+ min-height: calc(3.5em - #{$margin} / 2);
177
+
178
+ & > * + * {
179
+ margin-left: 0.5em;
180
+ }
181
+
182
+ .search-service-option-item-default-tree {
183
+ width: 1.8em;
184
+ height: 3.2em;
185
+ }
186
+
187
+ .search-service-option-item-default-label {
188
+
189
+ & > span:nth-child(1) {
190
+ color: plv(text-1);
191
+ }
192
+
193
+ & > span:nth-child(2) {
194
+ color: plv(text-3);
195
+ font-size: 0.8em;
196
+ margin-top: 0.25em;
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ }
203
+ .search-service-option-item-default {
204
+ &[data-service-item-type="group"] {
205
+ .search-service-option-item-default-title {
206
+ }
207
+ }
208
+ }
209
+
210
+ .search-service-empty {
211
+ padding-bottom: $margin;
212
+ margin: $margin 0;
213
+
214
+ svg + div {
215
+ margin-top: $margin;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
@@ -0,0 +1,112 @@
1
+ import {iMouseEvent} from "plain-design-composition";
2
+
3
+ export interface iSearchDataRender {
4
+ (dataMeta: iSearchDataMeta): any;
5
+ }
6
+
7
+ export interface iSearchDataMeta {
8
+ title?: string,
9
+ desc?: string,
10
+ type: 'group' | 'page' | 'header' | 'sub_header' | 'content' | 'favorite' | 'history',
11
+ data?: any
12
+ }
13
+
14
+ export interface iSearchServiceDefaultConfig {
15
+ width: number,
16
+ render?: iSearchDataRender,
17
+ placeholder: string,
18
+ footer?: () => any,
19
+ cacheName?: string,
20
+ }
21
+
22
+ export interface iSearchServiceCustomConfig {
23
+ getData: (searchKey: string) => Promise<iSearchDataMeta[]>,
24
+ onSelect: (data: iSearchDataMeta) => void,
25
+ }
26
+
27
+ export type iSearchServiceConfig = iSearchServiceDefaultConfig & iSearchServiceCustomConfig
28
+
29
+ export const SearchType2Icon: Record<iSearchDataMeta['type'], () => any> = {
30
+ group: () => null,
31
+ page: () => (
32
+ <svg width="20" height="20" viewBox="0 0 20 20">
33
+ <path d="M17 6v12c0 .52-.2 1-1 1H4c-.7 0-1-.33-1-1V2c0-.55.42-1 1-1h8l5 5zM14 8h-3.13c-.51 0-.87-.34-.87-.87V4" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinejoin="round"></path>
34
+ </svg>
35
+ ),
36
+ header: () => (
37
+ <svg width="20" height="20" viewBox="0 0 20 20">
38
+ <path d="M13 13h4-4V8H7v5h6v4-4H7V8H3h4V3v5h6V3v5h4-4v5zm-6 0v4-4H3h4z" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round"></path>
39
+ </svg>
40
+ ),
41
+ sub_header: () => (
42
+ <svg width="20" height="20" viewBox="0 0 20 20">
43
+ <path d="M13 13h4-4V8H7v5h6v4-4H7V8H3h4V3v5h6V3v5h4-4v5zm-6 0v4-4H3h4z" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round"></path>
44
+ </svg>
45
+ ),
46
+ content: () => (
47
+ <svg width="20" height="20" viewBox="0 0 20 20">
48
+ <path d="M17 5H3h14zm0 5H3h14zm0 5H3h14z" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinejoin="round"></path>
49
+ </svg>
50
+ ),
51
+ history: () => (
52
+ <svg width="20" height="20" viewBox="0 0 20 20">
53
+ <g stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
54
+ <path d="M3.18 6.6a8.23 8.23 0 1112.93 9.94h0a8.23 8.23 0 01-11.63 0"></path>
55
+ <path d="M6.44 7.25H2.55V3.36M10.45 6v5.6M10.45 11.6L13 13"></path>
56
+ </g>
57
+ </svg>
58
+ ),
59
+ favorite: () => (
60
+ <svg width="20" height="20" viewBox="0 0 20 20">
61
+ <path d="M10 14.2L5 17l1-5.6-4-4 5.5-.7 2.5-5 2.5 5 5.6.8-4 4 .9 5.5z" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinejoin="round"></path>
62
+ </svg>
63
+ ),
64
+ };
65
+
66
+ export const SearchTreeIcon = {
67
+ normal: () => (<svg className="search-service-option-item-default-tree" viewBox="0 0 24 54">
68
+ <g stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
69
+ <path d="M8 6v42M20 27H8.3"></path>
70
+ </g>
71
+ </svg>),
72
+ last: () => (
73
+ <svg className="search-service-option-item-default-tree" viewBox="0 0 24 54">
74
+ <g stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
75
+ <path d="M8 6v21M20 27H8.3"></path>
76
+ </g>
77
+ </svg>
78
+ )
79
+ };
80
+
81
+ export const SearchOptionButton = {
82
+ normal: (onClick?: (e: iMouseEvent) => void) => {
83
+ return (
84
+ <div key="normal" className="search-service-option-item-default-button" data-search-button="normal" onClick={onClick}>
85
+ <svg width="20" height="20" viewBox="0 0 20 20">
86
+ <g stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
87
+ <path d="M18 3v4c0 2-2 4-4 4H2"></path>
88
+ <path d="M8 17l-6-6 6-6"></path>
89
+ </g>
90
+ </svg>
91
+ </div>
92
+ );
93
+ },
94
+ favorite: (onClick?: (e: iMouseEvent) => void) => {
95
+ return (
96
+ <div key="favorite" className="search-service-option-item-default-button" data-search-button="favorite" onClick={onClick}>
97
+ <svg width="20" height="20" viewBox="0 0 20 20">
98
+ <path d="M10 14.2L5 17l1-5.6-4-4 5.5-.7 2.5-5 2.5 5 5.6.8-4 4 .9 5.5z" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinejoin="round"></path>
99
+ </svg>
100
+ </div>
101
+ );
102
+ },
103
+ remove: (onClick?: (e: iMouseEvent) => void) => {
104
+ return (
105
+ <div key="remove" className="search-service-option-item-default-button" data-search-button="remove" onClick={onClick}>
106
+ <svg width="20" height="20" viewBox="0 0 20 20">
107
+ <path d="M10 10l5.09-5.09L10 10l5.09 5.09L10 10zm0 0L4.91 4.91 10 10l-5.09 5.09L10 10z" stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round"></path>
108
+ </svg>
109
+ </div>
110
+ );
111
+ },
112
+ };
@@ -1,4 +1,4 @@
1
- import {computed, designComponent, Fragment, getComponentCls, iHTMLDivElement, mergeAttrs, useClasses, useRefs, useStyles} from "plain-design-composition";
1
+ import {computed, designComponent, Fragment, getComponentCls, iHTMLDivElement, mergeAttrs, onMounted, useClasses, useRefs, useStyles} from "plain-design-composition";
2
2
  import './index.scss';
3
3
  import {ThemeStatus, useStyle} from "../../uses/useStyle";
4
4
  import {useEdit} from "../../uses/useEdit";
@@ -17,6 +17,7 @@ import {useFocusHandler} from "../../uses/useFocusHandler";
17
17
  import {useSuggestionInput} from "./useSuggestionInput";
18
18
  import {useMultipleModel} from "../../uses/useMultipleModel";
19
19
  import {useParentPopupId} from "../usePopup/utils/popup.utils";
20
+ import {delay} from "plain-utils/utils/delay";
20
21
 
21
22
  export const Input = designComponent({
22
23
  name: '-input',
@@ -106,6 +107,13 @@ export const Input = designComponent({
106
107
  hooks.onInput.use(val => emit.onInput(val));
107
108
  hooks.onSuggestion.use(data => emit.onSuggestion(data));
108
109
 
110
+ onMounted(async () => {
111
+ await delay(78);
112
+ if (attrs.autoFocus) {
113
+ refs.input?.focus();
114
+ }
115
+ });
116
+
109
117
  return {
110
118
  refer: {
111
119
  model,
@@ -26,10 +26,11 @@ export const InputPropsOption = {
26
26
  multipleSeparator: { default: /[\s\n,,]/ }, // 多值输入的时候的分隔符。类型为字符串或者正则表达式;有这个分隔符的话,会自动按照这个分隔符对输入的文本分割
27
27
  fixedWidth: { type: Boolean }, // InputGroup下是否固定宽度
28
28
  bare: { type: Boolean }, // 去掉包裹节点
29
+ loadingType: { type: String }, // loading类型
29
30
 
30
31
  /*clear*/
31
32
  noClear: { type: Boolean }, // 去掉清空按钮
32
- clearHandler: { type: Function as PropType<(e: iMouseEvent) => void> }, // 自定义处理清空逻辑
33
+ clearHandler: { type: Function as PropType<(e: iMouseEvent) => void> }, // 自定义处理清空逻辑
33
34
 
34
35
  /*textarea*/
35
36
  textarea: { type: Boolean }, // 当前是否为文本域输入框
@@ -59,7 +59,7 @@ export function useInputSuffixIcon({ slots, hooks, props, editComputed, model, e
59
59
  <span className="input-suffix-icon-wrapper" onMouseDown={handler.onMousedownSuffix}>
60
60
  {hasSuffixIcon.value && slots.suffixIconContent(<Icon icon={props.suffixIcon} className="input-suffix-icon"/>)}
61
61
  {hasSuffixClear.value && (<Icon icon="pi-close-circle-fill" className="input-suffix-clear"/>)}
62
- {editComputed.value.loading && <Loading/>}
62
+ {editComputed.value.loading && <Loading type={props.loadingType}/>}
63
63
  </span>
64
64
  );
65
65
 
@@ -8,6 +8,7 @@ import {VerticalScrollbar} from "./VerticalScrollbar";
8
8
  import {HorizontalScrollbar} from "./HorizontalScrollbar";
9
9
  import {ResizeDetectFuncParam, useResizeDetector} from "../../directives/ResizeDetector";
10
10
  import {createScrollUtils} from "../createScrollUtils";
11
+ import {getRectAutoFormat} from "plain-utils/dom/getRectAutoFormat";
11
12
 
12
13
  export enum PLAIN_SCROLL_VERTICAL_POSITION {
13
14
  top = 'top',
@@ -213,6 +214,39 @@ export const Scroll = designComponent({
213
214
  autoScrollLeft() {scrollUtils.autoScrollLeft();},
214
215
  autoScrollRight() {scrollUtils.autoScrollRight();},
215
216
  stopAutoScroll() {scrollUtils.stopAutoScroll();},
217
+ showElement: (elementSelector: string | HTMLElement): boolean => {
218
+ if (!refs.host || !refs.content) {return false;}
219
+ const selectElement = typeof elementSelector === "string" ? refs.content?.querySelector(elementSelector) : elementSelector;
220
+
221
+ if (selectElement == null) {return false;}
222
+ const selectElementRect = getRectAutoFormat(selectElement as any);
223
+ const contentElementRect = getRectAutoFormat(refs.content);
224
+ const hostElementRect = getRectAutoFormat(refs.host);
225
+
226
+ /*vertical*/
227
+ if (selectElementRect.bottom >= hostElementRect.bottom) {
228
+ /*在下面*/
229
+ const offsetTop = selectElementRect.top - contentElementRect.top;
230
+ methods.scrollTop(offsetTop - (hostElementRect.height - selectElementRect.height));
231
+ } else if (selectElementRect.top <= hostElementRect.top) {
232
+ /*在上面*/
233
+ const offsetTop = selectElementRect.top - contentElementRect.top;
234
+ methods.scrollTop(offsetTop);
235
+ }
236
+
237
+ /*horizontal*/
238
+ if (selectElementRect.right >= hostElementRect.right) {
239
+ /*在右边*/
240
+ const offsetLeft = selectElementRect.left - contentElementRect.left;
241
+ methods.scrollLeft(offsetLeft - (hostElementRect.width - selectElementRect.width));
242
+ } else if (selectElementRect.left <= hostElementRect.left) {
243
+ /*在左边*/
244
+ const offsetLeft = selectElementRect.left - contentElementRect.left;
245
+ methods.scrollLeft(offsetLeft);
246
+ }
247
+
248
+ return true;
249
+ },
216
250
  /**
217
251
  * 禁用list的队列动画,300ms后恢复正常
218
252
  * @author 韦胜健
@@ -58,6 +58,9 @@ export function usePlcTreeRenderNode(
58
58
  */
59
59
  const renderTreeNode = (renderScope: iTableCellRenderScope) => {
60
60
  const treeNode = treeCore.flatNodeComputedData.value[renderScope.node.state.index];
61
+ if (!treeNode) {
62
+ return null;
63
+ }
61
64
  return (
62
65
  <RenderPlcTreeNode
63
66
  treeCore={treeCore}
@@ -189,6 +189,14 @@ export type {
189
189
  iPlainResponseDataType,
190
190
  iHttpResponseDataType
191
191
  } from './components/createHttp/http.utils';
192
+ export type {
193
+ iSearchServiceCustomConfig,
194
+ iSearchServiceConfig,
195
+ iSearchServiceDefaultConfig,
196
+ iSearchDataMeta,
197
+ iSearchDataRender,
198
+ } from './components/$search/search.utils';
199
+ export {$search} from './components/$search';
192
200
 
193
201
  export {Address} from './components/Address';
194
202
  export {AddressCascade} from './components/AddressCascade';
@@ -305,5 +305,13 @@ export const EnUsLocale: tZhCnLocale = {
305
305
  洋红: 'Magenta',
306
306
  极昼: 'Light',
307
307
  黑夜: 'Dark',
308
- }
308
+ },
309
+ search: {
310
+ select: 'Select',
311
+ switch: 'Switch',
312
+ noHistory: 'No Search History',
313
+ noMatch: 'There are no search result that can match "{val}"',
314
+ searchHistory: 'Search History',
315
+ favorite: 'Favorite'
316
+ },
309
317
  };
@@ -115,8 +115,8 @@ export const ZhCnLocale = {
115
115
  save: '保存',
116
116
  done: '完成',
117
117
  },
118
- yes:'是',
119
- no:'否',
118
+ yes: '是',
119
+ no: '否',
120
120
  },
121
121
  formatter: {
122
122
  week: 'gggg年第ww周',
@@ -303,7 +303,15 @@ export const ZhCnLocale = {
303
303
  洋红: '洋红',
304
304
  极昼: '极昼',
305
305
  黑夜: '黑夜',
306
- }
306
+ },
307
+ search: {
308
+ select: '选择',
309
+ switch: '切换',
310
+ noHistory: '没有搜索历史',
311
+ noMatch: '无法找到搜索结果 "{val}"',
312
+ searchHistory: '搜索历史',
313
+ favorite: '收藏'
314
+ },
307
315
  } as const;
308
316
 
309
317
  type ZhCnLocaleUtils<T> = { [k in keyof T]: T[k] extends string ? string : ZhCnLocaleUtils<T[k]> }
@@ -0,0 +1,38 @@
1
+ export function createListUtils<T>(
2
+ {
3
+ getList,
4
+ current,
5
+ }: {
6
+ getList: () => T[],
7
+ current: () => number | undefined | null,
8
+ }
9
+ ) {
10
+ return {
11
+ prevIndex: () => {
12
+ const list = getList();
13
+ if (!list.length) {
14
+ return -1;
15
+ }
16
+ let curIdx = current();
17
+ if (curIdx == null || curIdx - 1 < 0) {
18
+ curIdx = list.length - 1;
19
+ } else {
20
+ curIdx = curIdx - 1;
21
+ }
22
+ return curIdx;
23
+ },
24
+ nextIndex: () => {
25
+ const list = getList();
26
+ if (!list.length) {
27
+ return -1;
28
+ }
29
+ let curIdx = current();
30
+ if (curIdx == null || curIdx + 1 > list.length - 1) {
31
+ curIdx = 0;
32
+ } else {
33
+ curIdx = curIdx + 1;
34
+ }
35
+ return curIdx;
36
+ },
37
+ };
38
+ }