@fieldia/designer 0.19.1 → 0.21.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 (305) hide show
  1. package/package.json +4 -4
  2. package/src/lib/canvas-head.d.mts +22 -0
  3. package/src/lib/canvas-head.d.ts +22 -0
  4. package/src/lib/canvas-head.js +121 -0
  5. package/src/lib/canvas-head.js.map +1 -0
  6. package/src/lib/canvas-head.mjs +118 -0
  7. package/src/lib/canvas-head.mjs.map +1 -0
  8. package/src/lib/canvas-header.js +7 -1
  9. package/src/lib/canvas-header.js.map +1 -1
  10. package/src/lib/canvas-header.mjs +7 -1
  11. package/src/lib/canvas-header.mjs.map +1 -1
  12. package/src/lib/choice-commands.d.mts +2 -1
  13. package/src/lib/choice-commands.d.ts +2 -1
  14. package/src/lib/choice-commands.js +3 -0
  15. package/src/lib/choice-commands.js.map +1 -1
  16. package/src/lib/choice-commands.mjs +3 -0
  17. package/src/lib/choice-commands.mjs.map +1 -1
  18. package/src/lib/chrome.js +11 -0
  19. package/src/lib/chrome.js.map +1 -1
  20. package/src/lib/chrome.mjs +11 -0
  21. package/src/lib/chrome.mjs.map +1 -1
  22. package/src/lib/date-setting.d.mts +49 -0
  23. package/src/lib/date-setting.d.ts +49 -0
  24. package/src/lib/date-setting.js +125 -0
  25. package/src/lib/date-setting.js.map +1 -0
  26. package/src/lib/date-setting.mjs +122 -0
  27. package/src/lib/date-setting.mjs.map +1 -0
  28. package/src/lib/designer.d.mts +16 -4
  29. package/src/lib/designer.d.ts +16 -4
  30. package/src/lib/designer.js +25 -2
  31. package/src/lib/designer.js.map +1 -1
  32. package/src/lib/designer.mjs +25 -2
  33. package/src/lib/designer.mjs.map +1 -1
  34. package/src/lib/group-fold.js +2 -0
  35. package/src/lib/group-fold.js.map +1 -1
  36. package/src/lib/group-fold.mjs +2 -0
  37. package/src/lib/group-fold.mjs.map +1 -1
  38. package/src/lib/head-commands.d.mts +116 -0
  39. package/src/lib/head-commands.d.ts +116 -0
  40. package/src/lib/head-commands.js +504 -0
  41. package/src/lib/head-commands.js.map +1 -0
  42. package/src/lib/head-commands.mjs +497 -0
  43. package/src/lib/head-commands.mjs.map +1 -0
  44. package/src/lib/head-parts.d.mts +15 -0
  45. package/src/lib/head-parts.d.ts +15 -0
  46. package/src/lib/head-parts.js +69 -0
  47. package/src/lib/head-parts.js.map +1 -0
  48. package/src/lib/head-parts.mjs +64 -0
  49. package/src/lib/head-parts.mjs.map +1 -0
  50. package/src/lib/head-properties.d.mts +25 -0
  51. package/src/lib/head-properties.d.ts +25 -0
  52. package/src/lib/head-properties.js +383 -0
  53. package/src/lib/head-properties.js.map +1 -0
  54. package/src/lib/head-properties.mjs +376 -0
  55. package/src/lib/head-properties.mjs.map +1 -0
  56. package/src/lib/header-commands.js +7 -0
  57. package/src/lib/header-commands.js.map +1 -1
  58. package/src/lib/header-commands.mjs +7 -0
  59. package/src/lib/header-commands.mjs.map +1 -1
  60. package/src/lib/header-properties.js +5 -1
  61. package/src/lib/header-properties.js.map +1 -1
  62. package/src/lib/header-properties.mjs +5 -1
  63. package/src/lib/header-properties.mjs.map +1 -1
  64. package/src/lib/inline-settings.js +19 -11
  65. package/src/lib/inline-settings.js.map +1 -1
  66. package/src/lib/inline-settings.mjs +19 -11
  67. package/src/lib/inline-settings.mjs.map +1 -1
  68. package/src/lib/input-commands.js +3 -2
  69. package/src/lib/input-commands.js.map +1 -1
  70. package/src/lib/input-commands.mjs +3 -2
  71. package/src/lib/input-commands.mjs.map +1 -1
  72. package/src/lib/kind-settings-choices.js +16 -7
  73. package/src/lib/kind-settings-choices.js.map +1 -1
  74. package/src/lib/kind-settings-choices.mjs +16 -7
  75. package/src/lib/kind-settings-choices.mjs.map +1 -1
  76. package/src/lib/kind-settings-inputs.js +116 -58
  77. package/src/lib/kind-settings-inputs.js.map +1 -1
  78. package/src/lib/kind-settings-inputs.mjs +116 -58
  79. package/src/lib/kind-settings-inputs.mjs.map +1 -1
  80. package/src/lib/kind-settings-picker.d.mts +4 -0
  81. package/src/lib/kind-settings-picker.d.ts +4 -0
  82. package/src/lib/kind-settings-picker.js +115 -0
  83. package/src/lib/kind-settings-picker.js.map +1 -0
  84. package/src/lib/kind-settings-picker.mjs +112 -0
  85. package/src/lib/kind-settings-picker.mjs.map +1 -0
  86. package/src/lib/kind-settings-structures.js +35 -16
  87. package/src/lib/kind-settings-structures.js.map +1 -1
  88. package/src/lib/kind-settings-structures.mjs +36 -17
  89. package/src/lib/kind-settings-structures.mjs.map +1 -1
  90. package/src/lib/kind-settings.js +47 -43
  91. package/src/lib/kind-settings.js.map +1 -1
  92. package/src/lib/kind-settings.mjs +47 -43
  93. package/src/lib/kind-settings.mjs.map +1 -1
  94. package/src/lib/layout-settings.d.mts +7 -0
  95. package/src/lib/layout-settings.d.ts +7 -0
  96. package/src/lib/layout-settings.js +15 -2
  97. package/src/lib/layout-settings.js.map +1 -1
  98. package/src/lib/layout-settings.mjs +15 -2
  99. package/src/lib/layout-settings.mjs.map +1 -1
  100. package/src/lib/list-canvas.js +18 -7
  101. package/src/lib/list-canvas.js.map +1 -1
  102. package/src/lib/list-canvas.mjs +18 -7
  103. package/src/lib/list-canvas.mjs.map +1 -1
  104. package/src/lib/list-commands.d.mts +24 -1
  105. package/src/lib/list-commands.d.ts +24 -1
  106. package/src/lib/list-commands.js +105 -2
  107. package/src/lib/list-commands.js.map +1 -1
  108. package/src/lib/list-commands.mjs +102 -1
  109. package/src/lib/list-commands.mjs.map +1 -1
  110. package/src/lib/list-properties.js +248 -28
  111. package/src/lib/list-properties.js.map +1 -1
  112. package/src/lib/list-properties.mjs +248 -28
  113. package/src/lib/list-properties.mjs.map +1 -1
  114. package/src/lib/locales/ar/head.d.mts +2 -0
  115. package/src/lib/locales/ar/head.d.ts +2 -0
  116. package/src/lib/locales/ar/head.js +97 -0
  117. package/src/lib/locales/ar/head.js.map +1 -0
  118. package/src/lib/locales/ar/head.mjs +94 -0
  119. package/src/lib/locales/ar/head.mjs.map +1 -0
  120. package/src/lib/locales/ar/index.js +4 -1
  121. package/src/lib/locales/ar/index.js.map +1 -1
  122. package/src/lib/locales/ar/index.mjs +4 -1
  123. package/src/lib/locales/ar/index.mjs.map +1 -1
  124. package/src/lib/locales/ar/inputs.d.mts +2 -0
  125. package/src/lib/locales/ar/inputs.d.ts +2 -0
  126. package/src/lib/locales/ar/inputs.js +20 -0
  127. package/src/lib/locales/ar/inputs.js.map +1 -0
  128. package/src/lib/locales/ar/inputs.mjs +17 -0
  129. package/src/lib/locales/ar/inputs.mjs.map +1 -0
  130. package/src/lib/locales/ar/list.js +31 -0
  131. package/src/lib/locales/ar/list.js.map +1 -1
  132. package/src/lib/locales/ar/list.mjs +31 -0
  133. package/src/lib/locales/ar/list.mjs.map +1 -1
  134. package/src/lib/locales/ar/panel.js +38 -0
  135. package/src/lib/locales/ar/panel.js.map +1 -1
  136. package/src/lib/locales/ar/panel.mjs +38 -0
  137. package/src/lib/locales/ar/panel.mjs.map +1 -1
  138. package/src/lib/locales/ar/questions.js +8 -0
  139. package/src/lib/locales/ar/questions.js.map +1 -1
  140. package/src/lib/locales/ar/questions.mjs +8 -0
  141. package/src/lib/locales/ar/questions.mjs.map +1 -1
  142. package/src/lib/locales/ar/refusals.js +11 -0
  143. package/src/lib/locales/ar/refusals.js.map +1 -1
  144. package/src/lib/locales/ar/refusals.mjs +11 -0
  145. package/src/lib/locales/ar/refusals.mjs.map +1 -1
  146. package/src/lib/locales/ar/work-text.d.mts +3 -0
  147. package/src/lib/locales/ar/work-text.d.ts +3 -0
  148. package/src/lib/locales/ar/work-text.js +58 -0
  149. package/src/lib/locales/ar/work-text.js.map +1 -0
  150. package/src/lib/locales/ar/work-text.mjs +55 -0
  151. package/src/lib/locales/ar/work-text.mjs.map +1 -0
  152. package/src/lib/locales/en/head.d.mts +101 -0
  153. package/src/lib/locales/en/head.d.ts +101 -0
  154. package/src/lib/locales/en/head.js +106 -0
  155. package/src/lib/locales/en/head.js.map +1 -0
  156. package/src/lib/locales/en/head.mjs +103 -0
  157. package/src/lib/locales/en/head.mjs.map +1 -0
  158. package/src/lib/locales/en/index.d.mts +237 -0
  159. package/src/lib/locales/en/index.d.ts +237 -0
  160. package/src/lib/locales/en/index.js +4 -1
  161. package/src/lib/locales/en/index.js.map +1 -1
  162. package/src/lib/locales/en/index.mjs +4 -1
  163. package/src/lib/locales/en/index.mjs.map +1 -1
  164. package/src/lib/locales/en/inputs.d.mts +26 -0
  165. package/src/lib/locales/en/inputs.d.ts +26 -0
  166. package/src/lib/locales/en/inputs.js +30 -0
  167. package/src/lib/locales/en/inputs.js.map +1 -0
  168. package/src/lib/locales/en/inputs.mjs +27 -0
  169. package/src/lib/locales/en/inputs.mjs.map +1 -0
  170. package/src/lib/locales/en/list.d.mts +48 -0
  171. package/src/lib/locales/en/list.d.ts +48 -0
  172. package/src/lib/locales/en/list.js +33 -0
  173. package/src/lib/locales/en/list.js.map +1 -1
  174. package/src/lib/locales/en/list.mjs +33 -0
  175. package/src/lib/locales/en/list.mjs.map +1 -1
  176. package/src/lib/locales/en/panel.d.mts +6 -0
  177. package/src/lib/locales/en/panel.d.ts +6 -0
  178. package/src/lib/locales/en/panel.js +3 -0
  179. package/src/lib/locales/en/panel.js.map +1 -1
  180. package/src/lib/locales/en/panel.mjs +3 -0
  181. package/src/lib/locales/en/panel.mjs.map +1 -1
  182. package/src/lib/locales/en/questions.d.mts +8 -0
  183. package/src/lib/locales/en/questions.d.ts +8 -0
  184. package/src/lib/locales/en/questions.js +9 -0
  185. package/src/lib/locales/en/questions.js.map +1 -1
  186. package/src/lib/locales/en/questions.mjs +9 -0
  187. package/src/lib/locales/en/questions.mjs.map +1 -1
  188. package/src/lib/locales/en/refusals.d.mts +11 -0
  189. package/src/lib/locales/en/refusals.d.ts +11 -0
  190. package/src/lib/locales/en/refusals.js +11 -0
  191. package/src/lib/locales/en/refusals.js.map +1 -1
  192. package/src/lib/locales/en/refusals.mjs +11 -0
  193. package/src/lib/locales/en/refusals.mjs.map +1 -1
  194. package/src/lib/locales/en/work-text.d.mts +58 -0
  195. package/src/lib/locales/en/work-text.d.ts +58 -0
  196. package/src/lib/locales/en/work-text.js +67 -0
  197. package/src/lib/locales/en/work-text.js.map +1 -0
  198. package/src/lib/locales/en/work-text.mjs +64 -0
  199. package/src/lib/locales/en/work-text.mjs.map +1 -0
  200. package/src/lib/number-box.d.mts +66 -0
  201. package/src/lib/number-box.d.ts +66 -0
  202. package/src/lib/number-box.js +153 -0
  203. package/src/lib/number-box.js.map +1 -0
  204. package/src/lib/number-box.mjs +149 -0
  205. package/src/lib/number-box.mjs.map +1 -0
  206. package/src/lib/page-json.d.mts +3 -1
  207. package/src/lib/page-json.d.ts +3 -1
  208. package/src/lib/page-json.js +5 -3
  209. package/src/lib/page-json.js.map +1 -1
  210. package/src/lib/page-json.mjs +5 -3
  211. package/src/lib/page-json.mjs.map +1 -1
  212. package/src/lib/page-tree.js +11 -1
  213. package/src/lib/page-tree.js.map +1 -1
  214. package/src/lib/page-tree.mjs +11 -1
  215. package/src/lib/page-tree.mjs.map +1 -1
  216. package/src/lib/panel-block.js +22 -6
  217. package/src/lib/panel-block.js.map +1 -1
  218. package/src/lib/panel-block.mjs +22 -6
  219. package/src/lib/panel-block.mjs.map +1 -1
  220. package/src/lib/panel-field.js +5 -1
  221. package/src/lib/panel-field.js.map +1 -1
  222. package/src/lib/panel-field.mjs +5 -1
  223. package/src/lib/panel-field.mjs.map +1 -1
  224. package/src/lib/panel-group.js +7 -3
  225. package/src/lib/panel-group.js.map +1 -1
  226. package/src/lib/panel-group.mjs +7 -3
  227. package/src/lib/panel-group.mjs.map +1 -1
  228. package/src/lib/panel-layout.js +20 -15
  229. package/src/lib/panel-layout.js.map +1 -1
  230. package/src/lib/panel-layout.mjs +20 -15
  231. package/src/lib/panel-layout.mjs.map +1 -1
  232. package/src/lib/panel-look.js +28 -14
  233. package/src/lib/panel-look.js.map +1 -1
  234. package/src/lib/panel-look.mjs +28 -14
  235. package/src/lib/panel-look.mjs.map +1 -1
  236. package/src/lib/panel-map.js +53 -20
  237. package/src/lib/panel-map.js.map +1 -1
  238. package/src/lib/panel-map.mjs +53 -20
  239. package/src/lib/panel-map.mjs.map +1 -1
  240. package/src/lib/panel-page.js +5 -1
  241. package/src/lib/panel-page.js.map +1 -1
  242. package/src/lib/panel-page.mjs +5 -1
  243. package/src/lib/panel-page.mjs.map +1 -1
  244. package/src/lib/panel-parts.js +5 -1
  245. package/src/lib/panel-parts.js.map +1 -1
  246. package/src/lib/panel-parts.mjs +5 -1
  247. package/src/lib/panel-parts.mjs.map +1 -1
  248. package/src/lib/picker-commands.d.mts +16 -0
  249. package/src/lib/picker-commands.d.ts +16 -0
  250. package/src/lib/picker-commands.js +61 -0
  251. package/src/lib/picker-commands.js.map +1 -0
  252. package/src/lib/picker-commands.mjs +58 -0
  253. package/src/lib/picker-commands.mjs.map +1 -0
  254. package/src/lib/rules-answers.d.mts +1 -1
  255. package/src/lib/rules-answers.d.ts +1 -1
  256. package/src/lib/rules-answers.js +45 -21
  257. package/src/lib/rules-answers.js.map +1 -1
  258. package/src/lib/rules-answers.mjs +45 -21
  259. package/src/lib/rules-answers.mjs.map +1 -1
  260. package/src/lib/rules-formula-box.d.mts +7 -3
  261. package/src/lib/rules-formula-box.d.ts +7 -3
  262. package/src/lib/rules-formula-box.js +26 -6
  263. package/src/lib/rules-formula-box.js.map +1 -1
  264. package/src/lib/rules-formula-box.mjs +26 -6
  265. package/src/lib/rules-formula-box.mjs.map +1 -1
  266. package/src/lib/rules-formula.js +7 -5
  267. package/src/lib/rules-formula.js.map +1 -1
  268. package/src/lib/rules-formula.mjs +8 -6
  269. package/src/lib/rules-formula.mjs.map +1 -1
  270. package/src/lib/rules-worked-out.js +66 -12
  271. package/src/lib/rules-worked-out.js.map +1 -1
  272. package/src/lib/rules-worked-out.mjs +66 -12
  273. package/src/lib/rules-worked-out.mjs.map +1 -1
  274. package/src/lib/screen-properties.js +9 -2
  275. package/src/lib/screen-properties.js.map +1 -1
  276. package/src/lib/screen-properties.mjs +9 -2
  277. package/src/lib/screen-properties.mjs.map +1 -1
  278. package/src/lib/setting-refusals.d.mts +3 -0
  279. package/src/lib/setting-refusals.d.ts +3 -0
  280. package/src/lib/setting-refusals.js +25 -0
  281. package/src/lib/setting-refusals.js.map +1 -0
  282. package/src/lib/setting-refusals.mjs +22 -0
  283. package/src/lib/setting-refusals.mjs.map +1 -0
  284. package/src/lib/styles-head.d.mts +7 -0
  285. package/src/lib/styles-head.d.ts +7 -0
  286. package/src/lib/styles-head.js +28 -0
  287. package/src/lib/styles-head.js.map +1 -0
  288. package/src/lib/styles-head.mjs +25 -0
  289. package/src/lib/styles-head.mjs.map +1 -0
  290. package/src/lib/styles.d.mts +1 -1
  291. package/src/lib/styles.d.ts +1 -1
  292. package/src/lib/styles.js +9 -2
  293. package/src/lib/styles.js.map +1 -1
  294. package/src/lib/styles.mjs +9 -2
  295. package/src/lib/styles.mjs.map +1 -1
  296. package/src/lib/table-panel.js +16 -5
  297. package/src/lib/table-panel.js.map +1 -1
  298. package/src/lib/table-panel.mjs +16 -5
  299. package/src/lib/table-panel.mjs.map +1 -1
  300. package/src/lib/work-text.d.mts +43 -0
  301. package/src/lib/work-text.d.ts +43 -0
  302. package/src/lib/work-text.js +235 -0
  303. package/src/lib/work-text.js.map +1 -0
  304. package/src/lib/work-text.mjs +227 -0
  305. package/src/lib/work-text.mjs.map +1 -0
