@farris/cli 2.0.2-ba-beta1 → 2.0.3

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.
Files changed (76) hide show
  1. package/lib/common/get-vite-config.js +1 -3
  2. package/package.json +2 -2
  3. package/templates/lib/.eslintrc.cjs +15 -0
  4. package/templates/lib/.prettierrc.json +8 -0
  5. package/templates/lib/components/button/index.ts +7 -0
  6. package/templates/lib/components/button/src/button.component.tsx +84 -0
  7. package/templates/lib/components/button/src/button.props.ts +55 -0
  8. package/templates/lib/components/button/src/button.scss +179 -0
  9. package/templates/lib/components/common/index.ts +6 -0
  10. package/templates/lib/components/common/src/common.scss +4 -0
  11. package/templates/lib/components/common/src/compositions/index.ts +19 -0
  12. package/templates/lib/components/common/src/compositions/types.ts +6 -0
  13. package/templates/lib/components/common/src/compositions/use-bem/index.ts +46 -0
  14. package/templates/lib/components/common/src/compositions/use-click-away/index.ts +40 -0
  15. package/templates/lib/components/common/src/compositions/use-context/use-children-contexts.ts +40 -0
  16. package/templates/lib/components/common/src/compositions/use-context/use-parent-context.ts +24 -0
  17. package/templates/lib/components/common/src/compositions/use-event-listener/index.ts +45 -0
  18. package/templates/lib/components/common/src/compositions/use-expose/index.ts +9 -0
  19. package/templates/lib/components/common/src/compositions/use-lay-render/index.ts +17 -0
  20. package/templates/lib/components/common/src/compositions/use-link/index.ts +14 -0
  21. package/templates/lib/components/common/src/compositions/use-lock-scroll/index.ts +25 -0
  22. package/templates/lib/components/common/src/compositions/use-long-press/index.ts +141 -0
  23. package/templates/lib/components/common/src/compositions/use-momentum/index.ts +82 -0
  24. package/templates/lib/components/common/src/compositions/use-mount-component/index.ts +48 -0
  25. package/templates/lib/components/common/src/compositions/use-rect/index.ts +31 -0
  26. package/templates/lib/components/common/src/compositions/use-refs/index.ts +21 -0
  27. package/templates/lib/components/common/src/compositions/use-resize-observer/index.ts +85 -0
  28. package/templates/lib/components/common/src/compositions/use-resize-observer/utils.ts +37 -0
  29. package/templates/lib/components/common/src/compositions/use-scroll-parent/index.ts +42 -0
  30. package/templates/lib/components/common/src/compositions/use-touch/index.ts +82 -0
  31. package/templates/lib/components/common/src/compositions/use-touch-move/index.ts +120 -0
  32. package/templates/lib/components/common/src/entity/base-property.ts +129 -0
  33. package/templates/lib/components/common/src/entity/entity-schema.ts +274 -0
  34. package/templates/lib/components/common/src/entity/input-base-property.ts +285 -0
  35. package/templates/lib/components/common/src/style/animation/index.scss +150 -0
  36. package/templates/lib/components/common/src/style/base.scss +63 -0
  37. package/templates/lib/components/common/src/style/fonts/farris-mobile-icon.ttf +0 -0
  38. package/templates/lib/components/common/src/style/icon.scss +6 -0
  39. package/templates/lib/components/common/src/style/index.scss +4 -0
  40. package/templates/lib/components/common/src/style/mixins/bem.scss +204 -0
  41. package/templates/lib/components/common/src/style/mixins/border-radius.scss +13 -0
  42. package/templates/lib/components/common/src/style/mixins/ellipsis.scss +15 -0
  43. package/templates/lib/components/common/src/style/mixins/hairline.scss +113 -0
  44. package/templates/lib/components/common/src/style/mixins/index.scss +6 -0
  45. package/templates/lib/components/common/src/style/mixins/margin.scss +10 -0
  46. package/templates/lib/components/common/src/style/mixins/safe-area.scss +9 -0
  47. package/templates/lib/components/common/src/style/variables.scss +113 -0
  48. package/templates/lib/components/common/src/utils/index.ts +15 -0
  49. package/templates/lib/components/common/src/utils/src/common.ts +83 -0
  50. package/templates/lib/components/common/src/utils/src/date.ts +134 -0
  51. package/templates/lib/components/common/src/utils/src/dom/event.ts +37 -0
  52. package/templates/lib/components/common/src/utils/src/hook.ts +18 -0
  53. package/templates/lib/components/common/src/utils/src/number.ts +26 -0
  54. package/templates/lib/components/common/src/utils/src/query-filter.ts +40 -0
  55. package/templates/lib/components/common/src/utils/src/resove-asset.ts +33 -0
  56. package/templates/lib/components/common/src/utils/src/string.ts +18 -0
  57. package/templates/lib/components/common/src/utils/src/throttle.ts +41 -0
  58. package/templates/lib/components/common/src/utils/src/transition.ts +14 -0
  59. package/templates/lib/components/common/src/utils/src/type.ts +105 -0
  60. package/templates/lib/components/common/src/utils/src/use-appearance.ts +33 -0
  61. package/templates/lib/components/common/src/utils/src/with-install.ts +16 -0
  62. package/templates/lib/components/common/src/utils/src/with-register-designer.ts +16 -0
  63. package/templates/lib/components/common/src/utils/src/with-register.ts +16 -0
  64. package/templates/lib/components/common/types.ts +131 -0
  65. package/templates/lib/components/index.scss +2 -0
  66. package/templates/lib/components/index.ts +21 -0
  67. package/templates/lib/farris.config.mjs +56 -0
  68. package/templates/lib/index.html +12 -0
  69. package/templates/lib/package.json +32 -0
  70. package/templates/lib/src/App.vue +80 -0
  71. package/templates/lib/src/components/TheButton.vue +3 -0
  72. package/templates/lib/src/main.ts +10 -0
  73. package/templates/lib/src/router/index.ts +23 -0
  74. package/templates/lib/src/views/AboutView.vue +15 -0
  75. package/templates/lib/src/views/HomeView.vue +9 -0
  76. package/templates/lib/tsconfig.json +20 -0
