glib-web 4.42.2 → 4.43.0

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 (248) hide show
  1. package/.github/workflows/lint.yml +29 -0
  2. package/.nycrc.json +7 -3
  3. package/AGENTS.md +3 -0
  4. package/action.js +2 -0
  5. package/actions/analytics/logEvent.js +2 -2
  6. package/actions/bottom_banners/close.js +1 -1
  7. package/actions/bottom_banners/open.js +1 -1
  8. package/actions/browsers/setLocation.js +16 -0
  9. package/actions/cables/push.js +1 -1
  10. package/actions/components/invoke.js +1 -1
  11. package/actions/components/replace.js +1 -1
  12. package/actions/components/replaceChildren.js +1 -1
  13. package/actions/components/set.js +1 -1
  14. package/actions/dialogs/close.js +0 -2
  15. package/actions/fields/getValues.js +3 -7
  16. package/actions/http/patch.js +1 -1
  17. package/actions/http/post.js +1 -1
  18. package/actions/http/put.js +1 -1
  19. package/actions/http/retry.js +1 -1
  20. package/actions/logics/set.js +0 -11
  21. package/actions/popovers/open.js +0 -1
  22. package/actions/sheets/show.js +2 -2
  23. package/actions/timeouts/clear.js +6 -2
  24. package/actions/timeouts/set.js +5 -1
  25. package/actions/tours/stop.js +1 -1
  26. package/actions/windows/open.js +1 -1
  27. package/actions/windows/print.js +1 -1
  28. package/agent/{glib_test_sync.yaml → commands/generate_test.yaml} +3 -2
  29. package/agent/commands/scan_state_issue.yaml +64 -0
  30. package/app.vue +33 -27
  31. package/components/_avatar.vue +6 -6
  32. package/components/_badge.vue +7 -5
  33. package/components/_button.vue +2 -0
  34. package/components/_chip.vue +6 -5
  35. package/components/_dropdownMenu.vue +10 -15
  36. package/components/_icon.vue +4 -2
  37. package/components/_internal_button.vue +2 -0
  38. package/components/_internal_icon.vue +3 -3
  39. package/components/_message.vue +3 -1
  40. package/components/avatar.vue +2 -0
  41. package/components/banners/alert.vue +2 -0
  42. package/components/banners/select.vue +6 -4
  43. package/components/{mixins/events.js → base/eventsBase.js} +16 -26
  44. package/components/base/glibBase.js +10 -0
  45. package/components/button.vue +2 -0
  46. package/components/calendar.vue +11 -4
  47. package/components/charts/area.vue +8 -0
  48. package/components/charts/column.vue +8 -0
  49. package/components/charts/line.vue +8 -0
  50. package/components/charts/pie.vue +8 -0
  51. package/components/chip.vue +2 -0
  52. package/components/component.vue +25 -29
  53. package/components/composable/form.js +10 -5
  54. package/components/composable/inputVariant.js +43 -0
  55. package/components/composable/listAutoload.js +240 -0
  56. package/components/composable/socket.js +2 -2
  57. package/components/composable/tableAutoload.js +182 -0
  58. package/components/composable/tableExport.js +84 -0
  59. package/components/composable/tableImport.js +163 -0
  60. package/components/composable/tooltip.js +100 -0
  61. package/components/composable/upload.js +2 -2
  62. package/components/composable/upload_delegator.js +2 -2
  63. package/components/composable/upload_nothing.js +3 -3
  64. package/components/datetime.vue +18 -1
  65. package/components/fab.vue +2 -0
  66. package/components/fields/_buttonDate.vue +24 -18
  67. package/components/fields/_dynamicGroupEntry2.vue +3 -0
  68. package/components/fields/_patternText.vue +8 -6
  69. package/components/fields/_select.vue +23 -20
  70. package/components/fields/_selectItemDefault.vue +10 -2
  71. package/components/fields/_selectItemWithIcon.vue +9 -1
  72. package/components/fields/_selectItemWithImage.vue +11 -3
  73. package/components/fields/check/_featured.vue +12 -1
  74. package/components/fields/check/_thumbnail.vue +2 -0
  75. package/components/fields/check.vue +32 -22
  76. package/components/fields/checkGroup.vue +26 -16
  77. package/components/fields/chipGroup.vue +8 -5
  78. package/components/fields/creditCard.vue +15 -4
  79. package/components/fields/date.vue +2 -0
  80. package/components/fields/datetime.vue +2 -0
  81. package/components/fields/dynamicGroup2.vue +27 -16
  82. package/components/fields/dynamicSelect.vue +21 -15
  83. package/components/fields/file.vue +27 -8
  84. package/components/fields/googlePlace.vue +5 -3
  85. package/components/fields/hidden.vue +4 -7
  86. package/components/fields/inputUpload.vue +11 -3
  87. package/components/fields/location.vue +13 -11
  88. package/components/fields/multiUpload.vue +16 -14
  89. package/components/fields/newRichText.vue +15 -5
  90. package/components/fields/otpField.vue +13 -10
  91. package/components/fields/phone/field.vue +15 -7
  92. package/components/fields/phone.vue +17 -12
  93. package/components/fields/placeholderUpload.vue +10 -2
  94. package/components/fields/radio/_featured.vue +5 -3
  95. package/components/fields/radio/_subtitle.vue +5 -3
  96. package/components/fields/radio/_thumbnail.vue +3 -1
  97. package/components/fields/radio.vue +10 -8
  98. package/components/fields/radioGroup.vue +8 -6
  99. package/components/fields/rating.vue +2 -0
  100. package/components/fields/rawText.vue +8 -0
  101. package/components/fields/richText2.vue +35 -17
  102. package/components/fields/sign.vue +40 -32
  103. package/components/fields/stripe/stripeFields.vue +29 -15
  104. package/components/fields/stripe/stripeIndividualFields.vue +41 -27
  105. package/components/fields/stripeExternalAccount.vue +29 -5
  106. package/components/fields/stripeToken.vue +3 -1
  107. package/components/fields/submit.vue +7 -5
  108. package/components/fields/text.vue +11 -8
  109. package/components/fields/textarea.vue +8 -5
  110. package/components/fields/timeZone.vue +2 -0
  111. package/components/fields/timer.vue +15 -3
  112. package/components/fields/upload.vue +11 -3
  113. package/components/h1.vue +2 -0
  114. package/components/h2.vue +2 -0
  115. package/components/h3.vue +2 -0
  116. package/components/h4.vue +2 -0
  117. package/components/h5.vue +2 -0
  118. package/components/h6.vue +2 -0
  119. package/components/hr.vue +2 -0
  120. package/components/html.vue +2 -0
  121. package/components/icon.vue +3 -1
  122. package/components/image.vue +16 -5
  123. package/components/label.vue +2 -0
  124. package/components/map.vue +8 -3
  125. package/components/markdown.vue +25 -10
  126. package/components/mixins/generic.js +27 -28
  127. package/components/mixins/scrolling.js +5 -0
  128. package/components/mixins/styles.js +5 -3
  129. package/components/multimedia/video.vue +4 -4
  130. package/components/p.vue +2 -0
  131. package/components/panels/association.vue +3 -1
  132. package/components/panels/bulkEdit2.vue +41 -33
  133. package/components/panels/carousel.vue +8 -22
  134. package/components/panels/column.vue +45 -69
  135. package/components/panels/custom.vue +3 -1
  136. package/components/panels/flow.vue +39 -42
  137. package/components/panels/form.vue +9 -4
  138. package/components/panels/grid.vue +3 -1
  139. package/components/panels/horizontal.vue +23 -6
  140. package/components/panels/list.vue +13 -7
  141. package/components/panels/pagination.vue +23 -4
  142. package/components/panels/scroll.vue +3 -1
  143. package/components/panels/split.vue +3 -1
  144. package/components/panels/table.vue +39 -13
  145. package/components/panels/timeline.vue +19 -8
  146. package/components/panels/tree/standard.vue +21 -13
  147. package/components/panels/tree.vue +14 -6
  148. package/components/panels/ul.vue +3 -1
  149. package/components/panels/vertical.vue +4 -4
  150. package/components/panels/web.vue +2 -0
  151. package/components/popover.vue +4 -2
  152. package/components/progressCircle.vue +5 -3
  153. package/components/progressbar.vue +2 -0
  154. package/components/responsive.vue +3 -1
  155. package/components/shareButton.vue +19 -2
  156. package/components/skeleton.vue +8 -0
  157. package/components/skeletons/commentList.vue +1 -1
  158. package/components/spacer.vue +2 -0
  159. package/components/switch.vue +12 -2
  160. package/components/tabBar.vue +18 -7
  161. package/components/treeView.vue +64 -62
  162. package/components/validation.js +1 -2
  163. package/cypress/component/inputUpload.cy.ts +103 -0
  164. package/cypress/component/multiUpload.cy.ts +107 -0
  165. package/cypress/component/placeholderUpload.cy.ts +91 -0
  166. package/cypress/component/testUtils.ts +74 -0
  167. package/cypress/e2e/glib-web/auth.cy.ts +33 -0
  168. package/cypress/e2e/glib-web/browsers.cy.ts +14 -4
  169. package/cypress/e2e/glib-web/calendar.cy.ts +4 -4
  170. package/cypress/e2e/glib-web/carousel.cy.ts +7 -7
  171. package/cypress/e2e/glib-web/column.cy.ts +4 -4
  172. package/cypress/e2e/glib-web/commands.cy.ts +5 -5
  173. package/cypress/e2e/glib-web/components.cy.ts +18 -18
  174. package/cypress/e2e/glib-web/cookies.cy.ts +41 -0
  175. package/cypress/e2e/glib-web/custom.cy.ts +2 -2
  176. package/cypress/e2e/glib-web/fields.cy.ts +52 -0
  177. package/cypress/e2e/glib-web/fieldsDynamicSelect.cy.ts +81 -0
  178. package/cypress/e2e/glib-web/fieldsSelect.cy.ts +82 -0
  179. package/cypress/e2e/glib-web/fieldsSign.cy.ts +54 -0
  180. package/cypress/e2e/glib-web/fieldsTimer.cy.ts +39 -0
  181. package/cypress/e2e/glib-web/fieldsUpload.cy.ts +48 -0
  182. package/cypress/e2e/glib-web/flow.cy.ts +4 -4
  183. package/cypress/e2e/glib-web/forms.cy.ts +4 -5
  184. package/cypress/e2e/glib-web/grid.cy.ts +2 -2
  185. package/cypress/e2e/glib-web/horizontal.cy.ts +10 -10
  186. package/cypress/e2e/glib-web/http.cy.ts +8 -8
  187. package/cypress/e2e/glib-web/image.cy.ts +21 -21
  188. package/cypress/e2e/glib-web/list.cy.ts +19 -4
  189. package/cypress/e2e/glib-web/multimediaVideo.cy.ts +6 -6
  190. package/cypress/e2e/glib-web/pagination.cy.ts +4 -4
  191. package/cypress/e2e/glib-web/panels.cy.ts +1 -1
  192. package/cypress/e2e/glib-web/panelsBulkEdit2.cy.ts +21 -0
  193. package/cypress/e2e/glib-web/popovers.cy.ts +4 -4
  194. package/cypress/e2e/glib-web/progressCircle.cy.ts +14 -14
  195. package/cypress/e2e/glib-web/responsive.cy.ts +3 -3
  196. package/cypress/e2e/glib-web/scroll.cy.ts +4 -4
  197. package/cypress/e2e/glib-web/sheets.cy.ts +4 -4
  198. package/cypress/e2e/glib-web/snackbars.cy.ts +6 -6
  199. package/cypress/e2e/glib-web/split.cy.ts +1 -1
  200. package/cypress/e2e/glib-web/storageItems.cy.ts +7 -7
  201. package/cypress/e2e/glib-web/table.cy.ts +13 -3
  202. package/cypress/e2e/glib-web/timeline.cy.ts +3 -3
  203. package/cypress/e2e/glib-web/timeouts.cy.ts +3 -3
  204. package/cypress/e2e/glib-web/ul.cy.ts +2 -2
  205. package/cypress/e2e/glib-web/vertical.cy.ts +10 -10
  206. package/cypress/e2e/glib-web/web.cy.ts +4 -4
  207. package/cypress/fixtures/bulk_edit.csv +3 -0
  208. package/cypress/fixtures/upload.txt +1 -0
  209. package/cypress/support/component.ts +1 -0
  210. package/cypress.config.ts +14 -1
  211. package/eslint-rules/index.js +264 -0
  212. package/eslint.config.js +70 -0
  213. package/index.js +2 -17
  214. package/nav/appBar.vue +4 -2
  215. package/nav/dialog.vue +14 -12
  216. package/nav/drawer.vue +15 -13
  217. package/nav/drawerButton.vue +2 -0
  218. package/nav/drawerLabel.vue +3 -0
  219. package/nav/layout.vue +3 -1
  220. package/nav/sheet.vue +5 -2
  221. package/nav/snackbar.vue +13 -4
  222. package/package.json +9 -3
  223. package/plugins/driverCustomBehavior.js +1 -1
  224. package/settings.json.example +9 -4
  225. package/templates/_menu.vue +2 -0
  226. package/templates/comment.vue +46 -59
  227. package/templates/editable.vue +9 -7
  228. package/templates/featured.vue +7 -5
  229. package/templates/thumbnail.vue +6 -4
  230. package/utils/app.js +4 -0
  231. package/utils/form.js +1 -1
  232. package/utils/format.js +2 -2
  233. package/utils/history.js +5 -1
  234. package/utils/http.js +3 -3
  235. package/utils/panels/breakpoint.js +105 -0
  236. package/utils/settings.js +1 -1
  237. package/.claude/settings.local.json +0 -24
  238. package/.eslintrc.js +0 -41
  239. package/components/mixins/inputVariant.js +0 -13
  240. package/components/mixins/list/autoload.js +0 -212
  241. package/components/mixins/table/autoload.js +0 -135
  242. package/components/mixins/table/export.js +0 -52
  243. package/components/mixins/table/import.js +0 -141
  244. package/components/mixins/tooltip.js +0 -58
  245. package/components/mixins/ws/phoenixSocket.js +0 -120
  246. package/components/panels/bulkEdit.vue +0 -342
  247. package/cypress/component/component.cy.ts +0 -423
  248. package/cypress/e2e/glib-web/fileUploadNew.cy.ts +0 -12