@@ -5,8 +5,10 @@ exports.columnProperties = columnProperties;
5
5
  exports.listActionProperties = listActionProperties;
6
6
  const core_1 = require("@fieldia/core");
7
7
  const kinds_1 = require("./kinds");
8
+ const list_commands_1 = require("./list-commands");
8
9
  const menu_1 = require("./menu");
9
10
  const steps_panel_1 = require("./steps-panel");
11
+ const number_box_1 = require("./number-box");
10
12
  /**
11
13
  * The panels of a list page: the list's own — the rows a page holds, its
12
14
  * order, where the search looks, its named filters and its groupings — a
@@ -94,7 +96,12 @@ function filterRow(el, designer, id) {
94
96
  const field = fieldOf(current.field);
95
97
  save({ op: test.value, ...(current.value === undefined && field ? { value: firstValue(field) } : {}) });
96
98
  });
99
+ /** The value the filter holds now, as the page last drew it. */
100
+ let heldValue;
101
+ /** Its number box, drawn again when the page changes. */
102
+ let numberShown = null;
97
103
  function valueControl(field, condition) {
104
+ numberShown = null;
98
105
  if (field.type === 'selection' || field.type === 'boolean') {
99
106
  const options = field.type === 'boolean' ? [['true', w.yes], ['false', w.no]] : field.options.map((o) => [String(o.value), o.label]);
100
107
  const box = select(el, w.filterValue, options);
@@ -105,15 +112,21 @@ function filterRow(el, designer, id) {
105
112
  });
106
113
  return box;
107
114
  }