@@ -0,0 +1,285 @@
1
+ import { BaseControlProperty } from './base-property';
2
+ import { DesignerComponentInstance } from "@/components/designer-canvas";
3
+ import { SchemaDOMMapping } from "@/components/property-panel";
4
+ import { canvasChanged } from "@/components/designer-canvas/src/composition/designer-canvas-changed";
5
+ import { FormSchemaEntityFieldType$Type } from './entity-schema';
6
+
7
+ export class InputBaseProperty extends BaseControlProperty {
8
+ constructor(componentId: string, designerHostService: any) {
9
+ super(componentId, designerHostService);
10
+ }
11
+
12
+ public getPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance) {
13
+ // 基本信息
14
+ this.propertyConfig.categories['basic'] = this.getBasicProperties(propertyData, componentInstance);
15
+ // 外观
16
+ this.propertyConfig.categories['appearance'] = this.getAppearanceProperties(propertyData, componentInstance);
17
+ // 编辑器
18
+ this.propertyConfig.categories['editor'] = this.getEditorProperties(propertyData);
19
+ return this.propertyConfig;
20
+ }
21
+
22
+ public getBasicProperties(propertyData, componentInstance): any {
23
+ const self = this;
24
+ this.setDesignViewModelField(propertyData);
25
+ return {
26
+ description: 'Basic Information',
27
+ title: '基本信息',
28
+ properties: {
29
+ id: {
30
+ description: '组件标识',
31
+ title: '标识',
32
+ type: 'string',
33
+ readonly: true
34
+ },
35
+ type: {
36
+ description: '编辑器类型',
37
+ title: '编辑器类型',
38
+ type: 'string',
39
+ refreshPanelAfterChanged: true,
40
+ $converter: '/converter/change-editor.converter',
41
+ editor: {
42
+ type: 'combo-list',
43
+ textField: 'value',
44
+ valueField: 'key',
45
+ editable: false,
46
+ data: self.designViewModelField ? SchemaDOMMapping.getEditorTypesByMDataType(self.designViewModelField.type?.name) : SchemaDOMMapping.getAllInputTypes()
47
+ }
48
+ },
49
+ label: {
50
+ title: "标签",
51
+ type: "string",
52
+ $converter: '/converter/form-group-label.converter'
53
+ },
54
+ binding: {
55
+ description: "绑定的表单字段",
56
+ title: "绑定",
57
+ editor: {
58
+ type: "binding-selector",
59
+ bindingType: { "enable": false },
60
+ editorParams: {
61
+ componentSchema: propertyData,
62
+ needSyncToViewModel: true,
63
+ viewModelId: this.viewModelId,
64
+ designerHostService: this.designerHostService,
65
+ disableOccupiedFields: true
66
+ },
67
+ textField: 'bindingField'
68
+ }
69
+ }
70
+ },
71
+ setPropertyRelates(changeObject, prop) {
72
+ if (!changeObject) {
73
+ return;
74
+ }
75
+ switch (changeObject && changeObject.propertyID) {
76
+ case 'type': {
77
+ self.changeControlType(propertyData, changeObject, componentInstance);
78
+ break;
79
+ }
80
+ case 'label': {
81
+ changeObject.needRefreshControlTree = true;
82
+ break;
83
+ }
84
+ }
85
+ }
86
+ };
87
+ }
88
+ public getAppearanceProperties(propertyData, componentInstance): any {
89
+
90
+ const self = this;
91
+ return {
92
+ title: "外观",
93
+ description: "Appearance",
94
+ properties: {
95
+ class: {
96
+ title: "class样式",
97
+ type: "string",
98
+ description: "组件的CSS样式",
99
+ $converter: "/converter/appearance.converter"
100
+ },
101
+ style: {
102
+ title: "style样式",
103
+ type: "string",
104
+ description: "组件的样式",
105
+ $converter: "/converter/appearance.converter"
106
+ }
107
+ },
108
+ setPropertyRelates(changeObject, prop) {
109
+ if (!changeObject) {
110
+ return;
111
+ }
112
+ switch (changeObject && changeObject.propertyID) {
113
+ case 'class':
114
+ self.updateElementByParentContainer(propertyData.id, componentInstance);
115
+ break;
116
+ }
117
+
118
+ }
119
+ };
120
+ };
121
+
122
+ public getEditorProperties(propertyData): any {
123
+ return this.getComponentConfig(propertyData);
124
+ }
125
+
126
+
127
+ /**
128
+ * 卡片控件:切换控件类型后事件
129
+ * @param propertyData 控件DOM属性
130
+ * @param newControlType 新控件类型
131
+ */
132
+ private changeControlType(propertyData, changeObject, componentInstance: DesignerComponentInstance) {
133
+ const newControlType = changeObject.propertyValue;
134
+
135
+ // 1、定位控件父容器
136
+ const parentContainer = componentInstance && componentInstance.parent && componentInstance.parent['schema'];
137
+ if (!parentContainer) {
138
+ return;
139
+ }
140
+
141
+ const index = parentContainer.contents.findIndex(c => c.id === propertyData.id);
142
+ const oldControl = parentContainer.contents[index];
143
+
144
+ let newControl;
145
+ // 2、记录绑定字段viewModel的变更
146
+ if (this.designViewModelField) {
147
+ const dgViewModel = this.designViewModelUtils.getDgViewModel(this.viewModelId);
148
+ dgViewModel.changeField(this.designViewModelField.id, {
149
+ editor: {
150
+ $type: newControlType
151
+ },
152
+ name: this.designViewModelField.name,
153
+ require: this.designViewModelField.require,
154
+ readonly: this.designViewModelField.readonly
155
+ }, false);
156
+ // 3、创建新控件
157
+ newControl = this.controlCreatorUtils.setFormFieldProperty(this.designViewModelField, newControlType);
158
+ }
159
+ if (!newControl) {
160
+ newControl = this.controlCreatorUtils.createFormGroupWithoutField(newControlType);
161
+ }
162
+ // 4、保留原id样式等属性
163
+ Object.assign(newControl, {
164
+ id: oldControl.id,
165
+ appearance: oldControl.appearance,
166
+ size: oldControl.size,
167
+ label: oldControl.label,
168
+ binding: oldControl.binding,
169
+ visible: oldControl.visible
170
+ });
171
+ Object.assign(newControl.editor, {
172
+ isTextArea: newControl.isTextArea && oldControl.isTextArea,
173
+ placeholder: oldControl.editor?.placeholder,
174
+ holdPlace: oldControl.editor?.holdPlace,
175
+ readonly: oldControl.editor?.readonly,
176
+ required: oldControl.editor?.required,
177
+ });
178
+
179
+ // 5、替换控件
180
+ parentContainer.contents.splice(index, 1);
181
+ parentContainer.contents.splice(index, 0, newControl);
182
+ componentInstance.schema = Object.assign(oldControl, newControl);
183
+
184
+ // 6、暂时移除旧控件的选中样式(后续考虑更好的方式)
185
+ Array.from(document.getElementsByClassName('dgComponentSelected') as HTMLCollectionOf<HTMLElement>).forEach(
186
+ (element: HTMLElement) => element.classList.remove('dgComponentSelected')
187
+ );
188
+
189
+ Array.from(document.getElementsByClassName('dgComponentFocused') as HTMLCollectionOf<HTMLElement>).forEach(
190
+ (element: HTMLElement) => element.classList.remove('dgComponentFocused')
191
+ );
192
+ // 7、触发刷新
193
+ canvasChanged.value++;
194
+
195
+ }
196
+
197
+ public getComponentConfig(propertyData, info = {}, properties = {}, setPropertyRelates?: any) {
198
+ const editorBasic = Object.assign({
199
+ description: "编辑器",
200
+ title: "编辑器",
201
+ type: "input-group",
202
+ $converter: "/converter/property-editor.converter"
203
+ }, info);
204
+
205
+ const editorProperties = Object.assign({
206
+ readonly: {
207
+ description: "",
208
+ title: "只读",
209
+ type: "boolean",
210
+ editor: {
211
+ enableClear: true,
212
+ editable: true
213
+ }
214
+ },
215
+ disabled: {
216
+ description: "",
217
+ title: "禁用",
218
+ type: "boolean",
219
+ visible: false
220
+ },
221
+ required: {
222
+ description: "",
223
+ title: "必填",
224
+ type: "boolean",
225
+ visible: false
226
+ },
227
+ placeholder: {
228
+ description: "空值时,输入控件内的占位文本",
229
+ title: "提示文本",
230
+ type: "string",
231
+ visible: false
232
+
233
+ }
234
+ }, properties);
235
+
236
+ return { ...editorBasic, properties: { ...editorProperties }, setPropertyRelates };
237
+
238
+ }
239
+
240
+
241
+ /**
242
+ * 枚举项编辑器
243
+ * @param propertyData
244
+ * @param valueField
245
+ * @param textField
246
+ * @returns
247
+ */
248
+ protected getItemCollectionEditor(propertyData, valueField, textField) {
249
+ valueField = valueField || 'value';
250
+ textField = textField || 'name';
251
+ return {
252
+ editor: {
253
+ columns: [
254
+ { field: valueField, title: '值', dataType: 'string' },
255
+ { field: textField, title: '名称', dataType: 'string' },
256
+ { field: 'disabled', title: '禁用', visible: false, dataType: 'boolean', editor: { type: 'switch' } },
257
+ ],
258
+ type: "item-collection-editor",
259
+ valueField: valueField,
260
+ nameField: textField,
261
+ requiredFields: [valueField, textField],
262
+ uniqueFields: [valueField, textField],
263
+ readonly: this.checkEnumDataReadonly(propertyData)
264
+ }
265
+ };
266
+ }
267
+ /**
268
+ * 判断枚举数据是否只读
269
+ * 1、没有绑定信息或者绑定变量,可以新增、删除、修改
270
+ * 2、绑定类型为字段,且字段为枚举字段,则不可新增、删除、修改枚举值。只能从be修改然后同步到表单上。
271
+ * @param propertyData 下拉框控件属性值
272
+ */
273
+ private checkEnumDataReadonly(propertyData: any): boolean {
274
+ // 没有绑定信息或者绑定变量
275
+ if (!propertyData.binding || propertyData.binding.type !== 'Form') {
276
+ return false;
277
+ }
278
+ if (this.designViewModelField && this.designViewModelField.type &&
279
+ this.designViewModelField.type.$type === FormSchemaEntityFieldType$Type.EnumType) {
280
+ // 低代码、零代码,枚举字段均不可以改
281
+ return true;
282
+ }
283
+ return false;
284
+ }
285
+ }
@@ -0,0 +1,150 @@
1
+ @keyframes fm-fade-in {
2
+ from {
3
+ opacity: 0;
4
+ }
5
+
6
+ to {
7
+ opacity: 1;
8
+ }
9
+ }
10
+
11
+ @keyframes fm-fade-out {
12
+ from {
13
+ opacity: 1;
14
+ }
15
+
16
+ to {
17
+ opacity: 0;
18
+ }
19
+ }
20
+
21
+ .fm-fade {
22
+ &-enter-active {
23
+ animation: 0.3s fm-fade-in both ease-out;
24
+ }
25
+
26
+ &-leave-active {
27
+ animation: 0.3s fm-fade-out both ease-in;
28
+ }
29
+ }
30
+
31
+ .fm-slide-up {
32
+ &-enter-from,
33
+ &-leave-to {
34
+ transform: translate3d(0, 100%, 0);
35
+ }
36
+ }
37
+
38
+ .fm-slide-down {
39
+ &-enter-from,
40
+ &-leave-to {
41
+ transform: translate3d(0, -100%, 0);
42
+ }
43
+ }
44
+
45
+ .fm-slide-left {
46
+ &-enter-from,
47
+ &-leave-to {
48
+ transform: translate3d(-100%, 0, 0);
49
+ }
50
+ }
51
+
52
+ .fm-slide-right {
53
+ &-enter-from,
54
+ &-leave-to {
55
+ transform: translate3d(100%, 0, 0);
56
+ }
57
+ }
58
+
59
+ .fm-slide-up-enter-active,
60
+ .fm-slide-down-enter-active,
61
+ .fm-slide-left-enter-active,
62
+ .fm-slide-right-enter-active {
63
+ transition-timing-function: ease-out;
64
+ }
65
+ .fm-slide-up-leave-active,
66
+ .fm-slide-down-leave-active,
67
+ .fm-slide-left-leave-active,
68
+ .fm-slide-right-leave-active {
69
+ transition-timing-function: ease-in;
70
+ }
71
+
72
+ .fm-slide-in-enter-active,
73
+ .fm-slide-in-leave-active,
74
+ .fm-slide-out-enter-active,
75
+ .fm-slide-out-leave-active {
76
+ will-change: transform;
77
+ transition: all 0.3s;
78
+ height: 100%;
79
+ width: 100%;
80
+ top: 0px;
81
+ position: absolute;
82
+ backface-visibility: hidden;
83
+ perspective: 1000;
84
+ }
85
+
86
+ .fm-slide-in-leave-to,
87
+ .fm-slide-out-enter-from {
88
+ transform: translateX(-100%);
89
+ opacity: 0;
90
+ }
91
+ .fm-slide-in-enter-from,
92
+ .fm-slide-out-leave-to {
93
+ transform: translateX(100%);
94
+ opacity: 0;
95
+ }
96
+
97
+ @keyframes fm-drop-down-in {
98
+ from {
99
+ height: 0;
100
+ opacity: 0;
101
+ }
102
+
103
+ to {
104
+ opacity: 1;
105
+ }
106
+ }
107
+ @keyframes fm-drop-down-out {
108
+ from {
109
+ opacity: 1;
110
+ }
111
+ to {
112
+ height: 0;
113
+ opacity: 0;
114
+ }
115
+ }
116
+ .fm-drop-down {
117
+ &-enter-active {
118
+ animation: 0.3s fm-drop-down-in both ease-out;
119
+ }
120
+ &-leave-active {
121
+ animation: 0.3s fm-drop-down-out both ease-in;
122
+ }
123
+ }
124
+ @keyframes fm-circular {
125
+ 0% {
126
+ stroke-dasharray: 1, 200;
127
+ stroke-dashoffset: 0
128
+ }
129
+
130
+ 50% {
131
+ stroke-dasharray: 90, 150;
132
+ stroke-dashoffset: -40
133
+ }
134
+
135
+ 100% {
136
+ stroke-dasharray: 90, 150;
137
+ stroke-dashoffset: -120
138
+ }
139
+ }
140
+ @keyframes fm-rotate {
141
+ from {
142
+ -webkit-transform: rotate(0);
143
+ transform: rotate(0)
144
+ }
145
+
146
+ to {
147
+ -webkit-transform: rotate(360deg);
148
+ transform: rotate(360deg)
149
+ }
150
+ }
@@ -0,0 +1,63 @@
1
+ /* Box sizing rules */
2
+ *,
3
+ *::before,
4
+ *::after {
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ /* Remove default margin */
9
+ body,
10
+ h1,
11
+ h2,
12
+ h3,
13
+ h4,
14
+ p,
15
+ figure,
16
+ blockquote,
17
+ dl,
18
+ dd {
19
+ margin: 0;
20
+ }
21
+
22
+ /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
23
+ ul[role="list"],
24
+ ol[role="list"] {
25
+ list-style: none;
26
+ }
27
+
28
+ /* Set core root defaults */
29
+ html:focus-within {
30
+ scroll-behavior: smooth;
31
+ }
32
+
33
+ /* Set core body defaults */
34
+ body {
35
+ min-height: 100vh;
36
+ text-rendering: optimizeSpeed;
37
+ line-height: 1.5;
38
+ font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica,
39
+ "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB",
40
+ "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN",
41
+ "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti",
42
+ SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
43
+ }
44
+
45
+ /* A elements that don't have a class get default styles */
46
+ a:not([class]) {
47
+ text-decoration-skip-ink: auto;
48
+ }
49
+
50
+ /* Make images easier to work with */
51
+ img,
52
+ picture {
53
+ max-width: 100%;
54
+ display: block;
55
+ }
56
+
57
+ /* Inherit fonts for inputs and buttons */
58
+ input,
59
+ button,
60
+ textarea,
61
+ select {
62
+ font: inherit;
63
+ }
@@ -0,0 +1,6 @@
1
+ @font-face {
2
+ font-family: 'farrisMobile';
3
+ font-style: normal;
4
+ font-weight: normal;
5
+ src: url(./fonts/farris-mobile-icon.ttf) format('truetype');
6
+ }
@@ -0,0 +1,4 @@
1
+ // @import './base';
2
+ // @import './icon';
3
+ // @import './variables';
4
+ // @import './animation';
@@ -0,0 +1,204 @@
1
+ /*
2
+ 基于 BEM(Block Element Modifier)命名规范的 SCSS 混合(mixins)和辅助函数,用于生成符合 BEM 规范的 CSS 类名
3
+ */
4
+
5
+ // 命名空间前缀,所有类名都会以 fm- 开头
6
+ $namespace: 'fm';
7
+ // 元素分隔符,用于连接 Block 和 Element,例如 fm-block__element
8
+ $element-separator: '__';
9
+ //修饰符分隔符,用于连接 Block/Element 和 Modifier,例如 fm-block--modifier
10
+ $modifier-separator :'--';
11
+ //状态前缀,用于标识组件的状态,例如 is-active
12
+ $state-prefix: 'is-';
13
+
14
+ /**
15
+ 功能:将传入的选择器转换为字符串形式,并去掉首尾的引号。
16
+ 用途:在后续函数中用于解析选择器内容。
17
+ */
18
+ @function selectorToString($selector) {
19
+ $selector: inspect($selector);
20
+ $selector: str-slice($selector, 2, -2);
21
+ @return $selector;
22
+ }
23
+
24
+ /*
25
+ 功能:检查选择器是否包含修饰符分隔符 --。
26
+ 用途:判断是否需要特殊处理修饰符规则
27
+ */
28
+ @function containsModifier($selector) {
29
+ $selector: selectorToString($selector);
30
+
31
+ @if str-index($selector, $modifier-separator) {
32
+ @return true;
33
+ }
34
+
35
+ @else {
36
+ @return false;
37
+ }
38
+ }
39
+
40
+ /*
41
+ 功能:检查选择器是否包含状态前缀 .is-。
42
+ 用途:判断是否需要处理状态相关的规则。
43
+ */
44
+ @function containWhenFlag($selector) {
45
+ $selector: selectorToString($selector);
46
+
47
+ @if str-index($selector, '.' + $state-prefix) {
48
+ @return true;
49
+ }
50
+
51
+ @else {
52
+ @return false;
53
+ }
54
+ }
55
+
56
+ /*
57
+ 功能:检查选择器是否包含伪类(如 :hover、:active 等)。
58
+ 用途:判断是否需要处理伪类规则。
59
+ */
60
+ @function containPseudoClass($selector) {
61
+ $selector: selectorToString($selector);
62
+
63
+ @if str-index($selector, ':') {
64
+ @return true;
65
+ }
66
+
67
+ @else {
68
+ @return false;
69
+ }
70
+ }
71
+
72
+ /*
73
+ 功能:综合判断选择器是否包含修饰符、状态或伪类。
74
+ 用途:决定是否需要特殊嵌套规则。
75
+ */
76
+ @function hitAllSpecialNestRule($selector) {
77
+
78
+ @return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector);
79
+ }
80
+
81
+ /*
82
+ 功能:生成 Block 的基础类名。
83
+ 用法:
84
+ 输入: @include b(button) { color: red; }
85
+ 输出:.fm-button { color: red; }
86
+ */
87
+ @mixin b($block) {
88
+
89
+ $B: #{$namespace + '-' + $block} !global;
90
+
91
+ .#{$B} {
92
+ @content;
93
+ }
94
+ }
95
+
96
+ /*
97
+ 功能:生成 Element 的类名。
98
+ 用法:
99
+ 输入:
100
+ @include b(button) {
101
+ @include e(text) {
102
+ font-size: 14px;
103
+ }
104
+ }
105
+ 输出:
106
+ .fm-button__text {
107
+ font-size: 14px;
108
+ }
109
+
110
+ */
111
+ @mixin e($element, $inheritParent: false) {
112
+ $E: $element !global;
113
+ $selector: &;
114
+ $currentSelector: "";
115
+ $insertSelector: "";
116
+
117
+ @each $unit in $element {
118
+ @if $inheritParent {
119
+ $currentSelector: #{$currentSelector + $selector + $element-separator + $unit + ","};
120
+ } @else {
121
+ $currentSelector: #{$currentSelector + "." + $B + $element-separator + $insertSelector + $unit + ","};
122
+ }
123
+ }
124
+
125
+ @if hitAllSpecialNestRule($selector) {
126
+ @at-root {
127
+ #{$selector} {
128
+ #{$currentSelector} {
129
+ @content;
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ @else {
136
+ @at-root {
137
+ #{$currentSelector} {
138
+ @content;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ /*
145
+ 功能:生成 Modifier 的类名。
146
+ 用法:
147
+ 输入:
148
+ @include b(button) {
149
+ @include m(disabled) {
150
+ opacity: 0.5;
151
+ }
152
+ }
153
+ 输出:
154
+ .fm-button--disabled {
155
+ opacity: 0.5;
156
+ }
157
+
158
+ */
159
+ @mixin m($modifier) {
160
+ $selector: &;
161
+ $currentSelector: "";
162
+
163
+ @each $unit in $modifier {
164
+ $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
165
+ }
166
+
167
+ @at-root {
168
+ #{$currentSelector} {
169
+ @content;
170
+ }
171
+ }
172
+ }
173
+
174
+ /*
175
+ 功能:生成根节点上的 Modifier 类名。
176
+ 用法:
177
+ 输入:
178
+ @include b(button) {
179
+ @include e(text) {
180
+ @include root-m(disabled) {
181
+ pointer-events: none;
182
+ }
183
+ }
184
+ }
185
+
186
+ 输出:
187
+ .fm-button--disabled {
188
+ pointer-events: none;
189
+ }
190
+ */
191
+ @mixin root-m($modifier) {
192
+ $selector: #{"." + $B};
193
+ $currentSelector: "";
194
+
195
+ @each $unit in $modifier {
196
+ $currentSelector: #{$currentSelector + & + $selector + $modifier-separator + $unit + ","};
197
+ }
198
+
199
+ @at-root {
200
+ #{$currentSelector} {
201
+ @content;
202
+ }
203
+ }
204
+ }