widget.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. /* eslint-disable @typescript-eslint/no-unused-expressions */
  2. /* eslint-disable consistent-return */
  3. /* eslint-disable @typescript-eslint/no-unsafe-return */
  4. const { join } = require('path');
  5. module.paths.push(join(Editor.App.path, 'node_modules'));
  6. const Vue = require('vue/dist/vue.min.js');
  7. const propUtils = require('../utils/prop');
  8. const cssMediaWidth = 340;
  9. let layout = 'vertical';
  10. exports.template = `
  11. <style>
  12. .widget-component {
  13. position: relative;
  14. margin-bottom: 4px;
  15. margin-left: 4px;
  16. }
  17. .widget-component[layout='horizontal'] {
  18. margin-left: 8px;
  19. }
  20. .widget-component[layout='vertical']>.layout {
  21. padding-left: 0;
  22. padding-top: 8px;
  23. }
  24. .widget-component[layout='vertical']>.layout .rect {
  25. position: relative;
  26. top: 0;
  27. left: 0;
  28. height: 154px;
  29. width: 150px;
  30. margin: 0 auto;
  31. }
  32. .widget-component[layout='horizontal']>.layout .rect {
  33. position: absolute;
  34. top: calc(50% - 65px);
  35. left: -5px;
  36. }
  37. .widget-component .m20-t {
  38. margin-top: 20px;
  39. }
  40. .widget-component>.layout {
  41. position: relative;
  42. padding-left: 150px;
  43. padding-top: 8px;
  44. padding-bottom: 8px;
  45. }
  46. .widget-component>.layout .ui-prop {
  47. display: inline-block;
  48. }
  49. .widget-component>.layout ui-num-input {
  50. max-width: 80px;
  51. user-select: none;
  52. }
  53. .widget-component>.layout ui-checkbox {
  54. user-select: none;
  55. }
  56. .widget-component>.layout .rect>.top {
  57. position: absolute;
  58. top: 0;
  59. left: 0;
  60. width: 140px;
  61. height: 20px;
  62. text-align: center;
  63. }
  64. .widget-component>.layout .rect>.right {
  65. position: absolute;
  66. top: 140px;
  67. left: 120px;
  68. width: 140px;
  69. text-align: center;
  70. height: 20px;
  71. transform: rotate(-90deg);
  72. transform-origin: 0 0;
  73. }
  74. .widget-component>.layout .rect>.bottom {
  75. position: absolute;
  76. top: 125px;
  77. left: 0;
  78. width: 140px;
  79. height: 20px;
  80. text-align: center;
  81. }
  82. .widget-component>.layout .rect>.left {
  83. position: absolute;
  84. top: 140px;
  85. left: -2px;
  86. width: 140px;
  87. text-align: center;
  88. height: 20px;
  89. transform: rotate(-90deg);
  90. transform-origin: 0 0;
  91. }
  92. .widget-component>.layout .rect .widget-rect {
  93. position: absolute;
  94. top: 21px;
  95. left: 20px;
  96. width: 100px;
  97. height: 100px;
  98. background-color: var(--color-normal-fill-weaker);
  99. border: 1px solid var(--color-normal-fill-important);
  100. box-shadow: var(--color-normal-fill-emphasis) 0 0 4px;
  101. box-sizing: border-box;
  102. }
  103. .widget-component>.layout .rect .widget-rect>.center {
  104. position: absolute;
  105. top: 25%;
  106. left: 25%;
  107. width: 50%;
  108. height: 50%;
  109. z-index: 2;
  110. background-color: var(--color-normal-fill);
  111. border: 1px solid var(--color-normal-fill-important);
  112. border-radius: 2px;
  113. box-sizing: border-box;
  114. }
  115. .widget-component>.layout .rect .widget-rect>.center[bottom] {
  116. top: auto;
  117. bottom: 12.5%;
  118. }
  119. .widget-component>.layout .rect .widget-rect>.center[bottom][top] {
  120. height: auto;
  121. }
  122. .widget-component>.layout .rect .widget-rect>.center[bottom]>ui-icon[bottom] {
  123. display: block;
  124. }
  125. .widget-component>.layout .rect .widget-rect>.center[top] {
  126. top: 12.5%;
  127. }
  128. .widget-component>.layout .rect .widget-rect>.center[top]>ui-icon[top] {
  129. display: block;
  130. }
  131. .widget-component>.layout .rect .widget-rect>.center[right] {
  132. left: auto;
  133. right: 12.5%;
  134. }
  135. .widget-component>.layout .rect .widget-rect>.center[right][left] {
  136. width: auto;
  137. }
  138. .widget-component>.layout .rect .widget-rect>.center[right]>ui-icon[right] {
  139. display: block;
  140. }
  141. .widget-component>.layout .rect .widget-rect>.center[left] {
  142. left: 12.5%;
  143. }
  144. .widget-component>.layout .rect .widget-rect>.center[left]>ui-icon[left] {
  145. display: block;
  146. }
  147. .widget-component>.layout .rect .widget-rect>.center>ui-icon {
  148. display: none;
  149. }
  150. .widget-component>.layout .rect .widget-rect>.center>ui-icon[top] {
  151. position: absolute;
  152. top: -15px;
  153. left: calc(50% - 7px);
  154. font-size: 11px;
  155. line-height: 10px;
  156. }
  157. .widget-component>.layout .rect .widget-rect>.center>ui-icon[right] {
  158. transform: rotate(90deg);
  159. position: absolute;
  160. right: -15px;
  161. top: calc(50% - 7px);
  162. font-size: 11px;
  163. line-height: 11px;
  164. }
  165. .widget-component>.layout .rect .widget-rect>.center>ui-icon[bottom] {
  166. position: absolute;
  167. bottom: -15px;
  168. left: calc(50% - 7px);
  169. font-size: 11px;
  170. line-height: 10px;
  171. }
  172. .widget-component>.layout .rect .widget-rect>.center>ui-icon[left] {
  173. transform: rotate(90deg);
  174. position: absolute;
  175. left: -15px;
  176. top: calc(50% - 7px);
  177. font-size: 11px;
  178. line-height: 11px;
  179. }
  180. .widget-component>.layout .rect .widget-rect>.top {
  181. position: absolute;
  182. top: 0;
  183. left: 0;
  184. width: 100%;
  185. height: 12.5%;
  186. border-bottom: 1px dashed var(--color-normal-contrast);
  187. }
  188. .widget-component>.layout .rect .widget-rect>.bottom {
  189. position: absolute;
  190. bottom: 0;
  191. left: 0;
  192. width: 100%;
  193. height: 12.5%;
  194. border-top: 1px dashed var(--color-normal-contrast);
  195. }
  196. .widget-component>.layout .rect .widget-rect>.left {
  197. position: absolute;
  198. top: 0;
  199. left: 0;
  200. height: 100%;
  201. width: 12.5%;
  202. border-right: 1px dashed var(--color-normal-contrast);
  203. }
  204. .widget-component>.layout .rect .widget-rect>.right {
  205. position: absolute;
  206. top: 0;
  207. right: 0;
  208. height: 100%;
  209. width: 12.5%;
  210. border-left: 1px dashed var(--color-normal-contrast);
  211. }
  212. .widget-component>.layout .rect .widget-rect>.horizontal {
  213. position: absolute;
  214. top: 50%;
  215. left: 0;
  216. right: 0;
  217. z-index: 11;
  218. height: 1px;
  219. border-top: 1px dashed var(--color-normal-contrast);
  220. font-size: 0;
  221. }
  222. .widget-component>.layout .rect .widget-rect>.vertical {
  223. position: absolute;
  224. top: 0;
  225. left: 50%;
  226. bottom: 0;
  227. z-index: 11;
  228. width: 1px;
  229. border-left: 1px dashed var(--color-normal-contrast);
  230. font-size: 0;
  231. }
  232. .widget-component>.layout>.right>.line {
  233. --ui-prop-margin-left: 0;
  234. display: flex;
  235. justify-content: space-between;
  236. }
  237. .widget-component>.layout>.right>.line>.name {
  238. width: 60%;
  239. }
  240. .widget-component>.layout>.right>.inputs>.ui-prop {
  241. visibility: hidden;
  242. pointer-events: none;
  243. }
  244. .widget-component>.layout>.right>.inputs>.ui-prop[active] {
  245. visibility: visible;
  246. pointer-events: auto;
  247. }
  248. .widget-component .button-group {
  249. border: 1px solid var(--color-default-border-important);
  250. border-radius: 2px;
  251. overflow: hidden;
  252. display: flex;
  253. flex: 1;
  254. justify-content: space-between;
  255. }
  256. .widget-component .button-group .button {
  257. flex: 1;
  258. padding: 0 5px;
  259. text-align: center;
  260. font-size: 11px;
  261. border-right: 1px solid var(--color-default-border-important);
  262. cursor: pointer;
  263. }
  264. .widget-component .button-group .button:last-child {
  265. border-right: none;
  266. }
  267. .widget-component .button-group .button[active],
  268. .widget-component .button-group .button:hover {
  269. color: var(--color-normal-contrast-weakest);
  270. background-color: var(--color-normal-fill-emphasis);
  271. }
  272. .widget-component .button-group .button[active] .icon .long,
  273. .widget-component .button-group .button[active] .icon .short,
  274. .widget-component .button-group .button:hover .icon .long,
  275. .widget-component .button-group .button:hover .icon .short {
  276. background-color: var(--color-normal-contrast-weakest);
  277. }
  278. .widget-component .button-group .button .icon {
  279. width: 18px;
  280. height: 18px;
  281. margin: 1px auto 0 auto;
  282. font-size: 0;
  283. position: relative;
  284. }
  285. .widget-component .button-group .button .icon .line {
  286. position: absolute;
  287. z-index: 3;
  288. }
  289. .widget-component .button-group .button .icon .line.second {
  290. display: none;
  291. }
  292. .widget-component .button-group .button .icon .long {
  293. position: absolute;
  294. width: 12px;
  295. height: 4px;
  296. background-color: var(--color-normal-contrast-weakest);
  297. }
  298. .widget-component .button-group .button .icon .short {
  299. position: absolute;
  300. width: 7px;
  301. height: 4px;
  302. background-color: var(--color-normal-contrast-weakest);
  303. }
  304. .widget-component .button-group .button .left.bottom {
  305. transform: rotate(-90deg);
  306. }
  307. .widget-component .button-group .button .left .line {
  308. left: 0;
  309. top: 0;
  310. height: 100%;
  311. border-left: 1px dashed var(--color-focus-border);
  312. }
  313. .widget-component .button-group .button .left .long {
  314. left: 2px;
  315. top: 4px;
  316. }
  317. .widget-component .button-group .button .left .short {
  318. left: 2px;
  319. top: 11px;
  320. }
  321. .widget-component .button-group .button .center.middle {
  322. transform: rotate(-90deg);
  323. }
  324. .widget-component .button-group .button .center .line {
  325. left: 9px;
  326. top: 0;
  327. height: 100%;
  328. border-left: 1px dashed var(--color-focus-border);
  329. }
  330. .widget-component .button-group .button .center .long {
  331. left: 4px;
  332. top: 4px;
  333. }
  334. .widget-component .button-group .button .center .short {
  335. left: 6px;
  336. top: 11px;
  337. }
  338. .widget-component .button-group .button .right.top {
  339. transform: rotate(-90deg);
  340. }
  341. .widget-component .button-group .button .right .line {
  342. right: 0;
  343. top: 0;
  344. height: 100%;
  345. border-left: 1px dashed var(--color-focus-border);
  346. }
  347. .widget-component .button-group .button .right .long {
  348. right: 2px;
  349. top: 4px;
  350. }
  351. .widget-component .button-group .button .right .short {
  352. right: 2px;
  353. top: 11px;
  354. }
  355. .widget-component .button-group .button .horizontal.vertical {
  356. transform: rotate(-90deg);
  357. }
  358. .widget-component .button-group .button .horizontal .line {
  359. left: 0;
  360. top: 0;
  361. height: 100%;
  362. border-left: 1px dashed var(--color-focus-border);
  363. }
  364. .widget-component .button-group .button .horizontal .line.second {
  365. display: block;
  366. left: unset;
  367. right: 0;
  368. top: 0;
  369. }
  370. .widget-component .button-group .button .horizontal .long {
  371. left: 2px;
  372. right: 2px;
  373. top: 4px;
  374. width: auto;
  375. }
  376. .widget-component .button-group .button .horizontal .short {
  377. left: 2px;
  378. right: 2px;
  379. top: 11px;
  380. width: auto;
  381. }
  382. .widget-component .direction>.name {
  383. display: inline-block;
  384. }
  385. .widget-component .direction>.icon {
  386. float: right;
  387. font-size: 10px;
  388. margin-right: 2px;
  389. }
  390. .widget-component .direction>.icon>ui-icon:hover {
  391. color: var(--color-warn-fill);
  392. }
  393. .widget-component .direction>.icon>.lock {
  394. color: var(--color-warn-fill);
  395. }
  396. </style>
  397. <div id="app"></div>
  398. `;
  399. const excludeList = ['uuid', 'name', 'enabled', '_name', '_objFlags', '__scriptAsset', 'node', '_enabled', '__prefab', 'target', 'isAlignTop', 'isAlignBottom', 'isAlignLeft', 'isAlignRight', 'isAlignVerticalCenter', 'isAlignHorizontalCenter', 'isStretchWidth', 'isStretchHeight', 'top', 'editorTop', 'bottom', 'editorBottom', 'left', 'editorLeft', 'right', 'editorRight', 'horizontalCenter', 'editorHorizontalCenter', 'verticalCenter', 'editorVerticalCenter', 'isAbsoluteTop', 'isAbsoluteBottom', 'isAbsoluteLeft', 'isAbsoluteRight', 'isAbsoluteHorizontalCenter', 'isAbsoluteVerticalCenter', 'alignMode', 'alignFlags', '_alignFlags', '_target', '_left', '_right', '_top', '_bottom', '_horizontalCenter', '_verticalCenter', '_isAbsLeft', '_isAbsRight', '_isAbsTop', '_isAbsBottom', '_isAbsHorizontalCenter', '_isAbsVerticalCenter', '_originalWidth', '_originalHeight', '_alignMode', '_lockFlags'];
  400. // Used to determine if the value is locked
  401. const LockFlags = {
  402. top: 1 << 0,
  403. middle: 1 << 1,
  404. bottom: 1 << 2,
  405. left: 1 << 3,
  406. center: 1 << 4,
  407. right: 1 << 5,
  408. };
  409. exports.$ = {
  410. app: '#app',
  411. };
  412. exports.methods = {
  413. getObjectByKey(target, key) {
  414. let params = [];
  415. if (typeof key === 'string') {
  416. params = key.split('.');
  417. } else if (key instanceof Array) {
  418. params = key;
  419. }
  420. if (params.length > 0) {
  421. const value = params.shift();
  422. return this.getObjectByKey(target[value], params);
  423. } else {
  424. return target;
  425. }
  426. },
  427. getDimensionHorizontal() {
  428. const {
  429. isAlignLeft, isAlignRight, isAlignHorizontalCenter,
  430. } = this.dump.value;
  431. let dimension = '';
  432. if (isAlignLeft.value) {
  433. dimension = 'left';
  434. }
  435. if (isAlignRight.value) {
  436. dimension = 'right';
  437. }
  438. if (isAlignLeft.value && isAlignRight.value) {
  439. dimension = 'stretch';
  440. }
  441. if (isAlignHorizontalCenter.value) {
  442. dimension = 'center';
  443. }
  444. return dimension;
  445. },
  446. getDimensionVertical() {
  447. const {
  448. isAlignTop, isAlignBottom, isAlignVerticalCenter,
  449. } = this.dump.value;
  450. let dimension = '';
  451. if (isAlignTop.value) {
  452. dimension = 'top';
  453. }
  454. if (isAlignBottom.value) {
  455. dimension = 'bottom';
  456. }
  457. if (isAlignTop.value && isAlignBottom.value) {
  458. dimension = 'stretch';
  459. }
  460. if (isAlignVerticalCenter.value) {
  461. dimension = 'middle';
  462. }
  463. return dimension;
  464. },
  465. isInvalid(key) {
  466. if (Array.isArray(this.dump.value[key].values)) {
  467. return this.dump.value[key].values.some((value) => value !== this.dump.value[key].value);
  468. }
  469. return false;
  470. },
  471. update() {
  472. for (const key in uiElements) {
  473. const element = uiElements[key];
  474. if (typeof element.update === 'function') {
  475. element.update.call(this);
  476. }
  477. }
  478. },
  479. change(key, newValue) {
  480. this.dump.value[key].value = newValue;
  481. this.$refs.summitProp.dump = this.dump.value[key];
  482. if ('values' in this.dump.value[key]) {
  483. this.dump.value[key].values.forEach((_, index) => {
  484. this.dump.value[key].values[index] = newValue;
  485. });
  486. }
  487. this.$refs.summitProp.dispatch('change-dump');
  488. },
  489. snapshot() {
  490. // next tick snapshot
  491. setTimeout(() => {
  492. this.$refs.summitProp.dispatch('confirm-dump');
  493. });
  494. },
  495. getUnit(type) {
  496. const data = this.dump.value;
  497. switch (type) {
  498. case 'editorTop':
  499. return data.isAbsoluteTop.value ? 'px' : '%';
  500. case 'editorBottom':
  501. return data.isAbsoluteBottom.value ? 'px' : '%';
  502. case 'editorLeft':
  503. return data.isAbsoluteLeft.value ? 'px' : '%';
  504. case 'editorRight':
  505. return data.isAbsoluteRight.value ? 'px' : '%';
  506. case 'editorHorizontalCenter':
  507. return data.isAbsoluteHorizontalCenter.value ? 'px' : '%';
  508. case 'editorVerticalCenter':
  509. return data.isAbsoluteVerticalCenter.value ? 'px' : '%';
  510. default:
  511. break;
  512. }
  513. },
  514. changeUnit(type) {
  515. function update(dump, force) {
  516. if (force !== true && dump === this.dump) {
  517. return;
  518. }
  519. const value = dump.value;
  520. switch (type) {
  521. case 'editorTop':
  522. value.isAbsoluteTop.value = !value.isAbsoluteTop.value;
  523. if ('values' in value.isAbsoluteTop) {
  524. value.isAbsoluteTop.values.forEach((val, index) => {
  525. value.isAbsoluteTop.values[index] = value.isAbsoluteTop.value;
  526. });
  527. }
  528. return { path: 'isAbsoluteTop', dump: value.isAbsoluteTop };
  529. case 'editorBottom':
  530. value.isAbsoluteBottom.value = !value.isAbsoluteBottom.value;
  531. if ('values' in value.isAbsoluteBottom) {
  532. value.isAbsoluteBottom.values.forEach((val, index) => {
  533. value.isAbsoluteBottom.values[index] = value.isAbsoluteBottom.value;
  534. });
  535. }
  536. return { path: 'isAbsoluteBottom', dump: value.isAbsoluteBottom };
  537. case 'editorLeft':
  538. value.isAbsoluteLeft.value = !value.isAbsoluteLeft.value;
  539. if ('values' in value.isAbsoluteLeft) {
  540. value.isAbsoluteLeft.values.forEach((val, index) => {
  541. value.isAbsoluteLeft.values[index] = value.isAbsoluteLeft.value;
  542. });
  543. }
  544. return { path: 'isAbsoluteLeft', dump: value.isAbsoluteLeft };
  545. case 'editorRight':
  546. value.isAbsoluteRight.value = !value.isAbsoluteRight.value;
  547. if ('values' in value.isAbsoluteRight) {
  548. value.isAbsoluteRight.values.forEach((val, index) => {
  549. value.isAbsoluteRight.values[index] = value.isAbsoluteRight.value;
  550. });
  551. }
  552. return { path: 'isAbsoluteRight', dump: value.isAbsoluteRight };
  553. case 'editorHorizontalCenter':
  554. value.isAbsoluteHorizontalCenter.value = !value.isAbsoluteHorizontalCenter.value;
  555. if ('values' in value.isAbsoluteHorizontalCenter) {
  556. value.isAbsoluteHorizontalCenter.values.forEach((val, index) => {
  557. value.isAbsoluteHorizontalCenter.values[index] = value.isAbsoluteHorizontalCenter.value;
  558. });
  559. }
  560. return { path: 'isAbsoluteHorizontalCenter', dump: value.isAbsoluteHorizontalCenter };
  561. case 'editorVerticalCenter':
  562. value.isAbsoluteVerticalCenter.value = !value.isAbsoluteVerticalCenter.value;
  563. if ('values' in value.isAbsoluteVerticalCenter) {
  564. value.isAbsoluteVerticalCenter.values.forEach((val, index) => {
  565. value.isAbsoluteVerticalCenter.values[index] = value.isAbsoluteVerticalCenter.value;
  566. });
  567. }
  568. return { path: 'isAbsoluteVerticalCenter', dump: value.isAbsoluteVerticalCenter };
  569. default:
  570. break;
  571. }
  572. }
  573. const { dump } = update(this.dump, true);
  574. this.$refs.summitProp.dump = dump;
  575. this.$refs.summitProp.dispatch('change-dump');
  576. this.snapshot();
  577. },
  578. select(event) {
  579. if (!event.path) {
  580. event.path = event.composedPath();
  581. }
  582. // Handling through delegated events
  583. const button = event.path.find((element) => element && element.classList && element.classList.contains('button'));
  584. if (!button) {
  585. return;
  586. }
  587. const dimension = button.getAttribute('dimension');
  588. let horizontal;
  589. let vertical;
  590. switch (dimension) {
  591. case 'horizontal':
  592. horizontal = {
  593. isAlignLeft: {
  594. value: false,
  595. },
  596. isAlignRight: {
  597. value: false,
  598. },
  599. isAlignHorizontalCenter: {
  600. value: false,
  601. },
  602. };
  603. break;
  604. case 'left':
  605. horizontal = {
  606. isAlignLeft: {
  607. value: true,
  608. },
  609. isAlignRight: {
  610. value: false,
  611. },
  612. isAlignHorizontalCenter: {
  613. value: false,
  614. },
  615. };
  616. break;
  617. case 'center':
  618. horizontal = {
  619. isAlignLeft: {
  620. value: false,
  621. },
  622. isAlignRight: {
  623. value: false,
  624. },
  625. isAlignHorizontalCenter: {
  626. value: true,
  627. },
  628. };
  629. break;
  630. case 'right':
  631. horizontal = {
  632. isAlignLeft: {
  633. value: false,
  634. },
  635. isAlignRight: {
  636. value: true,
  637. },
  638. isAlignHorizontalCenter: {
  639. value: false,
  640. },
  641. };
  642. break;
  643. case 'h-stretch':
  644. horizontal = {
  645. isAlignLeft: {
  646. value: true,
  647. },
  648. isAlignRight: {
  649. value: true,
  650. },
  651. isAlignHorizontalCenter: {
  652. value: false,
  653. },
  654. };
  655. break;
  656. case 'vertical':
  657. vertical = {
  658. isAlignTop: {
  659. value: false,
  660. },
  661. isAlignVerticalCenter: {
  662. value: false,
  663. },
  664. isAlignBottom: {
  665. value: false,
  666. },
  667. };
  668. break;
  669. case 'top':
  670. vertical = {
  671. isAlignTop: {
  672. value: true,
  673. },
  674. isAlignVerticalCenter: {
  675. value: false,
  676. },
  677. isAlignBottom: {
  678. value: false,
  679. },
  680. };
  681. break;
  682. case 'middle':
  683. vertical = {
  684. isAlignTop: {
  685. value: false,
  686. },
  687. isAlignVerticalCenter: {
  688. value: true,
  689. },
  690. isAlignBottom: {
  691. value: false,
  692. },
  693. };
  694. break;
  695. case 'bottom':
  696. vertical = {
  697. isAlignTop: {
  698. value: false,
  699. },
  700. isAlignVerticalCenter: {
  701. value: false,
  702. },
  703. isAlignBottom: {
  704. value: true,
  705. },
  706. };
  707. break;
  708. case 'v-stretch':
  709. vertical = {
  710. isAlignTop: {
  711. value: true,
  712. },
  713. isAlignVerticalCenter: {
  714. value: false,
  715. },
  716. isAlignBottom: {
  717. value: true,
  718. },
  719. };
  720. break;
  721. default:
  722. break;
  723. }
  724. const dump = this.dump;
  725. if (horizontal) {
  726. if (dump.value.isAlignLeft.value !== horizontal.isAlignLeft.value || !this.isHorizontalAlignValid) {
  727. dump.value.isAlignLeft.value = horizontal.isAlignLeft.value;
  728. this.$refs.summitProp.dump = dump.value.isAlignLeft;
  729. if ('values' in dump.value.isAlignLeft) {
  730. dump.value.isAlignLeft.values.forEach((val, index) => {
  731. dump.value.isAlignLeft.values[index] = dump.value.isAlignLeft.value;
  732. });
  733. }
  734. this.$refs.summitProp.dispatch('change-dump');
  735. }
  736. if (dump.value.isAlignRight.value !== horizontal.isAlignRight.value || !this.isHorizontalAlignValid) {
  737. dump.value.isAlignRight.value = horizontal.isAlignRight.value;
  738. this.$refs.summitProp.dump = dump.value.isAlignRight;
  739. if ('values' in dump.value.isAlignRight) {
  740. dump.value.isAlignRight.values.forEach((val, index) => {
  741. dump.value.isAlignRight.values[index] = dump.value.isAlignRight.value;
  742. });
  743. }
  744. this.$refs.summitProp.dispatch('change-dump');
  745. }
  746. if (dump.value.isAlignHorizontalCenter.value !== horizontal.isAlignHorizontalCenter.value || !this.isHorizontalAlignValid) {
  747. dump.value.isAlignHorizontalCenter.value = horizontal.isAlignHorizontalCenter.value;
  748. this.$refs.summitProp.dump = dump.value.isAlignHorizontalCenter;
  749. if ('values' in dump.value.isAlignHorizontalCenter) {
  750. dump.value.isAlignHorizontalCenter.values.forEach((val, index) => {
  751. dump.value.isAlignHorizontalCenter.values[index] = dump.value.isAlignHorizontalCenter.value;
  752. });
  753. }
  754. this.$refs.summitProp.dispatch('change-dump');
  755. }
  756. this.dimensionHorizontal = this.getDimensionHorizontal();
  757. }
  758. if (vertical) {
  759. if (dump.value.isAlignTop.value !== vertical.isAlignTop.value || !this.isVerticalAlignValid) {
  760. dump.value.isAlignTop.value = vertical.isAlignTop.value;
  761. this.$refs.summitProp.dump = dump.value.isAlignTop;
  762. if ('values' in dump.value.isAlignTop) {
  763. dump.value.isAlignTop.values.forEach((val, index) => {
  764. dump.value.isAlignTop.values[index] = dump.value.isAlignTop.value;
  765. });
  766. }
  767. this.$refs.summitProp.dispatch('change-dump');
  768. }
  769. if (dump.value.isAlignVerticalCenter.value !== vertical.isAlignVerticalCenter.value || !this.isVerticalAlignValid) {
  770. dump.value.isAlignVerticalCenter.value = vertical.isAlignVerticalCenter.value;
  771. this.$refs.summitProp.dump = dump.value.isAlignVerticalCenter;
  772. if ('values' in dump.value.isAlignVerticalCenter) {
  773. dump.value.isAlignVerticalCenter.values.forEach((val, index) => {
  774. dump.value.isAlignVerticalCenter.values[index] = dump.value.isAlignVerticalCenter.value;
  775. });
  776. }
  777. this.$refs.summitProp.dispatch('change-dump');
  778. }
  779. if (dump.value.isAlignBottom.value !== vertical.isAlignBottom.value || !this.isVerticalAlignValid) {
  780. dump.value.isAlignBottom.value = vertical.isAlignBottom.value;
  781. this.$refs.summitProp.dump = dump.value.isAlignBottom;
  782. if ('values' in dump.value.isAlignBottom) {
  783. dump.value.isAlignBottom.values.forEach((val, index) => {
  784. dump.value.isAlignBottom.values[index] = dump.value.isAlignBottom.value;
  785. });
  786. }
  787. this.$refs.summitProp.dispatch('change-dump');
  788. }
  789. this.dimensionVertical = this.getDimensionVertical();
  790. }
  791. this.snapshot();
  792. },
  793. toggleLock(direction) {
  794. const isLock = this.isLock(direction);
  795. let directions = [direction];
  796. let lockValue = this.dump.value._lockFlags.value;
  797. const isStretchWidth = this.dump.value.isStretchWidth.value;
  798. const isStretchHeight = this.dump.value.isStretchHeight.value;
  799. const stretchWidth = ['left', 'right'];
  800. if (isStretchWidth && stretchWidth.includes(direction)) {
  801. directions = stretchWidth;
  802. }
  803. const stretchHeight = ['top', 'bottom'];
  804. if (isStretchHeight && stretchHeight.includes(direction)) {
  805. directions = stretchHeight;
  806. }
  807. directions.forEach((dir) => {
  808. const lockDirection = LockFlags[dir];
  809. if (isLock) {
  810. lockValue &= ~lockDirection;
  811. } else {
  812. lockValue |= lockDirection;
  813. }
  814. });
  815. // Submit data
  816. this.dump.value._lockFlags.value = lockValue;
  817. this.$refs.summitProp.dump = this.dump.value._lockFlags;
  818. if ('values' in this.dump.value._lockFlags) {
  819. this.dump.value._lockFlags.values.forEach((val, index) => {
  820. this.dump.value._lockFlags.values[index] = lockValue;
  821. });
  822. }
  823. this.$refs.summitProp.dispatch('change-dump');
  824. this.snapshot();
  825. },
  826. isLock(direction) {
  827. const lockValue = this.dump.value._lockFlags.value;
  828. const lockDirection = LockFlags[direction];
  829. return lockValue & lockDirection;
  830. },
  831. setLayout() {
  832. const rect = this.$this.getBoundingClientRect();
  833. this.layout ??= layout;
  834. if (rect.width) {
  835. if (rect.width > cssMediaWidth) {
  836. layout = this.layout = 'horizontal';
  837. } else {
  838. layout = this.layout = 'vertical';
  839. }
  840. }
  841. },
  842. };
  843. const uiElements = {
  844. baseProps: {
  845. ready() {
  846. this.$baseProps = this.$el && this.$el.querySelectorAll('ui-prop:not(.customProp)');
  847. },
  848. update() {
  849. if (!this.$baseProps) {
  850. uiElements.baseProps.ready.call(this);
  851. }
  852. this.$baseProps.forEach((element) => {
  853. const key = element.getAttribute('dump-key');
  854. const dump = this.getObjectByKey(this.dump.value, key);
  855. const isEmpty = element.getAttribute('empty');
  856. if (!isEmpty) {
  857. const isShow = dump.visible;
  858. if (isShow) {
  859. element.render(dump);
  860. }
  861. element.style = isShow ? '' : 'display: none;';
  862. }
  863. });
  864. },
  865. },
  866. customProps: {
  867. update() {
  868. if (!this.$customProps) {
  869. this.$customProps = this.$el.querySelector('#customProps');
  870. }
  871. propUtils.updateCustomPropElements(this.$customProps, excludeList, this.dump, (element, prop) => {
  872. element.className = 'customProp';
  873. if (prop.dump.visible) {
  874. element.render(prop.dump);
  875. }
  876. element.hidden = !prop.dump.visible;
  877. });
  878. },
  879. },
  880. };
  881. const template = /* html*/`
  882. <div class="widget-component" :layout="layout">
  883. <!--TODO: don't hack-->
  884. <ui-prop ref="summitProp" style="display:none"></ui-prop>
  885. <div class="layout">
  886. <div class="rect">
  887. <div class="top" v-show="dimensionVertical==='top' || dimensionVertical==='stretch'">top</div>
  888. <div class="right" v-show="dimensionHorizontal==='right' || dimensionHorizontal==='stretch'">right</div>
  889. <div class="bottom" v-show="dimensionVertical==='bottom' || dimensionVertical==='stretch'">bottom</div>
  890. <div class="left" v-show="dimensionHorizontal==='left' || dimensionHorizontal==='stretch'">left</div>
  891. <div class="widget-rect">
  892. <div class="center" :top="dump.value.isAlignTop.value" :bottom="dump.value.isAlignBottom.value"
  893. :left="dump.value.isAlignLeft.value" :right="dump.value.isAlignRight.value">
  894. <ui-icon value="arrowsv" top></ui-icon>
  895. <ui-icon value="arrowsv" right></ui-icon>
  896. <ui-icon value="arrowsv" bottom></ui-icon>
  897. <ui-icon value="arrowsv" left></ui-icon>
  898. </div>
  899. <div class="top" v-if="dump.value.isAlignTop.value">
  900. </div>
  901. <div class="bottom" v-if="dump.value.isAlignBottom.value">
  902. </div>
  903. <div class="left" v-if="dump.value.isAlignLeft.value">
  904. </div>
  905. <div class="right" v-if="dump.value.isAlignRight.value">
  906. </div>
  907. <div class="vertical" v-if="dump.value.isAlignHorizontalCenter.value"></div>
  908. <div class="horizontal" v-if="dump.value.isAlignVerticalCenter.value"></div>
  909. </div>
  910. </div>
  911. <div class="right">
  912. <div class="line">
  913. <ui-label>Horizontal Alignment</ui-label>
  914. </div>
  915. <div class="line">
  916. <div class="button-group" @click="select($event)">
  917. <div class="button" dimension="horizontal" :active="isHorizontalAlignValid && dimensionHorizontal === ''">NONE</div>
  918. <div class="button" dimension="left" :active="isHorizontalAlignValid && dimensionHorizontal === 'left'">
  919. <widget-icon class="left" title="Left"></widget-icon>
  920. </div>
  921. <div class="button" dimension="center" :active="isHorizontalAlignValid && dimensionHorizontal === 'center'">
  922. <widget-icon class="center" title="Center"></widget-icon>
  923. </div>
  924. <div class="button" dimension="right" :active="isHorizontalAlignValid && dimensionHorizontal === 'right'">
  925. <widget-icon class="right" title="Right"></widget-icon>
  926. </div>
  927. <div class="button" dimension="h-stretch" :active="isHorizontalAlignValid && dimensionHorizontal === 'stretch'">
  928. <widget-icon class="horizontal" title="Horizontal Stretch"></widget-icon>
  929. </div>
  930. </div>
  931. </div>
  932. <div class="line inputs" v-if="dimensionHorizontal && isHorizontalAlignValid">
  933. <ui-prop empty="true" type="dump" dump-key="editorLeft" :active="isHorizontalAlignValid && dump.value.isAlignLeft.value">
  934. <div class="direction" v-show="dump.value.isAlignLeft.value">
  935. <span class="name">Left</span>
  936. <div class="icon" title="Lock left value" @click="toggleLock('left')">
  937. <ui-icon class="lock" value="lock" v-if="isLock('left')"></ui-icon>
  938. <ui-icon value="unlock" v-else></ui-icon>
  939. </div>
  940. </div>
  941. <ui-num-input tabindex="0" :unit="getUnit('editorLeft')" :invalid="isInvalid('editorLeft')"
  942. :disabled="!dump.value.isAlignLeft.value" :value="dump.value.editorLeft.value"
  943. @change="change('editorLeft', $event.target.value)"
  944. @confirm="snapshot()"
  945. @unit-click="changeUnit('editorLeft')"
  946. v-show="dump.value.isAlignLeft.value">
  947. </ui-num-input>
  948. </ui-prop>
  949. <ui-prop empty="true" type="dump" dump-key="editorHorizontalCenter"
  950. :active="isHorizontalAlignValid && dump.value.isAlignHorizontalCenter.value">
  951. <div class="direction" v-show="dump.value.isAlignHorizontalCenter.value">
  952. <span class="name">Center</span>
  953. <div class="icon" title="Lock center value" @click="toggleLock('center')">
  954. <ui-icon class="lock" value="lock" v-if="isLock('center')"></ui-icon>
  955. <ui-icon value="unlock" v-else></ui-icon>
  956. </div>
  957. </div>
  958. <ui-num-input tabindex="0" :unit="getUnit('editorHorizontalCenter')"
  959. v-show="dump.value.isAlignHorizontalCenter.value" :invalid="isInvalid('editorHorizontalCenter')"
  960. :disabled="!dump.value.isAlignHorizontalCenter.value"
  961. :value="dump.value.editorHorizontalCenter.value"
  962. @change="change('editorHorizontalCenter', $event.target.value)"
  963. @confirm="snapshot()"
  964. @unit-click="changeUnit('editorHorizontalCenter')"></ui-num-input>
  965. </ui-prop>
  966. <ui-prop empty="true" type="dump" dump-key="editorRight" :active="isHorizontalAlignValid && dump.value.isAlignRight.value">
  967. <div class="direction" v-show="dump.value.isAlignRight.value">
  968. <span class="name">Right</span>
  969. <div class="icon" title="Lock right value" @click="toggleLock('right')">
  970. <ui-icon class="lock" value="lock" v-if="isLock('right')"></ui-icon>
  971. <ui-icon value="unlock" v-else></ui-icon>
  972. </div>
  973. </div>
  974. <ui-num-input tabindex="0" :unit="getUnit('editorRight')" :invalid="isInvalid('editorRight')"
  975. :disabled="!dump.value.isAlignRight.value" :value="dump.value.editorRight.value"
  976. v-show="dump.value.isAlignRight.value"
  977. @change="change('editorRight', $event.target.value)"
  978. @confirm="snapshot()"
  979. @unit-click="changeUnit('editorRight')">
  980. </ui-num-input>
  981. </ui-prop>
  982. </div>
  983. <div class="line m20-t">
  984. <ui-label>Vertical Alignment</ui-label>
  985. </div>
  986. <div class="line">
  987. <div class="button-group" @click="select($event)">
  988. <div class="button" dimension="vertical" :active="isVerticalAlignValid && dimensionVertical === ''">NONE</div>
  989. <div class="button" dimension="top" :active="isVerticalAlignValid && dimensionVertical === 'top'">
  990. <widget-icon class="top right" title="Top"></widget-icon>
  991. </div>
  992. <div class="button" dimension="middle" :active="isVerticalAlignValid && dimensionVertical === 'middle'">
  993. <widget-icon class="middle center" title="Middle"></widget-icon>
  994. </div>
  995. <div class="button" dimension="bottom" :active="isVerticalAlignValid && dimensionVertical === 'bottom'">
  996. <widget-icon class="bottom left" title="Bottom"></widget-icon>
  997. </div>
  998. <div class="button" dimension="v-stretch" :active="isVerticalAlignValid && dimensionVertical === 'stretch'">
  999. <widget-icon class="vertical horizontal" title="Vertical Stretch"></widget-icon>
  1000. </div>
  1001. </div>
  1002. </div>
  1003. <div class="line inputs" v-if="dimensionVertical && isVerticalAlignValid">
  1004. <ui-prop empty="true" type="dump" dump-key="editorTop" :active="isVerticalAlignValid && dump.value.isAlignTop.value">
  1005. <div class="direction" v-show="dump.value.isAlignTop.value">
  1006. <span class="name">Top</span>
  1007. <div class="icon" title="Lock top value" @click="toggleLock('top')">
  1008. <ui-icon class="lock" value="lock" v-if="isLock('top')"></ui-icon>
  1009. <ui-icon value="unlock" v-else></ui-icon>
  1010. </div>
  1011. </div>
  1012. <ui-num-input tabindex="0" :unit="getUnit('editorTop')" :invalid="isInvalid('editorTop')"
  1013. :disabled="!dump.value.isAlignTop.value"
  1014. @change="change('editorTop', $event.target.value)"
  1015. @confirm="snapshot()"
  1016. :value="dump.value.editorTop.value" @unit-click="changeUnit('editorTop')"
  1017. v-show="dump.value.isAlignTop.value">
  1018. </ui-num-input>
  1019. </ui-prop>
  1020. <ui-prop empty="true" type="dump" dump-key="editorVerticalCenter"
  1021. :active="isVerticalAlignValid && dump.value.isAlignVerticalCenter.value">
  1022. <div class="direction" v-show="dump.value.isAlignVerticalCenter.value">
  1023. <span class="name">Middle</span>
  1024. <div class="icon" title="Lock middle value" @click="toggleLock('middle')">
  1025. <ui-icon class="lock" value="lock" v-if="isLock('middle')"></ui-icon>
  1026. <ui-icon value="unlock" v-else></ui-icon>
  1027. </div>
  1028. </div>
  1029. <ui-num-input tabindex="0" :unit="getUnit('editorVerticalCenter')"
  1030. :invalid="isInvalid('editorVerticalCenter')" :disabled="!dump.value.isAlignVerticalCenter.value"
  1031. @change="change('editorVerticalCenter', $event.target.value)"
  1032. @confirm="snapshot()"
  1033. :value="dump.value.editorVerticalCenter.value" @unit-click="changeUnit('editorVerticalCenter')"
  1034. v-show="dump.value.isAlignVerticalCenter.value"></ui-num-input>
  1035. </ui-prop>
  1036. <ui-prop empty="true" type="dump" dump-key="editorBottom" :active="isVerticalAlignValid && dump.value.isAlignBottom.value">
  1037. <div class="direction" v-show="dump.value.isAlignBottom.value">
  1038. <span class="name">Bottom</span>
  1039. <div class="icon" title="Lock bottom value" @click="toggleLock('bottom')">
  1040. <ui-icon class="lock" value="lock" v-if="isLock('bottom')"></ui-icon>
  1041. <ui-icon value="unlock" v-else></ui-icon>
  1042. </div>
  1043. </div>
  1044. <ui-num-input tabindex="0" :unit="getUnit('editorBottom')" :invalid="isInvalid('editorBottom')"
  1045. :disabled="!dump.value.isAlignBottom.value" :value="dump.value.editorBottom.value"
  1046. @change="change('editorBottom', $event.target.value)"
  1047. @confirm="snapshot()"
  1048. @unit-click="changeUnit('editorBottom')"
  1049. v-show="dump.value.isAlignBottom.value">
  1050. </ui-num-input>
  1051. </ui-prop>
  1052. </div>
  1053. </div>
  1054. </div>
  1055. <ui-prop type="dump" dump-key="target"></ui-prop>
  1056. <ui-prop type="dump" dump-key="alignMode"></ui-prop>
  1057. <!-- Render other data that is not taken over -->
  1058. <div id="customProps"></div>
  1059. </div>
  1060. `;
  1061. const components = {
  1062. 'widget-icon': {
  1063. template: `
  1064. <div class="icon">
  1065. <span class="line"></span>
  1066. <span class="long"></span>
  1067. <span class="short"></span>
  1068. <span class="line second"></span>
  1069. </div>
  1070. `,
  1071. },
  1072. };
  1073. const computed = {
  1074. isHorizontalAlignValid() {
  1075. return !(this.isInvalid('isAlignLeft') || this.isInvalid('isAlignRight') || this.isInvalid('isAlignHorizontalCenter'));
  1076. },
  1077. isVerticalAlignValid() {
  1078. return !(this.isInvalid('isAlignTop') || this.isInvalid('isAlignBottom') || this.isInvalid('isAlignVerticalCenter'));
  1079. },
  1080. };
  1081. exports.ready = function() {
  1082. this.resizeObserver = new window.ResizeObserver((entries) => {
  1083. window.requestAnimationFrame(() => {
  1084. // avoid error: ResizeObserver loop limit exceeded
  1085. if (!Array.isArray(entries) || !entries.length) {
  1086. return;
  1087. }
  1088. this.setLayout();
  1089. });
  1090. });
  1091. this.resizeObserver.observe(this.$this);
  1092. for (const key in uiElements) {
  1093. const element = uiElements[key];
  1094. if (typeof element.ready === 'function') {
  1095. element.ready.call(this);
  1096. }
  1097. }
  1098. };
  1099. exports.update = function(dump) {
  1100. this.dump = dump;
  1101. this.dimensionHorizontal = this.getDimensionHorizontal();
  1102. this.dimensionVertical = this.getDimensionVertical();
  1103. this.setLayout();
  1104. if (!this.vm) {
  1105. this.vm = new Vue({
  1106. el: this.$.app,
  1107. data: this,
  1108. template,
  1109. components,
  1110. computed,
  1111. methods: exports.methods,
  1112. });
  1113. }
  1114. this.vm.update();
  1115. };
  1116. exports.close = function() {
  1117. this.resizeObserver.unobserve(this.$this);
  1118. };