108
- const numeric = ['integer', 'float', 'monetary'].includes(field.type);
109
- const kind = numeric ? 'number' : field.type === 'date' || field.type === 'datetime' ? 'date' : 'text';
110
- const box = el('input', { class: 'fd-input', type: kind, 'aria-label': w.filterValue });
115
+ if (['integer', 'float', 'monetary'].includes(field.type)) {
116
+ // A number read as every box reads one, kept as it is left; a whole number for a whole-number field.
117
+ const number = (0, number_box_1.settingNumber)(el, designer, {
118
+ label: w.filterValue,
119
+ integer: field.type === 'integer',
120
+ saved: () => (typeof heldValue === 'number' ? heldValue : null),
121
+ commit: (n) => save({ value: n }),
122
+ });
123
+ numberShown = number;
124
+ number.refresh();
125
+ return number.input;
126
+ }
127
+ const box = el('input', { class: 'fd-input', type: field.type === 'date' || field.type === 'datetime' ? 'date' : 'text', 'aria-label': w.filterValue });
111
128
  box.value = String(condition.value ?? '');
112
- box.addEventListener('input', () => {
113
- if (numeric && box.value.trim() === '')
114
- return;
115
- save({ value: numeric ? Number(box.value) : box.value });
116
- });
129
+ box.addEventListener('input', () => save({ value: box.value }));
117
130
  return box;