@@ -0,0 +1,29 @@
1
+ name: Lint
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "**"
7
+ pull_request:
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: "24"
20
+ cache: "yarn"
21
+
22
+ - name: Enable corepack
23
+ run: corepack enable
24
+
25
+ - name: Install dependencies
26
+ run: yarn install --frozen-lockfile
27
+
28
+ - name: Run lint
29
+ run: yarn lint
package/.nycrc.json CHANGED
@@ -26,8 +26,12 @@
26
26
  "utils/**"
27
27
  ],
28
28
  "exclude": [
29
- "actions/cables",
30
- "actions/analytics",
31
- "utils/private"
29
+ "actions/cables/*",
30
+ "actions/analytics/*",
31
+ "utils/private/*",
32
+ "extensions/*",
33
+ "components/treeView.vue",
34
+ "actions/files/*",
35
+ "actions/bottom_banners/*"
32
36
  ]
33
37
  }
package/AGENTS.md CHANGED
@@ -12,6 +12,9 @@
12
12
  - For working examples on how to use `glib` UI components, see all the jbuilder
13
13
  files located in `doc/garage/`.
14
14
 
15
+ ## Agent commands
16
+ - Available agent command definitions live in `agent/commands/` (see the `.yaml` files there).
17
+
15
18
  ## Behaviour and tendency
