react-extjs 0.0.1

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +36 -0
  3. package/src/components/AbsoluteLayout.tsx +206 -0
  4. package/src/components/Accordion.tsx +266 -0
  5. package/src/components/AnchorLayout.tsx +131 -0
  6. package/src/components/BorderLayout.tsx +512 -0
  7. package/src/components/Box.tsx +289 -0
  8. package/src/components/Button.tsx +300 -0
  9. package/src/components/ButtonGroup.tsx +170 -0
  10. package/src/components/CalendarPicker.tsx +431 -0
  11. package/src/components/CardLayout.tsx +122 -0
  12. package/src/components/CenterLayout.tsx +95 -0
  13. package/src/components/CheckboxGroup.tsx +200 -0
  14. package/src/components/ColorMenu.tsx +195 -0
  15. package/src/components/ColorPalette.tsx +126 -0
  16. package/src/components/ColumnLayout.tsx +183 -0
  17. package/src/components/ColumnTree.tsx +383 -0
  18. package/src/components/CycleButton.tsx +302 -0
  19. package/src/components/DataView.tsx +244 -0
  20. package/src/components/DataViewMore.tsx +337 -0
  21. package/src/components/DataViewTransition.tsx +235 -0
  22. package/src/components/DateMenu.tsx +163 -0
  23. package/src/components/DatePicker.tsx +387 -0
  24. package/src/components/DisplayField.tsx +73 -0
  25. package/src/components/Editor.tsx +214 -0
  26. package/src/components/EditorGrid.tsx +361 -0
  27. package/src/components/FieldLabeler.tsx +81 -0
  28. package/src/components/FieldReplicator.tsx +135 -0
  29. package/src/components/FieldSet.tsx +149 -0
  30. package/src/components/FitLayout.tsx +76 -0
  31. package/src/components/Form.tsx +3484 -0
  32. package/src/components/Grid.tsx +2173 -0
  33. package/src/components/GridFilters.tsx +332 -0
  34. package/src/components/GroupTab.tsx +274 -0
  35. package/src/components/HtmlEditor.tsx +391 -0
  36. package/src/components/ImageOrganizer.tsx +984 -0
  37. package/src/components/ImageOrganizerService.ts +361 -0
  38. package/src/components/ListView.tsx +221 -0
  39. package/src/components/LoadMask.tsx +74 -0
  40. package/src/components/Menu.tsx +227 -0
  41. package/src/components/MessageBox.tsx +138 -0
  42. package/src/components/MultiSlider.tsx +369 -0
  43. package/src/components/Organizer.tsx +403 -0
  44. package/src/components/PagingToolbar.tsx +255 -0
  45. package/src/components/Panel.tsx +451 -0
  46. package/src/components/ProgressBar.tsx +127 -0
  47. package/src/components/PropertyGrid.tsx +142 -0
  48. package/src/components/QuickTip.tsx +174 -0
  49. package/src/components/Resizable.tsx +241 -0
  50. package/src/components/RowLayout.tsx +126 -0
  51. package/src/components/Slider.tsx +353 -0
  52. package/src/components/SliderField.tsx +206 -0
  53. package/src/components/SpinnerField.tsx +141 -0
  54. package/src/components/SplitBar.tsx +215 -0
  55. package/src/components/Spotlight.tsx +262 -0
  56. package/src/components/StatusBar.tsx +118 -0
  57. package/src/components/TabPanel.tsx +540 -0
  58. package/src/components/TableLayout.tsx +132 -0
  59. package/src/components/ThemeSwitcher.tsx +167 -0
  60. package/src/components/ToolTip.tsx +226 -0
  61. package/src/components/Toolbar.tsx +321 -0
  62. package/src/components/Tree.tsx +809 -0
  63. package/src/components/TreeGrid.tsx +418 -0
  64. package/src/components/Viewport.tsx +218 -0
  65. package/src/components/Window.tsx +441 -0
  66. package/src/components/Writer.tsx +303 -0
  67. package/src/components/XmlTreeLoader.tsx +233 -0
  68. package/src/components/dnd/DndContext.tsx +328 -0
  69. package/src/components/dnd/DragSource.tsx +202 -0
  70. package/src/components/dnd/DragZone.tsx +66 -0
  71. package/src/components/dnd/DropTarget.tsx +86 -0
  72. package/src/components/dnd/DropZone.tsx +166 -0
  73. package/src/components/dnd/PanelProxy.tsx +81 -0
  74. package/src/components/dnd/PanelResizer.tsx +143 -0
  75. package/src/components/dnd/Portal.tsx +254 -0
  76. package/src/components/dnd/Reorderer.tsx +73 -0
  77. package/src/components/dnd/ToolbarReorderer.tsx +226 -0
  78. package/src/main.tsx +2649 -0
  79. package/src/styles/ext-theme.css +889 -0
  80. package/src/util/theme.ts +95 -0