118
131
  }
119
132
  return {
@@ -134,11 +147,13 @@ function filterRow(el, designer, id) {
134
147
  which.value = single.field;
135
148
  test.replaceChildren(...testsFor(field, designer.words).map(([op, words]) => el('option', { value: op }, words)));
136
149
  test.value = single.op;
150
+ heldValue = single.value;
137
151
  const key = `${single.field}:${single.op}`;
138
152
  if (key !== valueKey) {
139
153
  valueKey = key;
140
154
  valueBox.replaceChildren(...(single.op === 'set' || single.op === 'notset' ? [] : [valueControl(field, single)]));
141
155
  }
156
+ numberShown?.refresh();
142
157
  },
143
158
  focus() {
144
159
  name.focus();
@@ -190,7 +205,23 @@ function mapSection(el, designer) {
190
205
  // How the map sits in its page: its height, the gestures it takes, the closest it zooms to show its records.
191
206
  const height = el('select', { class: 'fd-input fd-select', 'aria-label': w.mapHeight });
192
207
  const allGestures = el('input', { type: 'checkbox' });
193
- const fitZoom = el('input', { class: 'fd-input', type: 'number', min: '1', max: '22', placeholder: '16', 'aria-label': w.fitZoom });
208
+ // A whole number, 1 to 22, refused with its range rather than brought into it in silence; emptied, the usual 16.
209
+ let zoomHeld = null;
210
+ const fitZoom = (0, number_box_1.settingNumber)(el, designer, {
211
+ label: w.fitZoom,
212
+ attrs: { class: 'fd-input', placeholder: '16' },
213
+ integer: true,
214
+ min: 1,
215
+ max: 22,
216
+ optional: true,
217
+ saved: () => zoomHeld,
218
+ commit: (n) => change((map) => {
219
+ if (n === null)
220
+ delete map.fitZoom;
221
+ else
222
+ map.fitZoom = n;
223
+ }),
224
+ });
194
225
  const rows = {
195
226
  lat: prop(el, w.latitude, lat),
196
227
  lng: prop(el, w.longitude, lng),
@@ -203,7 +234,7 @@ function mapSection(el, designer) {
203
234
  areaField: prop(el, w.areaField, areaField),
204
235
  areas: prop(el, w.areas, areas),
205
236
  };
206
- const details = el('div', { class: 'fd-props' }, prop(el, w.where, where), rows.lat, rows.lng, prop(el, w.pinShows, label), prop(el, w.pinLook, look), rows.photo, rows.icon, rows.iconFrom, rows.turn, rows.home, prop(el, w.cardTitle, title), prop(el, w.cardPicture, picture), el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.cardShows), cardFields, addToCard), el('label', { class: 'fd-q-required' }, cardLabels, el('span', {}, w.cardLabels)), el('label', { class: 'fd-q-required' }, cardLead, el('span', {}, w.cardLead)), prop(el, w.cardLink, cardLink), el('label', { class: 'fd-q-required' }, cardChoose, el('span', {}, w.cardChoose)), prop(el, w.opensAs, opens), el('label', { class: 'fd-q-required' }, bubbles, el('span', {}, w.bubbles)), el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.mapTools), tools), el('label', { class: 'fd-q-required' }, heat, el('span', {}, w.heat)), rows.weight, rows.areas, rows.areaField, areaWhy, prop(el, w.mapHeight, height), el('label', { class: 'fd-q-required' }, allGestures, el('span', {}, w.allGestures)), prop(el, w.fitZoom, fitZoom), el('p', { class: 'fd-properties-hint' }, w.mapNote));
237
+ const details = el('div', { class: 'fd-props' }, prop(el, w.where, where), rows.lat, rows.lng, prop(el, w.pinShows, label), prop(el, w.pinLook, look), rows.photo, rows.icon, rows.iconFrom, rows.turn, rows.home, prop(el, w.cardTitle, title), prop(el, w.cardPicture, picture), el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.cardShows), cardFields, addToCard), el('label', { class: 'fd-q-required' }, cardLabels, el('span', {}, w.cardLabels)), el('label', { class: 'fd-q-required' }, cardLead, el('span', {}, w.cardLead)), prop(el, w.cardLink, cardLink), el('label', { class: 'fd-q-required' }, cardChoose, el('span', {}, w.cardChoose)), prop(el, w.opensAs, opens), el('label', { class: 'fd-q-required' }, bubbles, el('span', {}, w.bubbles)), el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.mapTools), tools), el('label', { class: 'fd-q-required' }, heat, el('span', {}, w.heat)), rows.weight, rows.areas, rows.areaField, areaWhy, prop(el, w.mapHeight, height), el('label', { class: 'fd-q-required' }, allGestures, el('span', {}, w.allGestures)), prop(el, w.fitZoom, fitZoom.input), el('p', { class: 'fd-properties-hint' }, w.mapNote));
207
238
  const element = el('div', { class: 'fd-prop', 'data-part': 'map' }, el('span', { class: 'fd-prop-name' }, w.map), onRow, noPlace, details);