16
19
  - Read [behavior.md](doc/common/ai/behavior.md)
17
20
 
package/action.js CHANGED
@@ -87,6 +87,7 @@ import ActionBottomBannersClose from "./actions/bottom_banners/close";
87
87
 
88
88
  import ActionDetectCountry from "./actions/browsers/detectCountry";
89
89
  import ActionDetectTimezone from "./actions/browsers/detectTimezone";
90
+ import ActionSetLocation from "./actions/browsers/setLocation";
90
91
 
91
92
  import ActionsCookiesRemove from "./actions/cookies/remove";
92
93
  import ActionsCookiesClear from "./actions/cookies/clear";
@@ -190,6 +191,7 @@ const actions = {
190
191
 
191
192
  "browsers/detectCountry": ActionDetectCountry,
192
193
  "browsers/detectTimezone": ActionDetectTimezone,
194
+ "browsers/setLocation": ActionSetLocation,
193
195
 
194
196
  "cookies/remove": ActionsCookiesRemove,
195
197
  "cookies/clear": ActionsCookiesClear,
@@ -1,9 +1,9 @@
1
1
  export default class {
2
- logDisabled(spec) {
2
+ logDisabled() {
3
3
  return true;
4
4
  }
5
5
 
6
- execute(properties, component) {
6
+ execute(properties) {
7
7
  if (Utils.settings.isDev) {
8
8
  return;
9
9
  }
@@ -1,7 +1,7 @@
1
1
  import { vueApp } from "../../store";
2
2
 
3
3
  export default class {
4
- execute(properties, component) {
4
+ execute(properties) {
5
5
  const { targetId } = properties;
6
6
 
7
7
  delete vueApp.bottomBanners[targetId];
@@ -1,7 +1,7 @@
1
1
  import { vueApp } from "../../store";
2
2
 
3
3
  export default class {
4
- execute(properties, component) {
4
+ execute(properties) {
5
5
  const { targetId, newView } = properties;
6
6
 
7
7
  if (!targetId) console.error('targetId is required');
@@ -0,0 +1,16 @@
1
+ import Action from "../../action";
2
+ import { jsonView } from "../../store";
3
+ import history from "../../utils/history";
4
+
5
+ export default class {
6
+ execute(spec, component) {
7
+ Utils.type.ifString(spec.url, (url) => {
8
+ if (spec.replace) {
9
+ history.updatePage(jsonView.page, url);
10
+ } else {
11
+ history.pushPage(jsonView.page, url);
12
+ }
13
+ Action.execute(spec.onSet, component);
14
+ });
15
+ }
16
+ }
@@ -2,7 +2,7 @@ import { channels } from "../../components/composable/socket";
2
2
 
3
3
 
4
4
  export default class {
5
- execute(properties, component) {
5
+ execute(properties) {
6
6
  const { event, payload, channel } = properties;
7
7
  const ch = channels[channel];
8
8
 
@@ -2,7 +2,7 @@ import Action from "../../action";
2
2
  import { realComponent } from "../../components/helper";
3
3
 
4
4
  export default class {
5
- execute(spec, component) {
5
+ execute(spec) {
6
6
  const target = realComponent(GLib.component.findById(spec.targetId));
7
7
 
8
8
  if (!target) {
@@ -2,7 +2,7 @@ import Action from "../../action";
2
2
 
3
3
  // Experimental. Can this be merged with `components_set` ?
4
4
  export default class {
5
- execute(spec, component) {
5
+ execute(spec) {
6
6
  let target = GLib.component.findById(spec.targetId);
7
7
 
8
8
  if (!target) {
@@ -3,7 +3,7 @@ import Action from "../../action";
3
3
 
4
4
  // Experimental
5
5
  export default class {
6
- execute(spec, component) {
6
+ execute(spec) {
7
7
  const target = GLib.component.findById(spec.targetId);
8
8
 
9
9
  if (!target) {
@@ -1,7 +1,7 @@
1
1
  import { realComponent } from "../../components/helper";
2
2
 
3
3
  export default class {
4
- execute(spec, component) {
4
+ execute(spec) {
5
5
  let targetComponent = realComponent(GLib.component.findById(spec.targetId));
6
6
 
7
7
  if (!targetComponent) {
@@ -1,5 +1,3 @@
1
- import { dialogs } from "../../store";
2
-
3
1
  export default class {
4
2
  execute(spec, component) {
5
3
  Utils.launch.dialog.close(spec, component);
@@ -1,13 +1,9 @@
1
1
  import Action from "../../action";
2
- import { getFormData } from "../../components/composable/form";
3
- import { isPresent } from "../../utils/type";
2
+ import { getAllFormData } from "../../components/composable/form";
4
3
 
5
4
  export default class {
6
5
  execute(spec, component) {
7
- const inputEl = component.$el.querySelector('input,select,textarea');
8
- const formEl = inputEl.form;
9
-
10
- const formData = getFormData(formEl);
6
+ const formData = getAllFormData();
11
7
  // const values = spec.fieldNames.reduce((prev, curr) => {
12
8
  // Object.assign(prev, { [curr]: formData[curr] });
13
9
  // return prev;
@@ -20,7 +16,7 @@ export default class {
20
16
  }, {});
21
17
 
22
18
  Utils.type.ifObject(spec.onGet, onGet => {
23
- Action.executeWithFormData(onGet, component, values)
19
+ Action.executeWithFormData(onGet, component, values);
24
20
  });
25
21
  }
26
22
  }
@@ -1,7 +1,7 @@
1
1
  import { httpExecuteWithRetry } from "./retry";
2
2
 
3
3
  export default class {
4
- execute(properties, controller, params) {
4
+ execute(properties, controller) {
5
5
  httpExecuteWithRetry('PATCH', properties, controller);
6
6
  }
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { httpExecuteWithRetry } from "./retry";
2
2
 
3
3
  export default class {
4
- execute(properties, controller, params) {
4
+ execute(properties, controller) {
5
5
  httpExecuteWithRetry('POST', properties, controller);
6
6
  }
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { httpExecuteWithRetry } from "./retry";
2
2
 
3
3
  export default class {
4
- execute(properties, controller, params) {
4
+ execute(properties, controller) {
5
5
  httpExecuteWithRetry('PUT', properties, controller);
6
6
  }
7
7
  }
@@ -59,7 +59,7 @@ const httpExecuteWithRetry = (methodName, spec, component) => {
59
59
  spec.retryLimit,
60
60
  undefined,
61
61
  undefined,
62
- err => {
62
+ () => {
63
63
  if (!spec?.silent) {
64
64
  Utils.launch.snackbar.error("We're having trouble right now. Please try again.", component);
65
65
  }
@@ -2,7 +2,6 @@ import jsonLogic from 'json-logic-js';
2
2
  import merge from 'lodash.merge';
3
3
  import { nextTick } from "vue";
4
4
  import { sanitize } from "../../components/composable/date";
5
- import { htmlElement } from "../../components/helper";
6
5
  import { isPresent } from "../../utils/type";
7
6
  import { getFormData as _getFormData } from "../../components/composable/form";
8
7
 
@@ -95,16 +94,6 @@ export default class {
95
94
  if (!targetComponent) {
96
95
  console.warn("Component ID not found", id);
97
96
  }
98
- Utils.type.ifObject(targetComponent, (component) => {
99
- const element = htmlElement(component);
100
- Utils.type.ifObject(element, (el) => {
101
- Utils.type.ifFunction(el.closest, () => {
102
- if (!el.closest('form')) {
103
- console.warn("Target component is not inside a form", component.viewId || id);
104
- }
105
- });
106
- });
107
- });
108
97
  return targetComponent;
109
98
  }).filter((comp) => comp);
110
99
 
@@ -1,4 +1,3 @@
1
- import eventBus from "../../utils/eventBus";
2
1
  import http from "../../utils/http";
3
2
  import { closeAllExcept, isExist } from "./helper";
4
3
 
@@ -1,7 +1,7 @@
1
1
  import { vueApp } from "../../store";
2
2
 
3
3
  export default class {
4
- execute(spec, component) {
4
+ execute(spec) {
5
5
 
6
6
  const { body, placement, scrim, elevation } = spec;
7
7
 
@@ -13,4 +13,4 @@ export default class {
13
13
  elevation
14
14
  };
15
15
  }
16
- }
16
+ }
@@ -1,11 +1,15 @@
1
1
  import { timerIds } from "./state";
2
2
 
3
3
  export default class {
4
- execute(properties, component) {
4
+ execute(properties) {
5
5
  const { repeat, timerId } = properties;
6
6
 
7
7
  if (timerIds[timerId]) {
8
- repeat ? clearInterval(timerIds[timerId]) : clearTimeout(timerIds[timerId]);
8
+ if (repeat) {
9
+ clearInterval(timerIds[timerId]);
10
+ } else {
11
+ clearTimeout(timerIds[timerId]);
12
+ }
9
13
  delete timerIds[timerId];
10
14
  }
11
15
  }
@@ -6,7 +6,11 @@ export default class {
6
6
 
7
7
  // clear timeout/interval if there is same timerId exist
8
8
  if (timerId && timerIds[timerId]) {
9
- repeat ? clearInterval(timerIds[timerId]) : clearTimeout(timerIds[timerId]);
9
+ if (repeat) {
10
+ clearInterval(timerIds[timerId]);
11
+ } else {
12
+ clearTimeout(timerIds[timerId]);
13
+ }
10
14
  delete timerIds[timerId];
11
15
  }
12
16
 
@@ -1,7 +1,7 @@
1
1
  import tour from "./start.js";
2
2
 
3
3
  export default class {
4
- execute(spec, component) {
4
+ execute() {
5
5
  tour.stopTour();
6
6
  }
7
7
  }
@@ -1,4 +1,4 @@
1
- import { executeGlibEvent, vueApp } from "../../store";
1
+ import { executeGlibEvent } from "../../store";
2
2
  import { showSkeletionViewIfExist } from "./skeletionView";
3
3
 
4
4
  export default class {
@@ -1,5 +1,5 @@
1
1
  export default class {
2
- execute(properties, component) {
2
+ execute() {
3
3
  window.print();
4
4
  }
5
5
  }
@@ -19,6 +19,7 @@ inputs:
19
19
  - Click every button defined in {{doc_page_path}} (include nested buttons in dialogs/sheets/menus).
20
20
  - For each button click, assert the expected result implied by its onClick action.
21
21
  - Use one or more it blocks; revisit the page when state needs a reset.
22
+ - Prefer cy.contains('BUTTON_TEXT') over cy.contains('button', 'BUTTON_TEXT').
22
23
  - Match existing project patterns (describe/it, cy.visit, selectors, etc).
23
24
  - Use ASCII only.
24
25
  fallback_test_template: |
@@ -44,7 +45,7 @@ inputs:
44
45
 
45
46
  cy.wrap(labels).each((label) => {
46
47
  cy.visit(url)
47
- cy.contains('button', label).click({ force: true })
48
+ cy.contains(label).click({ force: true })
48
49
  cy.location('href').then((hrefAfter) => {
49
50
  if (hrefAfter !== url) {
50
51
  expect(hrefAfter).to.not.eq(url)
@@ -55,7 +56,7 @@ inputs:
55
56
  if ($body.find(overlaySelectors).length) {
56
57
  cy.get(overlaySelectors).should('exist')
57
58
  } else {
58
- cy.contains('button', label).should('exist')
59
+ cy.contains(label).should('exist')
59
60
  }
60
61
  })
61
62
  })
@@ -0,0 +1,64 @@
1
+ version: 1
2
+ name: glib-web-stale-state-scan
3
+ description: >
4
+ Scan glib-web components for stale state risks caused by spec updates via
5
+ components/set or logics/set, focusing on Vue2 mixins and Vue3 setup patterns.
6
+
7
+ inputs:
8
+ component_dir: components
9
+ actions_dir: actions
10
+ mixins_dir: components/mixins
11
+ utils_dir: utils
12
+ report_path: /tmp/glib-web-stale-state-scan/report.md
13
+
14
+ constraints:
15
+ - Use utils/type.js for any type checks in helper scripts.
16
+ - Use ASCII only in generated outputs.
17
+
18
+ steps:
19
+ - id: scan-setup-nonreactive
20
+ instruction: |
21
+ Find setup() blocks that read props.spec non-reactively.
22
+ Use rg to list matches for:
23
+ - "setup(" within {{inputs.component_dir}}
24
+ - "props.spec" or "ref(props.spec" or "= props.spec"
25
+ Output: setup_nonreactive_hits (file:line, matched_text).
26
+
27
+ - id: scan-options-data-created
28
+ instruction: |
29
+ Find Options API initialization that derives state once from spec.
30
+ Use rg for:
31
+ - "data()" or "created()" or "mounted()" within {{inputs.component_dir}}
32
+ - inspect for "this.spec" assignments in same file.
33
+ Output: options_init_hits (file:line, matched_text).
34
+
35
+ - id: scan-lifecycle-mixin-usage
36
+ instruction: |
37
+ Find components that implement $ready/$updated or rely on Vue2 mixins.
38
+ Use rg for "$ready(" or "$updated(" within {{inputs.component_dir}}.
39
+ Output: lifecycle_hook_hits (file:line, matched_text).
40
+
41
+ - id: scan-action-merge-usage
42
+ instruction: |
43
+ Identify where action_merge / components/set / logics/set are used.
44
+ Use rg for:
45
+ - "action_merge" within {{inputs.component_dir}} and {{inputs.actions_dir}}
46
+ - "components/set" or "logics/set" within {{inputs.actions_dir}}
47
+ Output: merge_action_hits (file:line, matched_text).
48
+
49
+ - id: correlate-risk
50
+ instruction: |
51
+ Correlate hits to flag components with stale-state risks:
52
+ - setup_nonreactive_hits are high risk.
53
+ - options_init_hits without watchers on spec are medium risk.
54
+ - lifecycle_hook_hits indicate reliance on $ready/$updated.
55
+ Produce a concise markdown report at {{inputs.report_path}} with:
56
+ - Summary counts by risk level.
57
+ - Tables of findings per risk level (file, line, reason).
58
+ - Note where components are likely affected by components/set/logics/set.
59
+ Output: report_path.
60
+
61
+ - id: review-report
62
+ instruction: |
63
+ Open {{inputs.report_path}} and verify the report is readable and concise.
64
+ Output: report_preview.
package/app.vue CHANGED
@@ -1,15 +1,15 @@
1
1
  <template>
2
- <v-app :class="pageClasses" ref="appRef">
2
+ <v-app ref="appRef" :class="pageClasses">
3
3
  <component :is="containerComponent" :spec="formSpec" :style="'height: 100%;'">
4
- <nav-layout ref="navBar" @mounted="updateMainHeight()" :page="page" :key="`navBar-${page.key}`" />
4
+ <nav-layout ref="navBar" :key="`navBar-${page.key}`" :page="page" @mounted="updateMainHeight()" />
5
5
 
6
- <v-progress-linear color="primary" v-if="vueApp.indicator" :indeterminate="true" height="5"
6
+ <v-progress-linear v-if="vueApp.indicator" color="primary" :indeterminate="true" height="5"
7
7
  style="position: fixed; z-index: 4">
8
8
  </v-progress-linear>
9
9
 
10
10
  <v-main :style="`height: ${mainHeight}px;`">
11
11
  <v-container v-show="!vueApp.loading" :fluid="page.template == 'fullWidth'" :class="containerClasses">
12
- <div class="pages-header" :key="`page-header-${page.key}`">
12
+ <div :key="`page-header-${page.key}`" class="pages-header">
13
13
  <panels-responsive :spec="header" />
14
14
  </div>
15
15
 
@@ -19,7 +19,7 @@
19
19
  <panels-responsive :key="`footer-${page.key}`" class="body-footer" :spec="bodyFooter" />
20
20
  </div>
21
21
 
22
- <div class="pages-footer" :key="`page-footer-${page.key}`">
22
+ <div :key="`page-footer-${page.key}`" class="pages-footer">
23
23
  <panels-responsive :spec="footer" />
24
24
  </div>
25
25
  </v-container>
@@ -48,8 +48,8 @@
48
48
  </v-sheet>
49
49
  </Transition>
50
50
  <div class="glib-bottomBanner">
51
- <glib-component v-for="(bottomBanner, index) in Object.values(vueApp.bottomBanners)" :spec="bottomBanner"
52
- :key="index"></glib-component>
51
+ <glib-component v-for="(bottomBanner, index) in Object.values(vueApp.bottomBanners)" :key="index"
52
+ :spec="bottomBanner"></glib-component>
53
53
  </div>
54
54
  </v-app>
55
55
  </template>
@@ -71,6 +71,14 @@ import { nextTick } from "vue";
71
71
 
72
72
  export default {
73
73
  expose: ['updateMainHeight'],
74
+ components: {
75
+ "nav-layout": NavLayout,
76
+ "panels-form": FormPanel,
77
+ },
78
+ // mixins: [phoenixSocketMixin, actionCableMixin],
79
+ props: {
80
+ page: { type: Object, required: true },
81
+ },
74
82
  setup(props) {
75
83
  const filePaster = computed(() => props.page.filePaster);
76
84
  usePasteable(filePaster);
@@ -105,14 +113,6 @@ export default {
105
113
 
106
114
  return { vueApp, tooltipId, appRef };
107
115
  },
108
- components: {
109
- "nav-layout": NavLayout,
110
- "panels-form": FormPanel,
111
- },
112
- // mixins: [phoenixSocketMixin, actionCableMixin],
113
- props: {
114
- page: { type: Object, required: true },
115
- },
116
116
  data() {
117
117
  return {
118
118
  title: "...",
@@ -121,6 +121,7 @@ export default {
121
121
  tabWasBlurred: false,
122
122
  onWindowFocus: null,
123
123
  onWindowBlur: null,
124
+ onWindowResize: null,
124
125
  };
125
126
  },
126
127
  computed: {
@@ -147,6 +148,7 @@ export default {
147
148
  },
148
149
  containerComponent() {
149
150
  if (this.formSpec) {
151
+ // eslint-disable-next-line vue/no-side-effects-in-computed-properties
150
152
  this.name = "panels-form";
151
153
  return "panels-form";
152
154
  }
@@ -161,13 +163,16 @@ export default {
161
163
  },
162
164
  },
163
165
  watch: {
164
- "vueApp.indicator": function (val, oldVal) {
166
+ "vueApp.indicator": function (val) {
165
167
  document.title = val ? "..." : this.page.title;
166
168
  },
167
169
  page: {
168
- handler(val, oldVal) {
170
+ handler(val) {
169
171
  if (!val) return;
170
172
  this.handleActionCable(val);
173
+ this.$nextTick(() => {
174
+ this.handlePageReady();
175
+ });
171
176
  },
172
177
  immediate: true
173
178
  }
@@ -187,6 +192,16 @@ export default {
187
192
  },
188
193
  beforeUnmount() {
189
194
  this.unbindForegroundListeners();
195
+ if (this.onWindowResize) {
196
+ window.removeEventListener("resize", this.onWindowResize, true);
197
+ }
198
+ },
199
+ mounted() {
200
+ this.onWindowResize = () => {
201
+ this.updateMainHeight();
202
+ };
203
+ window.addEventListener("resize", this.onWindowResize, true);
204
+ this.updateMainHeight();
190
205
  },
191
206
  methods: {
192
207
  closeSheet() {
@@ -228,16 +243,7 @@ export default {
228
243
  this.actionCableConsumer = null;
229
244
  }
230
245
  },
231
- $mounted() {
232
- window.addEventListener(
233
- "resize",
234
- (event) => {
235
- this.updateMainHeight();
236
- },
237
- true
238
- );
239
- },
240
- $ready() {
246
+ handlePageReady() {
241
247
  this.$type.ifString(this.page.title, (title) => {
242
248
  document.title = title;
243
249
  });
@@ -1,26 +1,26 @@
1
1
  <template>
2
2
  <common-badge :spec="spec">
3
3
  <v-avatar :size="spec.size" :color="spec.backgroundColor || 'surface-variant'" :class="cssClasses">
4
- <p class="initials" v-if="spec.initials" :style="{ color: spec.initials.color || 'white' }">{{ spec.initials.text }}</p>
4
+ <p v-if="spec.initials" class="initials" :style="{ color: spec.initials.color || 'white' }">{{ spec.initials.text }}</p>
5
5
  <!-- Use `img` instead of `v-img` otherwise the rounded border will not work. -->
6
6
  <!-- <img :style="$styles()" :src="spec.url || spec.base64Data" @click="$onClick()" /> -->
7
- <v-img :style="$styles()" :class="$classes()" :src="spec.url || spec.base64Data" @click="$onClick()"
8
- v-else></v-img>
7
+ <v-img v-else :style="$styles()" :class="$classes()" :src="spec.url || spec.base64Data"
8
+ @click="$onClick()"></v-img>
9
9
  </v-avatar>
10
10
  </common-badge>
11
11
  </template>
12
12
 
13
13
  <script>
14
+ import GlibBase from "./base/glibBase.js";
14
15
  export default {
16
+ extends: GlibBase,
15
17
  props: {
16
18
  spec: { type: Object, required: true }
17
19
  },
18
20
  computed: {
19
21
  cssClasses() {
20
22
  // Icons are nameless when used in other components, e.g. buttons
21
- this.spec.view = this.spec.view || "avatar";
22
-
23
- return this.$classes();
23
+ return this.$classes(this.spec, "avatar");
24
24
  },
25
25
  },
26
26
  };
@@ -1,19 +1,21 @@
1
1
  <template>
2
2
  <v-badge :color="badge.backgroundColor || 'red'" overlap :model-value="badgeExists()" :style="styles()">
3
- <template v-slot:badge>{{ badge.text }}</template>
3
+ <template #badge>{{ badge.text }}</template>
4
4
  <slot />
5
5
  </v-badge>
6
6
  </template>
7
7
 
8
8
  <script>
9
+ import GlibBase from "./base/glibBase.js";
9
10
  export default {
11
+ extends: GlibBase,
10
12
  props: {
11
13
  spec: { type: Object, required: true }
12
14
  },
13
- data() {
14
- return {
15
- badge: this.spec.badge || {}
16
- };
15
+ computed: {
16
+ badge() {
17
+ return this.spec.badge || {};
18
+ }
17
19
  },
18
20
  methods: {
19
21
  badgeExists() {
@@ -6,12 +6,14 @@
6
6
  </template>
7
7
 
8
8
  <script>
9
+ import GlibBase from "./base/glibBase.js";
9
10
  import InternalButton from "./_internal_button.vue";
10
11
 
11
12
  export default {
12
13
  components: {
13
14
  "internal-button": InternalButton,
14
15
  },
16
+ extends: GlibBase,
15
17
  props: {
16
18
  spec: { type: Object, required: true }
17
19
  },