@@ -0,0 +1,889 @@
1
+ /* 保真契约:原样复用 Ext JS 3.2.1 样式(结构 ext-all.css + sprite 相对路径)。
2
+ 资源已复制到 public/extjs-assets/(见 npm run sync:extjs),从项目内引用,
3
+ 开发/生产构建均不再走 Vite 的 @fs 跨目录限制。url(../images/...) 相对
4
+ /extjs-assets/css/ext-all.css 解析到 /extjs-assets/images/...,像素保真不变。
5
+ 主题(xtheme-*.css)不再在此 @import,改为运行时由框架入口 / ThemeSwitcher 通过
6
+ <link id="extjs-dynamic-theme"> 注入默认主题(见 src/util/theme.ts ensureDefaultTheme),
7
+ 以支持「不用 ThemeSwitcher 也有默认主题」与运行时整篇换主题。注入的 link 用 head.prepend
8
+ 置于最前,使 xtheme 早于下方覆盖规则加载(覆盖规则只设 border-width/style,颜色由 xtheme 补)。 */
9
+ @import '/extjs-assets/css/ext-all.css';
10
+ /* TreeGrid(Ext.ux.tree.TreeGrid)专属样式:来自 Ext 发行 examples/ux/treegrid/treegrid.css,
11
+ 是 Ext 官方的、与 ext-all.css 配套的组件样式,复用契约不变(仅路径在 examples/ux 而非
12
+ resources/css,故单独复制并 import,sync:extjs 不会覆盖它)。 */
13
+ @import '/extjs-assets/css/treegrid.css';
14
+ /* StatusBar(Ext.ux.StatusBar)专属样式:来自 Ext 发行 examples/ux/statusbar/css/StatusBar.css,
15
+ 与 ext-all.css 配套的组件样式,复用契约不变(路径在 examples/ux 而非 resources/css,故单独复制并 import)。 */
16
+ @import '/extjs-assets/css/statusbar/statusbar.css';
17
+ /* SpinnerField(Ext.ux.form.SpinnerField)专属样式:来自 Ext 发行 examples/ux/css/Spinner.css,
18
+ 与 ext-all.css 配套的组件样式,复用契约不变(路径在 examples/ux 而非 resources/css,故单独复制并 import)。 */
19
+ @import '/extjs-assets/css/spinner/spinner.css';
20
+ /* Grid 表头列菜单箭头显隐(对齐 Ext GridView 运行时 JS:hovered 表头加 x-grid3-hd-over 显示
21
+ x-grid3-hd-btn;ext-all.css 仅定义 display:none 与 :over/:menu-open 显隐规则,此处用原生 CSS
22
+ :hover 等价复现 interactive 显隐,不引入任何新布局/视觉样式,复用 ext-all 既有意图)。 */
23
+ .x-grid3-hd:hover .x-grid3-hd-btn { display: block; }
24
+
25
+
26
+ /* Panel.minButtonWidth:footer 按钮最小宽度(对齐 Ext.minButtonWidth,默认 75px)。
27
+ 仅约束 footer 按钮区,不引入新布局/视觉,复用 Ext 既有意图。 */
28
+ .x-panel-btns button,
29
+ .x-panel-fbar button { min-width: var(--x-panel-minbtn, 75px); }
30
+
31
+ /* 表单字段盒模型用 border-box,使 width/height 直接对应 Ext 的外框尺寸。
32
+ 原因:content-box 下 width:260 实为 260(content) + 2(border) + 6(padding) = 268 外宽,
33
+ 导致输入框实际比设定宽 8px,且 FileUploadField(无边框 wrap)与其它文本字段差 8px、无法对齐。
34
+ border-box 下内联 height:22 即还原 22px 外高(原 18 content + 2 border + 2 padding = 22),
35
+ 与 .x-form-trigger 的 22px 一致,高度保真不变;宽度则严格等于配置值,符合 Ext 的 width=外框。 */
36
+ .x-form-text,
37
+ .x-form-file-text,
38
+ textarea.x-form-field,
39
+ .x-form-trigger {
40
+ box-sizing: border-box;
41
+ }
42
+ /* trigger 在 ext-all.css 为 content-box 的 height:21(21 + 1 border-bottom = 22 外高);
43
+ border-box 下改为 22 即可保持 22px 外高、内容区 21px,与输入框对齐。 */
44
+ .x-form-trigger {
45
+ width: 17px;
46
+ height: 22px;
47
+ }
48
+ /* 提权覆盖 Ext ext-all 的 .x-form-field-wrap .x-form-trigger{height:21px}(content-box 语义)。
49
+ 本表已对 .x-form-trigger 设 box-sizing:border-box,故 Ext 的 21 被当成总高 → 触发器比
50
+ 输入框(22)矮 1px;用 0,3,0 特异性强制 22px 总高,使所有带图标字段的图标与输入框等高对齐。 */
51
+ .x-form-field-wrap.x-form-field-trigger-wrap .x-form-trigger {
52
+ height: 22px;
53
+ }
54
+ /* 让触发器进入正常流,由 .x-form-field-trigger-wrap 的 flex 排布:
55
+ 输入框 flex:1 自动填空余,触发器 flex:0 0 auto 取 CSS 宽度(默认 17px;
56
+ access 主题经 .x-form-field-wrap .x-form-trigger 提权为 20px)。覆盖
57
+ ext-all.css 给触发器设的 position:absolute,否则会脱离 flex 流、重叠在左。
58
+ 注意:排除 .x-form-spinner-trigger —— SpinnerField 仍用绝对定位(见下方 .x-form-spinner-trigger
59
+ 规则及 SpinnerField.tsx 的 inputWidth=wrap-17 布局),不能强行 static,否则右图标错位。 */
60
+ .x-form-field-trigger-wrap > .x-form-trigger:not(.x-form-spinner-trigger) {
61
+ position: static;
62
+ }
63
+
64
+ /* 聚焦边框:对齐 Ext 蓝边 #7eadd9。xtheme-blue 已对 .x-form-focus 设蓝边(普通文本框生效);
65
+ 触发类字段(ComboBox/DateField/TimeField)聚焦时仅 wrap 加 x-trigger-wrap-focus,输入框本身
66
+ 未加 x-form-focus,这里补一条等价规则,使其输入框同样显示 Ext 蓝边。 */
67
+ .x-trigger-wrap-focus .x-form-text {
68
+ border-color: #7eadd9;
69
+ }
70
+
71
+ /* Spinner 触发器对齐 Ext 真值(.x-form-field-wrap .x-form-trigger 为 position:absolute;top:0)。
72
+ 原 React 实现把 img 渲染成静态/块级元素,靠 img 固有高度(21px+border 1px=22px)与输入框
73
+ 错位;改为绝对定位、与输入框顶部对齐,两者均为 22px,高度一致。 */
74
+ .x-form-spinner-trigger {
75
+ position: absolute;
76
+ top: 0;
77
+ }
78
+
79
+ /* 保真修正:SpinnerField 的上下箭头应来自 spinner.gif,由 spinner.css 的
80
+ .x-form-field-wrap .x-form-spinner-trigger(特异性 0,2,0)提供。但 Ext 主题表
81
+ xtheme-blue.css 的 .x-form-field-wrap .x-form-trigger(同为 0,2,0)加载更晚,
82
+ 把图标覆盖成默认 trigger.gif(普通下拉箭头),表现为 spinner 的上下箭头被替换/看不见。
83
+ 这里用复合类把特异性提到 0,3,0(父 div 同时带 x-form-field-wrap 与
84
+ x-form-field-trigger-wrap)锁定 spinner 图标。仅设 background-image/repeat,
85
+ 不碰 background-position,保留 spinner.css 的 overup/clickup 等位移态。 */
86
+ .x-form-field-wrap.x-form-field-trigger-wrap .x-form-spinner-trigger {
87
+ background-image: url('/extjs-assets/css/spinner/images/spinner.gif');
88
+ background-repeat: no-repeat;
89
+ }
90
+
91
+ /* 输入框与圆角/触发器对齐:显式 content 高度 18px(Ext 标准 .x-form-text 高度契约)。
92
+ 在缺少 ext-strict 上下文时,ext-all.css 的 .x-form-text{height:22px} 在 content-box 下
93
+ 会把输入框总高撑成 26px(22 content + 2 padding + 2 border),与 22px 触发器错位;
94
+ 此处用 content-box 下的 18px content + 2px padding + 2px border = 22px 还原,对齐触发器与
95
+ 其他 Form 字段。 */
96
+ .x-form-num-field {
97
+ height: 22px;
98
+ }
99
+ /* 提权覆盖 Ext ext-all 的 .ext-strict .x-form-text{height:18px}(content-box 语义)。
100
+ 本表对 .x-form-text 设 box-sizing:border-box,故 18 被当成总高 → SpinnerField 输入框
101
+ 比触发器(21/22)矮 3px;用 0,4,0 特异性强制 22px 总高,与图标对齐。 */
102
+ .x-form-field-wrap.x-form-field-trigger-wrap .x-form-text.x-form-num-field {
103
+ height: 22px;
104
+ }
105
+
106
+ /* 触发器字段:输入框与绝对定位的触发器(top:0)顶端对齐。
107
+ 原生 .x-form-text 为 vertical-align:middle;在缺少 Ext 表单布局受控 line-height 的上下文
108
+ (如文档站独立演示)下,input 的 line box 高于字段高度,middle 对齐会把输入框整体下推约
109
+ 0.5px,使触发器相对输入框“上移”错位;改为 top 对齐后两者顶端重合,与表单内 FormField
110
+ 受控上下文表现一致(触发器在顶、输入框在顶)。 */
111
+ .x-form-field-trigger-wrap .x-form-text {
112
+ vertical-align: top;
113
+ }
114
+
115
+ /* Editor 浮层输入框:同 SpinnerField 思路,content-box 下显式 18px content 还原 22px 总高,
116
+ 与浮层 div 的 height=22 对齐(避免 unscoped .x-form-text{height:22px} 撑成 26px 溢出 4px)。 */
117
+ .x-editor .x-form-text {
118
+ height: 22px;
119
+ }
120
+
121
+ /* DatePicker 宽度适配内容(对齐 Ext 浮层/菜单形态)。
122
+ 默认 block div 在宽容器中被撑满,导致 1px 右边框远离日历内容(table 仅 ~175px),
123
+ 视觉上"右边没有边框"。改为 inline-block 后宽度由内容决定,四边 border 紧贴日历。 */
124
+ .x-date-picker {
125
+ display: inline-block;
126
+ }
127
+
128
+ /* Editor 演示的"表单内"场景:label 与编辑内容等高对齐。
129
+ FormField 的 label 带 padding:3px(上+下),继承到的行高若偏大,label 盒会被撑到 23px、
130
+ 而编辑内容固定 22px,两者都顶对齐时 label 中心比内容低 0.5px → 内容视觉"上偏"。
131
+ 显式令 label 行高 = 22 - 3 - 3 = 16px,使 label 盒高恰为 22px(= field 高),
132
+ 与 Ext 表单行"label/field 等高"一致。仅作用域到 Editor 演示,不污染其它表单组件。 */
133
+ .x-editor-form-item .x-form-item-label {
134
+ line-height: 16px;
135
+ }
136
+
137
+ /* EditorGrid 单元格编辑器浮层(对齐 Ext.EditorGrid 的 x-grid-editor 容器)。
138
+ 仅作为绝对定位容器承载内联字段(input 用 border-box 100% 填满单元格),
139
+ 不引入新视觉;overflow:visible 保证输入框若有阴影/边框不被裁剪。 */
140
+ .x-grid-editor {
141
+ overflow: visible;
142
+ }
143
+
144
+ /* TreeGrid 选中行高亮(对齐 Ext.ux.tree.TreeGrid 的 x-tree-node-selected):
145
+ 官方 treegrid.css 只定义结构与列边框,未给选中态上色;此处沿用 xtheme-blue
146
+ 树选中蓝 #d9e8fb(与 Tree 组件 x-tree-selected 一致),让选中行整行着色。 */
147
+ .x-tree-node-selected {
148
+ background-color: #d9e8fb;
149
+ }
150
+
151
+ /* TextArea:对齐 Ext textarea。真实 Ext 经 `textarea.x-form-field`(ext-all.css:871 内边距 2px 3px、
152
+ :913 resize:none;xtheme-blue.css:205-209 边框 #b5b8c8 + 背景 #fff + text-bg.gif sprite)接管渲染。
153
+ 因 `textarea.x-form-field` 优先级(0,1,1) 高于本选择器(0,1,0),边框/背景/sprite/内边距已由原生规则
154
+ 生效;此处仅显式补齐两项原生规则未覆盖的兜底,并纠正一处偏离:原 `resize:vertical` 优先级反转覆盖了
155
+ 原生 `resize:none`,改为 none 以完全对齐 Ext(textarea 不可拖拽缩放)。 */
156
+ .x-form-textarea {
157
+ padding: 2px 3px;
158
+ border: 1px solid #b5b8c8;
159
+ background-color: #fff;
160
+ resize: none;
161
+ }
162
+
163
+ /* CompositeField:对齐 Ext.form.CompositeField 的单行多字段容器。
164
+ 子字段横向排列,分隔符居中;不引入新视觉,仅做 inline-flex 行布局。 */
165
+ .x-form-composite {
166
+ display: inline-flex;
167
+ align-items: center;
168
+ flex-wrap: wrap;
169
+ gap: 4px;
170
+ }
171
+ .x-composite-separator {
172
+ color: #333;
173
+ user-select: none;
174
+ }
175
+
176
+ /* FileUploadField(Ext.ux.form.FileUploadField):对齐 examples/ux/fileuploadfield/css/fileuploadfield.css。
177
+ 容器相对定位;文本框绝对左、按钮绝对右、透明 file input 覆盖按钮触发系统对话框。
178
+ 不引入新视觉,仅做定位与透明覆盖(z-index 层级:text=3 > file=2 > btn=1)。 */
179
+ .x-form-file-wrap {
180
+ position: relative;
181
+ display: inline-block;
182
+ height: 22px;
183
+ vertical-align: middle;
184
+ }
185
+ .x-form-file-wrap .x-form-file-text {
186
+ position: absolute;
187
+ left: 0;
188
+ top: 0;
189
+ z-index: 3;
190
+ color: #777;
191
+ height: 22px;
192
+ }
193
+ .x-form-file-wrap .x-form-file-btn {
194
+ position: absolute;
195
+ right: 0;
196
+ top: 0;
197
+ z-index: 1;
198
+ }
199
+ .x-form-file-wrap .x-form-file {
200
+ position: absolute;
201
+ inset: 0;
202
+ width: 100%;
203
+ height: 100%;
204
+ opacity: 0;
205
+ z-index: 2;
206
+ cursor: pointer;
207
+ font-size: 0;
208
+ color: transparent;
209
+ }
210
+ .x-form-file-wrap.x-item-disabled .x-form-file {
211
+ cursor: default;
212
+ }
213
+
214
+ /* MultiSelect —— 对齐 examples/ux/css/MultiSelect.css */
215
+ .x-multiselect {
216
+ padding: 0;
217
+ }
218
+ .x-multiselect-tbar {
219
+ display: flex;
220
+ gap: 2px;
221
+ padding: 2px;
222
+ border-bottom: 1px solid #d0d0d0;
223
+ background: #f3f7fb;
224
+ }
225
+ .ux-mselect {
226
+ overflow: auto;
227
+ background: #fff;
228
+ position: relative;
229
+ zoom: 1;
230
+ }
231
+ .ux-mselect-item {
232
+ font: normal 12px tahoma, arial, helvetica, sans-serif;
233
+ padding: 2px;
234
+ border: 1px solid #fff;
235
+ white-space: nowrap;
236
+ cursor: pointer;
237
+ }
238
+ .ux-mselect-selected {
239
+ border: 1px dotted #a3bae9 !important;
240
+ background: #dfe8f6;
241
+ cursor: pointer;
242
+ }
243
+ .x-item-disabled .ux-mselect-item {
244
+ cursor: default;
245
+ color: #999;
246
+ }
247
+ .x-item-disabled .ux-mselect-item:hover {
248
+ background: transparent;
249
+ }
250
+
251
+ /* ItemSelector —— 双栏项选择器(对齐 Ext table 三列布局) */
252
+ .x-itemselector {
253
+ display: inline-flex;
254
+ align-items: center;
255
+ gap: 6px;
256
+ }
257
+ .x-itemselector .x-multiselect {
258
+ margin: 0;
259
+ }
260
+ .x-itemselector-actions {
261
+ display: flex;
262
+ flex-direction: column;
263
+ gap: 4px;
264
+ align-items: center;
265
+ justify-content: center;
266
+ }
267
+ .x-itemselector-actions .x-btn {
268
+ min-width: 28px;
269
+ }
270
+
271
+ /* CenterLayout —— UX 绝对水平居中子面板(对齐 Ext examples/ux/css/CenterLayout.css) */
272
+ .ux-layout-center-item {
273
+ margin:0 auto;
274
+ text-align:left;
275
+ }
276
+ .ux-layout-center .x-panel-body,
277
+ body.ux-layout-center {
278
+ text-align:center;
279
+ }
280
+
281
+ /* ColumnTree(Ext.ux.tree.ColumnTree + ColumnNodeUI)专属样式:来源 Ext 发行
282
+ examples/ux/css/ColumnNodeUI.css,与 ext-all.css 配套的组件样式,复用契约不变
283
+ (表头背景图改绝对路径 /extjs-assets/images/default/grid/grid3-hrow.gif)。 */
284
+ .x-column-tree .x-panel-header {
285
+ padding: 3px 0px 0px 0px;
286
+ border-bottom-width: 0px;
287
+ }
288
+ .x-column-tree .x-panel-header .x-panel-header-text {
289
+ margin-left: 3px;
290
+ }
291
+ .x-column-tree .x-tree-node {
292
+ zoom:1;
293
+ }
294
+ .x-column-tree .x-tree-node-el {
295
+ zoom:1;
296
+ }
297
+ .x-column-tree .x-tree-selected {
298
+ background: #d9e8fb;
299
+ }
300
+ .x-column-tree .x-tree-node a {
301
+ line-height:18px;
302
+ vertical-align:middle;
303
+ }
304
+ .x-column-tree .x-tree-node a span {
305
+ }
306
+ .x-column-tree .x-tree-node .x-tree-selected a span {
307
+ background:transparent;
308
+ color:#000;
309
+ }
310
+ .x-tree-col {
311
+ float:left;
312
+ overflow:hidden;
313
+ padding:0 1px;
314
+ zoom:1;
315
+ }
316
+ .x-tree-col-text, .x-tree-hd-text {
317
+ color:#000;
318
+ overflow:hidden;
319
+ -o-text-overflow: ellipsis;
320
+ text-overflow: ellipsis;
321
+ padding:3px 3px 3px 5px;
322
+ white-space: nowrap;
323
+ font:normal 11px arial, tahoma, helvetica, sans-serif;
324
+ }
325
+ .x-tree-headers {
326
+ margin-top: 3px;
327
+ background: #f9f9f9 url(/extjs-assets/images/default/grid/grid3-hrow.gif) repeat-x 0 bottom;
328
+ cursor:default;
329
+ zoom:1;
330
+ }
331
+ .x-tree-hd {
332
+ float:left;
333
+ overflow:hidden;
334
+ border-left:1px solid #eee;
335
+ border-right:1px solid #d0d0d0;
336
+ }
337
+
338
+ /* XmlTreeLoader 读书清单示例图标(自包含 SVG data URI,无外部资源):覆盖默认 folder/leaf 背景 */
339
+ .x-tree-node-icon.author-m {
340
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48Y2lyY2xlIGN4PSI4IiBjeT0iNC41IiByPSIzIiBmaWxsPSIjM2E2ZWE1Ii8+PHBhdGggZD0iTTIgMTVjMC0zLjMxIDIuNjktNiA2LTZzNiAyLjY5IDYgNnoiIGZpbGw9IiMzYTZlYTUiLz48L3N2Zz4=");
341
+ background-position: 0 0;
342
+ background-repeat: no-repeat;
343
+ }
344
+ .x-tree-node-icon.author-f {
345
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48Y2lyY2xlIGN4PSI4IiBjeT0iNC41IiByPSIzIiBmaWxsPSIjYTU1Yjg1Ii8+PHBhdGggZD0iTTIgMTVjMC0zLjMxIDIuNjktNiA2LTZzNiAyLjY5IDYgNnoiIGZpbGw9IiNhNTViODUiLz48L3N2Zz4=");
346
+ background-position: 0 0;
347
+ background-repeat: no-repeat;
348
+ }
349
+ .x-tree-node-icon.book {
350
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48cGF0aCBkPSJNOCAzQzYgMiAzIDIgMiAyLjV2MTFDMyAxMyA2IDEzIDggMTRjMi0xIDUtMSA2LS41di0xMUMxMSAyIDggMiA4IDN6IiBmaWxsPSIjNGE3YTM1Ii8+PHBhdGggZD0iTTggM3YxMSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjAuOCIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==");
351
+ background-position: 0 0;
352
+ background-repeat: no-repeat;
353
+ }
354
+
355
+ /* DataView(base / DataViewMore):原版 ext-all.css 仅定义 .x-view-selector(框选浮层),
356
+ .x-view-item / .x-view-over / .x-view-selected 均无 base 规则——选中/悬停本就不由主题
357
+ 着色(Ext 3.2.1 原生表现)。此处仅保留 cursor 提示,不新增主题色。 */
358
+ .x-data-view .x-view-item {
359
+ cursor: pointer;
360
+ }
361
+
362
+ /* Organizer(examples/organizer/organizer.css 逐字复用):原版示例自带的样式,仅把
363
+ url(../tree/xxx.gif) 改为本项目资源绝对路径 /extjs-assets/images/tree/,不增删任何
364
+ 视觉/布局,完全复用原版 CSS(对齐用户“在原来的 css 找合适的 css 用”的约定)。
365
+ #images 是图片面板 body 的 id(原版 My Images 面板 body);.thumb-wrap/.thumb/.thumb-img
366
+ /span 为原版 DataView 缩略图 DOM;选中态用原版 .x-view-selected(与 DataView 原生
367
+ selectedClass 一致),原版 .x-view-selector 负责橡皮筋框选层。 */
368
+ #images {
369
+ /* 背景不在此写死:原生 center-bg.gif 资源缺失,且白底应由主题化 .x-panel-body 提供,
370
+ 避免写死静态资源破坏主题切换。仅保留原版 organizer.css 的中性字体。 */
371
+ font: 11px Arial, Helvetica, sans-serif;
372
+ }
373
+ #images .thumb {
374
+ background: #dddddd;
375
+ padding: 3px;
376
+ }
377
+ #images .thumb img {
378
+ border: 1px solid white;
379
+ height: 60px;
380
+ width: 80px;
381
+ }
382
+ #images .thumb-wrap {
383
+ float: left;
384
+ margin: 4px;
385
+ margin-right: 0;
386
+ padding: 5px;
387
+ }
388
+ #images .thumb-wrap span {
389
+ display: block;
390
+ overflow: hidden;
391
+ text-align: center;
392
+ }
393
+ #images .x-view-selected .thumb {
394
+ background: #8db2e3;
395
+ }
396
+ .album-node .x-tree-node-icon,
397
+ .album-btn {
398
+ background-image: url(/extjs-assets/images/tree/album.gif) !important;
399
+ }
400
+ .image-node .x-tree-node-icon {
401
+ margin-bottom: 1px;
402
+ height: 15px;
403
+ }
404
+ /* 内联标签编辑器触发区:虚线下划线 + 手型光标(对齐 Ext labelSelector 交互暗示) */
405
+ .x-editable {
406
+ cursor: text;
407
+ border-bottom: 1px dashed #3164a1;
408
+ padding-bottom: 1px;
409
+ }
410
+ /* LabelEditor 浮层输入框对齐 Ext.Editor(x-small-editor + x-editor 组合)。
411
+ ext-all.css 的 .x-editor{visibility:hidden} 是 Ext 在 JS 定位后再显隐的约定;本组件静态渲染,
412
+ 故在编辑态显式置为可见(等价 Ext Editor 定位后 show())。 */
413
+ .x-editable .x-editor.x-small-editor {
414
+ visibility: visible;
415
+ font-family: tahoma, arial, sans-serif;
416
+ }
417
+
418
+ /* ============ 拖放(Drag & Drop)—— x-dd 系列(保真 Ext.dd.StatusProxy / DropTarget) ============
419
+ 源图标用内联 SVG 替代 ext-all.css 的 gif(drop-yes/drop-no/drop-add),对齐项目「SVG 替代图片」范式 */
420
+ .x-dd-drag-proxy {
421
+ position: absolute;
422
+ left: 0;
423
+ top: 0;
424
+ z-index: 15000;
425
+ pointer-events: none;
426
+ }
427
+ .x-dd-drag-ghost {
428
+ opacity: .85;
429
+ border: 1px solid;
430
+ background-color: #fff;
431
+ color: #000;
432
+ font: normal 11px arial, helvetica, sans-serif;
433
+ padding: 3px;
434
+ padding-left: 20px;
435
+ white-space: nowrap;
436
+ }
437
+ .x-dd-drop-icon {
438
+ position: absolute;
439
+ top: 3px;
440
+ left: 3px;
441
+ display: block;
442
+ width: 16px;
443
+ height: 16px;
444
+ background-position: center;
445
+ background-repeat: no-repeat;
446
+ z-index: 1;
447
+ }
448
+ .x-dd-drop-ok .x-dd-drop-icon {
449
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Ccircle cx='8' cy='8' r='7' fill='%23c6efce' stroke='%2361b76a'/%3E%3Cpath d='M4 8.5l2.5 2.5L12 5' fill='none' stroke='%23378a40' stroke-width='1.6'/%3E%3C/svg%3E");
450
+ }
451
+ .x-dd-drop-nodrop .x-dd-drop-icon {
452
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Ccircle cx='8' cy='8' r='7' fill='%23f8d7da' stroke='%23d9534f'/%3E%3Cpath d='M5 5l6 6M11 5l-6 6' fill='none' stroke='%23c12e2f' stroke-width='1.6'/%3E%3C/svg%3E");
453
+ }
454
+ .x-dd-drop-ok-add .x-dd-drop-icon {
455
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Ccircle cx='8' cy='8' r='7' fill='%23d6e9f8' stroke='%233399bb'/%3E%3Cpath d='M8 4v8M4 8h8' fill='none' stroke='%233399bb' stroke-width='1.6'/%3E%3C/svg%3E");
456
+ }
457
+ .x-dd-drag-repair .x-dd-drag-ghost {
458
+ opacity: .4;
459
+ border: 0 none;
460
+ padding: 0;
461
+ background-color: transparent;
462
+ }
463
+ .x-dd-drag-repair .x-dd-drop-icon {
464
+ visibility: hidden;
465
+ }
466
+ /* repair 回弹动画(等价 Ext StatusProxy.repair 的 .5s shift) */
467
+ .x-dd-drag-proxy.x-dd-drag-repair {
468
+ transition: left .5s ease-out, top .5s ease-out;
469
+ }
470
+ /* DropTarget over 高亮示例类:原版 Ext.dd 的 overClass(默认 'x-dd-drop-over')本身不附带任何
471
+ 视觉规则(原生 ext-all.css / structure/dd.css / theme-*/dd.css 均无此选择器,视觉交由应用层
472
+ 提供)。故此处不新增主题色、不新增中性色,保持原版「无强制高亮」表现——组件只负责在交互态
473
+ 给 el 加 overClass,是否渲染、如何渲染由原生 dd.css 或应用决定,本主题层不干预。 */
474
+
475
+ /* 面板:对齐 ExtJS 原生边框模型 —— .x-panel 元素本身无边框,
476
+ 可见边框由 .x-panel-header / .x-panel-body 承担,使 x-panel-noborder 重新生效。
477
+ frame 面板(.x-panel-mc .x-panel-body / .x-panel-tl .x-panel-header)由 ext-all.css
478
+ 原生规则把 CSS 边框置 0,改由 9 宫格 sprite 绘制,不在此叠加。 */
479
+ .x-panel {
480
+ border: 0;
481
+ background: #fff;
482
+ font: 12px tahoma, arial, sans-serif;
483
+ }
484
+ .x-panel-header {
485
+ /* 完全对齐真实 Ext 3.2.1 的 non-framed .x-panel-header(我们的扁平 header 正好对应此模式;
486
+ 9 宫格 .x-panel-tl/.tc/... 仅 frame:true 才有)。逐项映射:
487
+ ext-all.css:3685-3691 → overflow:hidden; padding:5px 3px 4px 5px;
488
+ border:1px solid; line-height:15px; background:transparent repeat-x 0 -1px
489
+ xtheme-blue.css:1324-1331 → color:#15428b; font-weight:bold; font-size:11px;
490
+ font-family:tahoma,arial,verdana,sans-serif; border-color:#99bbe8;
491
+ background-image:url(../images/default/panel/white-top-bottom.gif)
492
+ 背景完全交给原生(不覆盖 background),让 white-top-bottom.gif sprite 直接作用;此处仅显式
493
+ 锁定 Ext 在更外层写死、且本项目需对齐的项。header 自然高度 = 5(pad-t)+15(line)+4(pad-b)
494
+ +2(border) = 26px,与本项目的 HEADER_H=26 一致。不新增任何 CSS/图片资源。 */
495
+ overflow: hidden;
496
+ padding: 5px 3px 4px 5px;
497
+ /* 仅结构(width/style),border-color 完全交给 xtheme-*.css 的 .x-panel-header{border-color}
498
+ (blue #99bbe8 / gray #d0d0d0)覆盖 —— 对齐真实 ext-all.css:3689 .x-panel-header{border:1px solid},
499
+ 颜色不写死在默认主题里,切换主题时整圈边框自动跟随。
500
+ 注意:此处必须用 longhand 的 border-width/border-style,绝不能用 `border:1px solid` 简写——
501
+ 简写会把 border-color 重置为 currentColor,覆盖掉 @import 进来的 xtheme 颜色,导致边框变黑。 */
502
+ border-width: 1px;
503
+ border-style: solid;
504
+ /* 用 longhand 而非 font 简写:简写会把 line-height 重置为 normal(且 Vite 压缩会删掉被覆盖的
505
+ line-height:15px 死声明),导致 header 行高变 normal。Ext 原生也是 ext-all 设 line-height:15px、
506
+ xtheme 用 font longhand(不重置行高),此处同构对齐。 */
507
+ line-height: 15px;
508
+ font-weight: bold;
509
+ font-size: 11px;
510
+ font-family: tahoma, arial, verdana, sans-serif;
511
+ color: #15428b;
512
+ }
513
+ /* 手风琴子面板 header 沿用真实 Ext 的 .x-accordion-hd 文字色(#222,非普通面板头的蓝 #15428b)。
514
+ .x-accordion-hd 与 .x-panel-header 同为单类选择器、特异性相等,此处置于 .x-panel-header 之后,
515
+ 使其对“带 x-accordion-hd 的 header”优先生效,对齐真实 Ext 手风琴灰标题。 */
516
+ .x-accordion-hd {
517
+ color: #222;
518
+ }
519
+ .x-panel-header-text { user-select: none; }
520
+ .x-panel-header-draggable { cursor: move; }
521
+ /* .x-panel-body 的 border/overflow/position/color 完全交给原生 ext-all.css 定义;此处仅补一项:
522
+ - box-sizing 显式声明为 content-box(对齐原生默认:ext-all.css 的 .x-panel-body 未声明
523
+ box-sizing,故遵循初始 content-box 模型)。文档站 docs/src/docs.css 有 `*{box-sizing:border-box}`
524
+ 全局重置,会把它变成 border-box,导致设固定 height 的 body 内容区被 border 挤占 2px。此处显式
525
+ 声明 content-box 抵消该重置,使所有 .x-panel-body(含链B手写面板)统一对齐真实 Ext。
526
+ 颜色不在此覆盖:真实 Ext 的 .x-panel-body 不声明 color,正文继承浏览器默认黑字(xtheme-blue.css
527
+ 也未给 body/panel 设色),故沿用默认即可,不要自加 #222,否则与真实 Ext 出现文字色偏差。 */
528
+ .x-panel-body {
529
+ /* 对齐真实 ext-all.css:3694-3699:non-framed 面板 body 自身带 1px 边框(border-top:0,
530
+ 顶部边框由上方相邻 .x-panel-header 提供,避免 header 底/body 顶双线叠加);
531
+ 颜色完全交给 xtheme-*.css 的 .x-panel-body{border-color}(blue #99bbe8 / gray #d0d0d0)
532
+ 覆盖,故边框随主题切换自动跟随、默认主题不写死颜色。结构与真实 Ext 完全一致,
533
+ region 面板(.x-border-panel)的整圈框即由 .x-panel-header + .x-panel-body 拼接。
534
+ box-sizing:content-box 抵消文档站 docs/src/docs.css 的全局 border-box 重置,
535
+ 使设固定 width 的 body 内容区不被 1px 边框挤占(bodyW = box.w - 2 已预留两侧边框)。
536
+ 边框颜色(border-color)由 @import 的 xtheme-*.css 提供,此处仅给结构与顶边留空。 */
537
+ border-width: 0 1px 1px;
538
+ border-style: none solid solid;
539
+ box-sizing: content-box;
540
+ }
541
+ /* 无 header 时 body 顶边需补回(对齐 ext-all.css:3713 .x-panel-body-noheader{border-top:1px solid});
542
+ 同时本规则亦作用于布局根 .x-border-layout-ct(它兼带 x-panel-body-noheader),提供整篇布局的
543
+ 最外层边框 —— 颜色同样由 xtheme 主题化,原版即如此(容器边框 = 其 .x-panel-body 边框)。
544
+ 同样用 longhand,避免简写把 border-top-color 重置为 currentColor。 */
545
+ .x-panel-body-noheader {
546
+ border-top-width: 1px;
547
+ border-top-style: solid;
548
+ }
549
+ /* 无 header 时 body 顶边由原生 .x-panel-body-noheader 规则(ext-all.css)提供,此处不重复。 */
550
+
551
+ /* StatusProxy 内的面板形 ghost */
552
+ .x-dd-panel-proxy {
553
+ opacity: .9;
554
+ pointer-events: none;
555
+ }
556
+ .x-dd-panel-proxy .x-panel-body { opacity: .85; }
557
+
558
+ /* 门户 Portal / PortalColumn / Portlet(等价 Ext.ux.Portal / PortalColumn / Portlet) */
559
+ .x-portal {
560
+ padding: 4px;
561
+ }
562
+ /* 注意:此处必须为普通块级容器,让内部的 ColumnLayout 根(.x-column-layout-ct)按父宽撑满;
563
+ 若误设为 display:flex,唯一子项 .x-column-layout-ct 会被按「内容宽」收缩(内容初始为 0)
564
+ 导致整列布局塌陷为 0 宽。列间距由 ColumnLayout 的 margins(Portal 传 '0 14 0 0')提供。 */
565
+ .x-portal-canvas {
566
+ position: relative;
567
+ min-height: 240px;
568
+ }
569
+ /* Portal 列:原生 Ext.ux.Portal 里 .x-portal-column 仅作布局槽位(float/overflow),无背景无边框,
570
+ 主题外观完全由内部 .x-portlet(真实 .x-panel,经 xtheme 主题化、随主题切换)提供。故此处只保留
571
+ 布局属性,不写任何颜色(不硬编码中性、也不硬编码主题蓝),对齐原生「列透明、Portlet 承载主题」
572
+ 的结构,满足「零新增 CSS / 零硬编码色」铁律。拖拽悬停高亮(.x-portal-column-active)与占位符
573
+ (.x-portal-placeholder)的视觉由组件在 JS 中按交互态内联计算,不在此建 CSS 规则。 */
574
+ /* 列仅作布局槽位:宽度由 ColumnLayout 内联 width 决定(float:left,列间距由 margins 提供),
575
+ 内部 Portlet 靠 ux/Portal.css 的 margin-bottom 堆叠;box-sizing 让列宽计算包含内边距,
576
+ 避免 float 列额外 padding 撑破容器。min-height 保证空列仍可放置。不写任何主题色(零硬编码)。 */
577
+ .x-portal-column {
578
+ box-sizing: border-box;
579
+ min-height: 200px;
580
+ }
581
+ /* .x-portal-column-active / .x-portal-placeholder / .x-portal-placeholder-text 的视觉由组件内联
582
+ 提供(交互态驱动),此处不定义颜色规则。 */
583
+ /* Portlet = Ext.Panel + draggable:外观完全由原生 .x-panel / .x-panel-header / .x-panel-body
584
+ 承载(边框颜色走 xtheme,切换 Gray 等主题时整圈框自动跟随)。.x-portlet 仅作原生
585
+ ux/Portal.css 的 margin-bottom 堆叠标记 + 拖拽手势(cursor:move),不叠加任何硬编码边框 /
586
+ 圆角 / 阴影——否则会与 header/body 主题边框形成双层框且无法随主题切换,违反「零硬编码主题色、
587
+ 原生优先」铁律。
588
+ 注:.x-portlet-header / .x-portlet-header-text / .x-portlet-body / .x-portlet-ghost 均为死规则
589
+ (组件实际渲染 .x-panel-header / .x-panel-header-text / .x-panel-body,无任何元素使用这些
590
+ 私有类),已删除,避免其中硬编码的 #cfd8e6 蓝边框 / #15428b 蓝字残留为不随主题切换的死色。 */
591
+ .x-portlet {
592
+ cursor: move;
593
+ }
594
+
595
+ /* TabScrollerMenu 插件按钮(.x-tab-tabmenu-right):核心 xtheme-blue / ext-all 均未提供该 class 样式,
596
+ 属原始 ExtJS 亦缺失的真实缺口;仅补最小可用样式,布局摆位由组件内联样式还原原始插件行为。 */
597
+ .x-tab-tabmenu-right {
598
+ position: absolute;
599
+ top: 0;
600
+ right: 0;
601
+ width: 18px;
602
+ cursor: pointer;
603
+ z-index: 11;
604
+ text-align: center;
605
+ line-height: 22px;
606
+ color: #15428b;
607
+ font-size: 11px;
608
+ background-color: #deecfd; /* 对齐 header 背景,避免透明叠在标签上 */
609
+ /* 高度由组件内联 height:arrowHeight 控制(对齐 scroller 箭头),此处不设 height 回退,避免首帧回退到 100% 铺满 */
610
+ }
611
+ .x-tab-tabmenu-right::before { content: '\25BE'; } /* ▾ 无真实素材,纯文案占位 */
612
+ .x-tab-tabmenu-over { background-color: #eaf2fb; }
613
+
614
+ /* GroupTabs 面板基础外观(对齐 examples/ux/css/GroupTab.css:7-10)
615
+ 真实 Ext 的 .x-grouptabs-panel 为 15px 蓝(#4E78B1)外框 + 同色背景,是 GroupTabs 的招牌外观。
616
+ 因文档站全局 `*{box-sizing:border-box}` 重置,此处显式声明 content-box,使 15px 边框不挤占
617
+ 内容区(等价 Ext 原生 content-box:height:300 时内容区保持 300px,而非被边框吃掉 30px)。 */
618
+ .x-grouptabs-panel {
619
+ box-sizing: content-box;
620
+ background-color: #4E78B1;
621
+ border: solid 15px #4E78B1;
622
+ }
623
+ .x-grouptabs-panel.x-tab-panel-right {
624
+ flex-direction: row-reverse;
625
+ }
626
+ .x-grouptabs-panel-header {
627
+ border: 0;
628
+ background: transparent;
629
+ flex: 0 0 auto;
630
+ height: 100%;
631
+ overflow: hidden;
632
+ }
633
+ .x-grouptabs-bwrap {
634
+ flex: 1 1 auto;
635
+ min-width: 0;
636
+ height: 100%;
637
+ position: relative;
638
+ }
639
+ /* 左侧分组条容器:高度撑满面板,内容超出时自身出现滚动条(等价 Ext 面板固定高、内容过多时左侧滚动,不浮出容器) */
640
+ .x-grouptabs-panel .x-tab-strip-wrap {
641
+ height: 100%;
642
+ overflow-x: hidden;
643
+ overflow-y: auto;
644
+ }
645
+
646
+ .x-grouptabs-strip {
647
+ width: auto;
648
+ display: block;
649
+ list-style: none;
650
+ margin: 0;
651
+ padding: 0;
652
+ }
653
+ .x-grouptabs-strip li {
654
+ float: none;
655
+ margin: 0;
656
+ position: relative;
657
+ clear: both;
658
+ padding: 6px 0 2px 6px;
659
+ }
660
+ .x-grouptabs-strip a.x-grouptabs-text {
661
+ display: block;
662
+ overflow: hidden;
663
+ white-space: nowrap;
664
+ color: #DFE8F6;
665
+ font-family: tahoma, arial, sans-serif;
666
+ font-size: 13px;
667
+ line-height: 18px;
668
+ font-weight: bold;
669
+ text-decoration: none;
670
+ cursor: pointer;
671
+ padding-left: 18px;
672
+ }
673
+ .x-tab-panel-right .x-grouptabs-strip a.x-grouptabs-text {
674
+ padding-left: 0;
675
+ padding-right: 18px;
676
+ text-align: right;
677
+ }
678
+ .x-grouptabs-strip a.x-grouptabs-text span {
679
+ vertical-align: middle;
680
+ }
681
+ .x-grouptabs-panel .x-grouptabs-strip-active > a.x-grouptabs-text {
682
+ color: #395B8E;
683
+ }
684
+ .x-grouptabs-sub {
685
+ display: none;
686
+ margin: 4px 0 2px 4px;
687
+ list-style: none;
688
+ padding: 0;
689
+ }
690
+ .x-grouptabs-sub a.x-grouptabs-text {
691
+ font-weight: normal;
692
+ font-size: 12px;
693
+ padding-left: 14px;
694
+ }
695
+ .x-grouptabs-expanded .x-grouptabs-sub {
696
+ display: block;
697
+ }
698
+
699
+ /* expand 开关:CSS +/- 复刻原 elbow-plus/elbow-minus gif(原文件用 gif,无背景色;此处 CSS 替代,蓝调与主题一致) */
700
+ .x-grouptabs-expand {
701
+ position: absolute;
702
+ left: 7px;
703
+ top: 6px;
704
+ width: 16px;
705
+ height: 16px;
706
+ cursor: pointer;
707
+ z-index: 2;
708
+ }
709
+ .x-grouptabs-expand::before {
710
+ content: '+';
711
+ position: absolute;
712
+ inset: 0;
713
+ display: flex;
714
+ align-items: center;
715
+ justify-content: center;
716
+ font-weight: bold;
717
+ font-size: 14px;
718
+ line-height: 1;
719
+ color: #DFE8F6;
720
+ background: #3a5d8c;
721
+ border: 1px solid #6f93c4;
722
+ border-radius: 2px;
723
+ box-sizing: border-box;
724
+ }
725
+ .x-grouptabs-expanded .x-grouptabs-expand::before {
726
+ content: '\2212'; /* − */
727
+ }
728
+ .x-grouptabs-strip-active > .x-grouptabs-expand::before {
729
+ color: #395B8E;
730
+ background: #c9d8ee;
731
+ border-color: #9bb3d0;
732
+ }
733
+
734
+ /* 图标占位(demo 中通过 x-icon-* 自定义着色,无图片依赖) */
735
+ .x-grouptabs-icon {
736
+ display: inline-block;
737
+ width: 12px;
738
+ height: 12px;
739
+ margin-right: 4px;
740
+ border-radius: 2px;
741
+ background: #9bb3d0;
742
+ vertical-align: middle;
743
+ }
744
+
745
+ /* 激活分组:白底 + 白边 joint 复刻原 x-tab-joint 连接条 */
746
+ .x-grouptabs-strip li.x-grouptabs-strip-active {
747
+ background: #fff;
748
+ border-top: 1px solid #999;
749
+ border-bottom: 1px solid #999;
750
+ }
751
+ .x-grouptabs-panel.x-tab-panel-left .x-grouptabs-strip li.x-grouptabs-strip-active {
752
+ border-left: 1px solid #999;
753
+ border-right: 3px solid #fff; /* joint 连接到右侧 body */
754
+ }
755
+ .x-grouptabs-panel.x-tab-panel-right .x-grouptabs-strip li.x-grouptabs-strip-active {
756
+ border-right: 1px solid #999;
757
+ border-left: 3px solid #fff;
758
+ }
759
+ .x-grouptabs-strip li.x-grouptabs-strip-active ul.x-grouptabs-sub {
760
+ background: transparent;
761
+ }
762
+ .x-grouptabs-strip li.x-grouptabs-strip-active ul.x-grouptabs-sub li.x-grouptabs-strip-active {
763
+ background-color: #EDEEF0;
764
+ }
765
+ .x-grouptabs-strip li.x-grouptabs-strip-active ul.x-grouptabs-sub li {
766
+ border-color: transparent;
767
+ }
768
+
769
+ /* 右侧内容区(等价 CardLayout 当前页) */
770
+ .x-grouptabs-panel-body {
771
+ border: 1px solid #999;
772
+ background: #fff;
773
+ overflow: auto;
774
+ height: 100%;
775
+ box-sizing: border-box;
776
+ }
777
+ .x-grouptab-page {
778
+ min-height: 100%;
779
+ }
780
+
781
+ /* demo 图标着色 */
782
+ .x-icon-tickets { background: #5a8f3c !important; }
783
+ .x-icon-dashboard { background: #3892d4 !important; }
784
+ .x-icon-sub { background: #2e7d32 !important; }
785
+ .x-icon-users { background: #6a1b9a !important; }
786
+ .x-icon-configuration { background: #ef6c00 !important; }
787
+ .x-icon-templates { background: #00838f !important; }
788
+ .x-icon-reports { background: #c62828 !important; }
789
+
790
+ /* 工具栏重排与拖入 ToolbarReorderer / ToolbarDroppable */
791
+ .x-toolbar-reorderer {
792
+ position: relative;
793
+ padding: 2px 0;
794
+ }
795
+ .x-toolbar-reorderer.x-toolbar-drop-over .x-toolbar {
796
+ outline: 2px dashed #3892d4;
797
+ outline-offset: 1px;
798
+ }
799
+ .x-toolbar-reorder-indicator {
800
+ position: absolute;
801
+ width: 2px;
802
+ background: #3892d4;
803
+ z-index: 15001;
804
+ pointer-events: none;
805
+ box-shadow: 0 0 0 1px rgba(56, 146, 212, .25);
806
+ }
807
+
808
+ /* ── ProgressBarPager:工具栏内进度条去"白底" + 垂直居中 ──────────────
809
+ * 1) 原始 Ext.ProgressBar 的 .x-progress-inner 底色为 #e0e8f3(极浅蓝,近似白),
810
+ * 嵌入工具栏(ProgressBarPager)后,未填充区域会露出这层白底,在边框与蓝色填充
811
+ * 之间显得像一块白底。在 .x-toolbar 上下文中仅令 inner 背景透明(露出工具栏底色),
812
+ * 保留 .x-progress-wrap 的蓝色边框与蓝色填充。
813
+ * 2) 垂直居中:.x-toolbar 有 padding:2px 但只有底边 1px 边框(border-width:0 0 1px 0),
814
+ * 导致整行内容相对工具栏中心偏上 0.5px;实心进度条比按钮更显眼。用纯视觉偏移
815
+ * translateY(0.5px) 补偿(不影响布局/兄弟项)。 */
816
+ .x-toolbar .x-progress-inner {
817
+ background-color: transparent;
818
+ background-image: none;
819
+ }
820
+ .x-toolbar .x-progress-wrap {
821
+ transform: translateY(0.5px);
822
+ }
823
+
824
+ /* ===== Grid 表格固定布局(保真修复:表头与数据行列宽对齐) =====
825
+ 原版 ExtJS 由 GridView 运行时给 .x-grid3-row-table / 表头 table 注入 table-layout:fixed,
826
+ 并靠 colgroup/td 显式宽度锁定列宽(resources/css/ext-all.css 未直接给出该规则,系 JS 注入)。
827
+ 本实现只用内联 width 设列宽,若缺 table-layout:fixed,浏览器会按内容自动分配列宽,
828
+ 导致数据行各列与表头错位(如长文本列把后续列挤偏)。此处显式锁定固定布局,使表头与
829
+ 数据行严格对齐;配合各单元格内联 width(+末列吸收 slack)实现像素级对齐。 */
830
+ .x-grid3-row-table,
831
+ .x-grid3-header-offset > table {
832
+ table-layout: fixed;
833
+ }
834
+
835
+ /* ===== RowExpander(Ext.ux.grid.RowExpander)专属样式 =====
836
+ 真值来源:resources/css/ext-all.css(行 2471–2498、5860–5862)。原版用
837
+ row-expand-sprite.gif 精灵图作 + / - 切换图标(收起 background-position:4px 2px 显示 +,
838
+ 展开 .x-grid3-row-expanded 时 -21px 2px 显示 −),背景图由 ext-all.css 注入、素材位于
839
+ public/extjs-assets/images/default/grid/row-expand-sprite.gif,保真直接复用,不自绘。
840
+ 此处只补少量交互/布局态,绝不覆盖原版的 width/height/background-position/background-image,
841
+ 否则精灵图会错位、并与我们的图形叠加导致图标错乱。 */
842
+ .x-grid3-td-expander { cursor: pointer; }
843
+ .x-grid3-hd-expander { cursor: default; }
844
+ .x-grid3-row-expander { cursor: pointer; user-select: none; }
845
+ .x-grid3-row-body {
846
+ overflow: hidden;
847
+ }
848
+ .x-grid3-row-body-inner {
849
+ padding: 6px 22px 8px 24px;
850
+ font: 11px arial, helvetica, sans-serif;
851
+ color: #222;
852
+ background: #f7f9fc;
853
+ border-top: 1px solid #e0e6ef;
854
+ border-bottom: 1px solid #e0e6ef;
855
+ }
856
+
857
+ /* ===== CheckColumn(Ext.ux.grid.CheckColumn)专属样式 =====
858
+ 原 Ext 用默认复选框(x-grid3-checkcol 包一个 <input type=checkbox>)。此处改用 CSS 绘制的
859
+ 方块复选框 + 内联 SVG 对勾,自包含渲染(避免原生 checkbox 在不同浏览器下样式不一致);
860
+ 勾选态加 x-grid3-checkcol-on(蓝底白勾,对齐 Ext 选中色 #15428b)。表头不渲染选择-all,
861
+ 仅显示列标题(对齐 Ext.ux.grid.CheckColumn 默认行为)。 */
862
+ .x-grid3-td-checkcolumn { cursor: pointer; }
863
+ .x-grid3-hd-checkcolumn { cursor: default; }
864
+ .x-grid3-checkcol {
865
+ width: 13px;
866
+ height: 13px;
867
+ margin: 0 auto;
868
+ box-sizing: border-box;
869
+ border: 1px solid #8a9bb3;
870
+ border-radius: 2px;
871
+ background: #fff;
872
+ display: flex;
873
+ align-items: center;
874
+ justify-content: center;
875
+ cursor: pointer;
876
+ color: #fff;
877
+ user-select: none;
878
+ transition: background-color 0.05s, border-color 0.05s;
879
+ }
880
+ .x-grid3-checkcol:hover { border-color: #5b7ab5; }
881
+ .x-grid3-checkcol-on {
882
+ background: #15428b;
883
+ border-color: #15428b;
884
+ }
885
+ .x-grid3-checkcol-icon { display: block; }
886
+ .x-grid3-checkcol:focus-visible {
887
+ outline: 1px solid #7f9de0;
888
+ outline-offset: 1px;
889
+ }