208
239
  const fieldsOf = (page, kinds) => everyField(page, designer).filter(({ field }) => kinds(field));
209
240
  const fill = (box, choices, none) => box.replaceChildren(...(none === undefined ? [] : [el('option', { value: '' }, none)]), ...choices.map(({ name, field }) => el('option', { value: name }, field.label)));
@@ -236,7 +267,7 @@ function mapSection(el, designer) {
236
267
  delete map.pin;
237
268
  if (card && !Object.keys(card).length)
238
269
  delete map.card;
239
- designer.setListOptions({ map });
270
+ return designer.setListOptions({ map });
240
271
  };
241
272
  const pinOf = (map) => (map.pin ??= {});
242
273
  /** Where a new map puts its records: the first point, or the two numbers named for latitude and longitude. */
@@ -334,13 +365,6 @@ function mapSection(el, designer) {
334
365
  else
335
366
  delete map.gestures;
336
367
  }));
337
- fitZoom.addEventListener('change', () => change((map) => {
338
- const zoom = Math.round(Number(fitZoom.value));
339
- if (fitZoom.value.trim() === '' || !Number.isFinite(zoom))
340
- delete map.fitZoom;
341
- else
342
- map.fitZoom = Math.min(22, Math.max(1, zoom));
343
- }));
344
368
  addToCard.addEventListener('click', () => {
345
369
  const page = designer.getPage();
346
370
  const lines = current()?.card?.fields ?? [];
@@ -436,18 +460,24 @@ function mapSection(el, designer) {
436
460
  height.replaceChildren(...heights.map(([value, text]) => el('option', { value }, text)));
437
461
  height.value = own;
438
462
  allGestures.checked = map.gestures === 'all';
439
- if (!focused(fitZoom))
440
- fitZoom.value = map.fitZoom === undefined ? '' : String(map.fitZoom);
463
+ zoomHeld = map.fitZoom ?? null;
464
+ fitZoom.refresh();
441
465
  },
442
466
  };
443
467
  }
444
468
  // ---- the list --------------------------------------------------------------------------------
445
469
  function listProperties(el, designer) {
446
470
  const w = designer.words.list;
447
- const size = el('input', { class: 'fd-input', type: 'number', min: '1', max: '500', placeholder: '40', 'aria-label': w.rowsOnPage });
448
- size.addEventListener('input', () => {
449
- if (size.value.trim() !== '')
450
- designer.setListOptions({ pageSize: Number(size.value) });
471
+ // A whole number, 1 to 500, kept as it is left: never the 60 that "600" passes on its way.
472
+ let pageSize = 40;
473
+ const size = (0, number_box_1.settingNumber)(el, designer, {
474
+ label: w.rowsOnPage,
475
+ attrs: { class: 'fd-input', placeholder: '40' },
476
+ integer: true,
477
+ min: 1,
478
+ max: 500,
479
+ saved: () => pageSize,
480
+ commit: (n) => n !== null && designer.setListOptions({ pageSize: n }),
451
481
  });
452
482
  const sortBy = el('select', { class: 'fd-input fd-select', 'aria-label': w.sortedBy });
453
483
  const direction = select(el, w.direction, [['asc', w.ascending], ['desc', w.descending]]);
@@ -480,14 +510,35 @@ function listProperties(el, designer) {
480
510
  onPick: (name) => designer.setListOptions({ groupBy: [...taken, name] }),
481
511
  });
482
512
  });
513
+ // A box to choose each row; the search bar, or a keyword and each field's values; rows under their parent.
514
+ const choose = el('input', { type: 'checkbox', 'aria-label': w.choose });
515
+ choose.addEventListener('change', () => {
516
+ if (!designer.setListOptions({ choose: choose.checked }))
517
+ choose.checked = !choose.checked;
518
+ });
519
+ const chooseRow = el('div', { class: 'fd-prop', 'data-part': 'choose' }, el('label', { class: 'fd-q-required' }, choose, el('span', {}, w.choose)), el('p', { class: 'fd-properties-hint' }, w.chooseHint));
520
+ const sorts = el('input', { type: 'checkbox', 'aria-label': w.sortable });
521
+ sorts.addEventListener('change', () => {
522
+ if (!designer.setListOptions({ sortable: sorts.checked }))
523
+ sorts.checked = !sorts.checked;
524
+ });
525
+ const sortsRow = el('div', { class: 'fd-prop', 'data-part': 'sortable' }, el('label', { class: 'fd-q-required' }, sorts, el('span', {}, w.sortable)), el('p', { class: 'fd-properties-hint' }, w.sortableHint));
526
+ const searchWay = select(el, w.searchWay, [['bar', w.searchWays.bar], ['fields', w.searchWays.fields]]);
527
+ searchWay.addEventListener('change', () => designer.setListOptions({ search: searchWay.value }));
528
+ const filterBox = el('div', { class: 'fd-list-filter-fields' });
529
+ const filterFieldsRow = el('div', { class: 'fd-prop', 'data-part': 'filter-fields' }, el('span', { class: 'fd-prop-name' }, w.filterFields), filterBox, el('p', { class: 'fd-properties-hint' }, w.filterFieldsNote), el('p', { class: 'fd-properties-hint' }, w.filterLabelsNote));
530
+ const parent = el('select', { class: 'fd-input fd-select', 'aria-label': w.parentField });
531
+ parent.addEventListener('change', () => designer.setListOptions({ parentField: parent.value || null }));
532
+ const parentWhy = el('p', { class: 'fd-properties-hint', hidden: '' }, w.noParentField);
533
+ const parentRow = el('label', { class: 'fd-prop', 'data-part': 'parent' }, el('span', { class: 'fd-prop-name' }, w.parentField), parent, parentWhy);
483
534
  const onMap = mapSection(el, designer);
484
- const element = el('div', { class: 'fd-props' }, prop(el, w.rowsOnPage, size), prop(el, w.sortedBy, sortBy), directionRow, el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.searchLooksIn), searchBox, el('p', { class: 'fd-properties-hint' }, w.noneTicked)), el('div', { class: 'fd-prop fd-prop-filters', 'data-part': 'filters' }, el('span', { class: 'fd-prop-name' }, w.filters), filtersBox, addFilter), el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.groupBy), groupsBox, addGroup), onMap.element, el('p', { class: 'fd-properties-hint' }, w.rowOpens));
535
+ const element = el('div', { class: 'fd-props' }, prop(el, w.rowsOnPage, size.input), prop(el, w.sortedBy, sortBy), directionRow, chooseRow, sortsRow, prop(el, w.searchWay, searchWay), filterFieldsRow, parentRow, el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.searchLooksIn), searchBox, el('p', { class: 'fd-properties-hint' }, w.noneTicked)), el('div', { class: 'fd-prop fd-prop-filters', 'data-part': 'filters' }, el('span', { class: 'fd-prop-name' }, w.filters), filtersBox, addFilter), el('div', { class: 'fd-prop' }, el('span', { class: 'fd-prop-name' }, w.groupBy), groupsBox, addGroup), onMap.element, el('p', { class: 'fd-properties-hint' }, w.rowOpens));
485
536
  return {
486
537
  element,
487
538
  update(page) {
488
539
  const list = listOf(page);
489
- if (!focused(size))
490
- size.value = list.pageSize === undefined ? '' : String(list.pageSize);
540
+ pageSize = list.pageSize ?? 40;
541
+ size.refresh();
491
542
  const sortable = everyField(page, designer).filter(({ field }) => !UNSORTABLE.has(field.type));
492
543
  sortBy.replaceChildren(el('option', { value: '' }, w.appOrder), ...sortable.map(({ name, field }) => el('option', { value: name }, field.label)));
493
544
  const order = list.sort?.[0];
@@ -524,6 +575,37 @@ function listProperties(el, designer) {
524
575
  });
525
576
  while (filtersBox.children.length > drawn.length)
526
577
  filtersBox.lastElementChild?.remove();
578
+ choose.checked = list.choose !== false;
579
+ sorts.checked = list.sortable !== false;
580
+ searchWay.value = list.search ?? 'bar';
581
+ // The filter bar's fields: those whose values it can list, ticked in the order the page keeps them.
582
+ filterFieldsRow.hidden = list.search !== 'fields';
583
+ const offered = list.filterFields ?? [];
584
+ const filterable = [...new Set([...offered, ...everyField(page, designer).filter(({ field }) => list_commands_1.FILTERABLE.has(field.type)).map(({ name }) => name)])];
585
+ // Each one offered has a name of its own on the bar, its label unless one is written; kept while it is being typed in.
586
+ const typing = filterBox.querySelector('.fd-list-filter-name:focus');
587
+ if (!typing || !filterable.includes(typing.dataset['field'] ?? '')) {
588
+ filterBox.replaceChildren(...filterable.map((name) => {
589
+ const label = page.fields[name]?.label ?? designer.modelFields().find((m) => m.name === name)?.field.label ?? name;
590
+ const box = el('input', { type: 'checkbox', 'aria-label': w.filterFieldNamed(label) });
591
+ box.checked = offered.includes(name);
592
+ box.addEventListener('change', () => designer.setListOptions({ filterFields: box.checked ? [...offered, name] : offered.filter((n) => n !== name) }));
593
+ const choice = el('label', { class: 'fd-q-required' }, box, el('span', {}, label));
594
+ if (!offered.includes(name))
595
+ return choice;
596
+ const own = el('input', { class: 'fd-input fd-list-filter-name', 'data-field': name, 'aria-label': w.filterLabelOf(label), title: w.filterLabelOf(label), placeholder: label });
597
+ own.value = list.filterLabels?.[name] ?? '';
598
+ own.addEventListener('change', () => designer.setListOptions({ filterLabel: { field: name, label: own.value } }));
599
+ return el('div', { class: 'fd-list-filter-field' }, choice, own);
600
+ }));
601
+ }
602
+ // Rows under their parent: a link to the list's own records.
603
+ const own = page.data.kind === 'record' ? page.data.model : '';
604
+ const links = everyField(page, designer).filter(({ field }) => field.type === 'many2one' && field.relation === own);
605
+ parent.replaceChildren(el('option', { value: '' }, w.noParent), ...links.map(({ name, field }) => el('option', { value: name }, field.label)));
606
+ parent.value = list.parentField ?? '';
607
+ parentWhy.hidden = links.length > 0;
608
+ parentRow.hidden = !links.length && !list.parentField;
527
609
  const groups = list.groupBy ?? [];
528
610
  groupsBox.replaceChildren(...groups.map((name) => {
529
611
  const label = page.fields[name]?.label ?? name;
@@ -556,7 +638,8 @@ function columnProperties(el, designer, name) {
556
638
  left.addEventListener('click', () => designer.moveColumn(name, at() - 1));
557
639
  right.addEventListener('click', () => designer.moveColumn(name, at() + 1));
558
640
  remove.addEventListener('click', () => designer.removeColumn(name));
559
- const element = el('div', { class: 'fd-props' }, about, orderRow, el('div', { class: 'fd-props-actions' }, left, right, remove));
641
+ const cells = cellSettings(el, designer, name);
642
+ const element = el('div', { class: 'fd-props' }, about, orderRow, ...cells.rows, el('div', { class: 'fd-props-actions' }, left, right, remove));
560
643
  return {
561
644
  element,
562
645
  update(page) {
@@ -568,6 +651,7 @@ function columnProperties(el, designer, name) {
568
651
  orderRow.hidden = UNSORTABLE.has(field.type);
569
652
  const first = list.sort?.[0];
570
653
  order.value = first?.field === name ? (first.desc ? 'desc' : 'asc') : '';
654
+ cells.update(page);
571
655
  const index = list.columns.indexOf(name);
572
656
  left.hidden = index <= 0;
573
657
  right.hidden = index === list.columns.length - 1;
@@ -575,6 +659,142 @@ function columnProperties(el, designer, name) {
575
659
  },
576
660
  };
577
661
  }
662
+ // ---- a column's cells ------------------------------------------------------------------------------
663
+ /**
664
+ * How a column draws its cells, as a work tracker's backlog does: a state's
665
+ * dot in each value's colour, an icon before the words by another field's
666
+ * value (a bug's, a task's), a width in characters, the side its words keep to.
667
+ */
668
+ function cellSettings(el, designer, name) {
669
+ const w = designer.words.list;
670
+ const cellNow = () => ({ ...listOf(designer.getPage()).cells?.[name] });
671
+ const save = (cell) => {
672
+ for (const key of Object.keys(cell))
673
+ if (cell[key] === undefined)
674
+ delete cell[key];
675
+ return designer.setListCell(name, Object.keys(cell).length ? cell : null);
676
+ };
677
+ const widget = select(el, w.cellWidget, [['', w.cellWidgets.words], ['dot', w.cellWidgets.dot]]);
678
+ widget.addEventListener('change', () => save({ ...cellNow(), widget: widget.value || undefined, ...(widget.value ? {} : { options: undefined }) }));
679
+ const widgetRow = prop(el, w.cellWidget, widget);
680
+ const dots = el('div', { class: 'fd-list-cell-looks' });
681
+ const dotsRow = el('div', { class: 'fd-prop', 'data-part': 'dots' }, dots);
682
+ const lead = el('select', { class: 'fd-input fd-select', 'aria-label': w.leadFrom });
683
+ lead.addEventListener('change', () => save({ ...cellNow(), lead: lead.value ? { field: lead.value, looks: {} } : undefined }));
684
+ const leadLooks = el('div', { class: 'fd-list-cell-looks' });
685
+ const leadRow = el('div', { class: 'fd-prop', 'data-part': 'lead' }, el('span', { class: 'fd-prop-name' }, w.leadFrom), lead, leadLooks);
686
+ // Its width, in characters of its words or in pixels for the whole column: a number and its unit.
687
+ // A whole number: 1 to 200 characters, or 8 to 4000 pixels; kept as it is left, refused with its range; emptied, as its words need.
688
+ const unit = select(el, w.cellWidthUnit, [['ch', w.cellWidthUnits.characters], ['px', w.cellWidthUnits.pixels]]);
689
+ const widthIn = (n) => (unit.value === 'px' ? `${n}px` : n);
690
+ const sizing = {
691
+ label: w.cellWidth,
692
+ integer: true,
693
+ min: 1,
694
+ max: 200,
695
+ optional: true,
696
+ saved: () => {
697
+ const now = cellNow().width;
698
+ return now === undefined ? null : Number.parseInt(String(now), 10);
699
+ },
700
+ commit: (n) => save({ ...cellNow(), width: n === null ? undefined : widthIn(n) }),
701
+ };
702
+ const width = (0, number_box_1.settingNumber)(el, designer, sizing);
703
+ const unitRange = () => {
704
+ sizing.min = unit.value === 'px' ? 8 : 1;
705
+ sizing.max = unit.value === 'px' ? 4000 : 200;
706
+ };
707
+ unit.addEventListener('change', () => {
708
+ unitRange();
709
+ // The same number in the other unit, when it is a width in that one too.
710
+ const n = sizing.saved();
711
+ if (typeof n === 'number' && (0, list_commands_1.widthIsGood)(widthIn(n)))
712
+ save({ ...cellNow(), width: widthIn(n) });
713
+ });
714
+ const widthRow = el('div', { class: 'fd-prop', 'data-part': 'width' }, el('span', { class: 'fd-prop-name' }, w.cellWidth), el('div', { class: 'fd-list-width' }, width.input, unit), el('p', { class: 'fd-properties-hint' }, w.cellWidthHint));
715
+ const side = select(el, w.cellSide, [['', w.cellSides.auto], ['start', w.cellSides.start], ['end', w.cellSides.end]]);
716
+ side.addEventListener('change', () => save({ ...cellNow(), align: (side.value || undefined) }));
717
+ // Its header's rule: at its end, none, or set in from its end by some pixels.
718
+ const rule = select(el, w.cellRule, [['', w.cellRules.end], ['none', w.cellRules.none], ['in', w.cellRules.in]]);
719
+ // Pixels in from the column's end, a whole number 0 to 400, kept as it is left: letters never become 0px.
720
+ const ruleNow = () => {
721
+ const now = cellNow().rule;
722
+ return typeof now === 'string' ? Number.parseInt(now, 10) : null;
723
+ };
724
+ const ruleIn = (0, number_box_1.settingNumber)(el, designer, {
725
+ label: w.cellRuleIn,
726
+ integer: true,
727
+ min: 0,
728
+ max: 400,
729
+ saved: ruleNow,
730
+ commit: (n) => n !== null && (0, list_commands_1.ruleIsGood)(`${n}px`) && save({ ...cellNow(), rule: `${n}px` }),
731
+ });
732
+ rule.addEventListener('change', () => {
733
+ if (rule.value === 'none')
734
+ return void save({ ...cellNow(), rule: false });
735
+ if (rule.value === '')
736
+ return void save({ ...cellNow(), rule: undefined });
737
+ save({ ...cellNow(), rule: `${ruleNow() ?? 0}px` });
738
+ });
739
+ const ruleInRow = prop(el, w.cellRuleIn, ruleIn.input);
740
+ const ruleRow = el('div', { class: 'fd-prop', 'data-part': 'rule' }, el('span', { class: 'fd-prop-name' }, w.cellRule), rule, el('p', { class: 'fd-properties-hint' }, w.cellRuleHint));
741
+ /** A value's colour, and its icon for a lead: a row for each option of a choice. */
742
+ const optionRows = (field, row) => field?.type === 'selection' ? field.options.map((option) => el('div', { class: 'fd-list-cell-look' }, el('span', { class: 'fd-list-cell-look-name' }, option.label), ...row(String(option.value), option.label))) : [];
743
+ return {
744
+ rows: [widgetRow, dotsRow, leadRow, widthRow, prop(el, w.cellSide, side), ruleRow, ruleInRow],
745
+ update(page) {
746
+ const field = page.fields[name];
747
+ const cell = page.layout.type === 'list' ? (page.layout.cells?.[name] ?? {}) : {};
748
+ widget.value = cell.widget === 'dot' ? 'dot' : '';
749
+ widgetRow.hidden = field?.type !== 'selection';
750
+ // Each state's dot colour, when the column draws its dot.
751
+ dotsRow.hidden = cell.widget !== 'dot';
752
+ const colours = (cell.options?.['colours'] ?? {});
753
+ dots.replaceChildren(...optionRows(field, (value, label) => {
754
+ const colour = el('input', { type: 'color', class: 'fd-insp-colour', 'aria-label': w.dotColour(label) });
755
+ colour.value = colours[value] ?? '#7e7e7e';
756
+ colour.addEventListener('input', () => save({ ...cellNow(), options: { ...cellNow().options, colours: { ...(cellNow().options?.['colours'] ?? {}), [value]: colour.value } } }));
757
+ return [colour];
758
+ }));
759
+ // An icon before the words: by a choice's value, each with its icon and colour.
760
+ const choices = everyField(page, designer).filter(({ field: f }) => f.type === 'selection');
761
+ lead.replaceChildren(el('option', { value: '' }, w.noLead), ...choices.map(({ name: n, field: f }) => el('option', { value: n }, f.label)));
762
+ lead.value = cell.lead?.field ?? '';
763
+ const from = cell.lead ? page.fields[cell.lead.field] : undefined;
764
+ leadLooks.replaceChildren(...optionRows(from, (value, label) => {
765
+ const look = cell.lead?.looks[value];
766
+ const icon = el('input', { class: 'fd-input', 'aria-label': w.leadIcon(label), placeholder: 'bug' });
767
+ icon.value = look?.icon ?? '';
768
+ const colour = el('input', { type: 'color', class: 'fd-insp-colour', 'aria-label': w.leadColour(label) });
769
+ colour.value = look?.colour ?? '#7e7e7e';
770
+ const write = () => {
771
+ const now = cellNow();
772
+ if (!now.lead)
773
+ return;
774
+ const looks = { ...now.lead.looks };
775
+ if (icon.value.trim())
776
+ looks[value] = { icon: icon.value.trim(), ...(look?.colour || colour.value !== '#7e7e7e' ? { colour: colour.value } : {}) };
777
+ else
778
+ delete looks[value];
779
+ save({ ...now, lead: { field: now.lead.field, looks } });
780
+ };
781
+ icon.addEventListener('change', write);
782
+ colour.addEventListener('input', write);
783
+ return [icon, colour];
784
+ }));
785
+ const inPixels = typeof cell.width === 'string';
786
+ if (!focused(unit) && cell.width !== undefined)
787
+ unit.value = inPixels ? 'px' : 'ch';
788
+ unitRange();
789
+ width.refresh();
790
+ side.value = cell.align ?? '';
791
+ if (!focused(rule))
792
+ rule.value = cell.rule === false ? 'none' : cell.rule ? 'in' : '';
793
+ ruleIn.refresh();
794
+ ruleInRow.hidden = rule.value !== 'in';
795
+ },
796
+ };
797
+ }
578
798
  // ---- a button for the rows chosen ----------------------------------------------------------------
579
799
  function listActionProperties(el, designer, id) {
580
800
  const w = designer.words.list;