@xeplr/ui-factory 1.8.4 → 1.8.5

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.
package/README.md CHANGED
@@ -433,6 +433,7 @@ actions.register({
433
433
  | `ask` | asked first: `title`, `message`, and `note` — the label of a note box, whose text reaches the function as `note` (it must take one) |
434
434
  | `when` | shown only when every rule matches the record |
435
435
  | `disabledWhen` | greyed out when every rule matches |
436
+ | `pressedWhen` | drawn as the **current choice** (filled, `aria-pressed`) when every rule matches — the status an order is at. Give it the same rules as `disabledWhen` when choosing it again would change nothing |
436
437
  | `variant` | `default`, `primary`, `danger` |
437
438
 
438
439
  Rules are [`@xeplr/rules`](https://www.npmjs.com/package/@xeplr/rules) rules — `{ field, op, value }`, with `eq`, `neq`, `isEmpty`, `contains`, `gt`, … — read from **the record the screen is about**: the `record` handed to `<FactoryScreen>`, with the form's own changes on top.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xeplr/ui-factory",
3
- "version": "1.8.4",
3
+ "version": "1.8.5",
4
4
  "description": "Design data-entry screens on @xeplr/ui-canvas, save them as JSON, run them as auto-saving forms with lists of saved records — built for Claude to draft and people to refine",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/buttons.js CHANGED
@@ -7,6 +7,7 @@
7
7
  //
8
8
  // { id: 'packed', label: 'Packed', action: 'setOrderStatus', params: { status: 'packed' },
9
9
  // ask: { title: 'Change status to Packed', note: 'Reason (optional)' },
10
+ // pressedWhen: [{ field: 'customStatus', op: 'eq', value: 'packed' }],
10
11
  // disabledWhen: [{ field: 'customStatus', op: 'eq', value: 'packed' }] }
11
12
  //
12
13
  // The function lives in the app, registered with @xeplr/actions and an
@@ -66,6 +67,16 @@ export function buttonDisabled(button, record) {
66
67
  return Array.isArray(rules) && rules.length > 0 && conditionMet(rules, record)
67
68
  }
68
69
 
70
+ /**
71
+ * Whether a button is drawn as the CURRENT choice — the status an order is
72
+ * at, filled in. Usually also its disabledWhen: choosing it again changes
73
+ * nothing. No pressedWhen: never.
74
+ */
75
+ export function buttonPressed(button, record) {
76
+ const rules = button && button.pressedWhen
77
+ return Array.isArray(rules) && rules.length > 0 && conditionMet(rules, record)
78
+ }
79
+
69
80
  /** Every button on a screen, with the panel it is on. */
70
81
  export function screenButtons(doc) {
71
82
  return ((doc && doc.nodes) || [])
@@ -37,7 +37,8 @@ export default function ButtonsView({ node, design, panel }) {
37
37
  <button
38
38
  key={b.id}
39
39
  type="button"
40
- className={`xeplr-factory-button xeplr-factory-button--${b.variant || 'default'}`}
40
+ className={`xeplr-factory-button xeplr-factory-button--${b.variant || 'default'}${b.pressed ? ' xeplr-factory-button--pressed' : ''}`}
41
+ aria-pressed={b.pressedWhen ? Boolean(b.pressed) : undefined}
41
42
  disabled={b.disabled || Boolean(panel.pressing)}
42
43
  aria-busy={panel.pressing === b.id}
43
44
  onClick={() => run(b)}
@@ -985,6 +985,7 @@ function ButtonsEditor({ value, onChange, functions }) {
985
985
  )}
986
986
  <ConditionsEditor label="Shows when" value={b.when} onChange={(v) => edit(i, { when: v })} />
987
987
  <ConditionsEditor label="Greyed out when" value={b.disabledWhen} onChange={(v) => edit(i, { disabledWhen: v })} />
988
+ <ConditionsEditor label="Shown as the current choice when" value={b.pressedWhen} onChange={(v) => edit(i, { pressedWhen: v })} />
988
989
  <MiniField label="Look">
989
990
  <select className="xeplr-factory-prop-input" value={b.variant || 'default'} onChange={(e) => edit(i, { variant: e.target.value === 'default' ? undefined : e.target.value })}>
990
991
  {BUTTON_VARIANTS.map((v) => <option key={v} value={v}>{VARIANT_LABELS[v]}</option>)}
@@ -372,6 +372,10 @@ button.xeplr-factory-activity-item:hover, button.xeplr-factory-activity-item:foc
372
372
  .xeplr-factory-button--danger { color: var(--xf-danger); border-color: color-mix(in srgb, var(--xf-danger) 55%, var(--xf-border)); }
373
373
  .xeplr-factory-button--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--xf-danger) 10%, transparent); }
374
374
  .xeplr-factory-buttons--design .xeplr-factory-button { cursor: inherit; }
375
+ /* The current choice (pressedWhen): filled, and never faded — greyed out is
376
+ "unavailable", which the current status is not. */
377
+ .xeplr-factory-button--pressed,
378
+ .xeplr-factory-button--pressed:disabled { color: var(--xf-accent-ink); background: var(--xf-accent); border-color: var(--xf-accent); opacity: 1; }
375
379
  .xeplr-factory-buttons-note {
376
380
  display: inline-flex; align-items: center; gap: 6px; font-size: .9em; padding: 4px 6px 4px 10px; border-radius: 6px;
377
381
  color: var(--xf-good); background: color-mix(in srgb, var(--xf-good) 10%, transparent);
package/src/model.js CHANGED
@@ -17,7 +17,7 @@ export { SCREEN_EVENTS, SCREEN_EVENT_KEYS, DECIDE_EVENT, FUNCTION_NAME, screenFu
17
17
  // Buttons and lists whose rows a function works out — names and values only.
18
18
  export {
19
19
  BUTTON_EVENT, LIST_ROWS_EVENT, ROW_DETAILS_EVENT, BUTTON_VARIANTS, COLUMN_DISPLAYS, BADGE_TONES, NOTE_INPUT, CONDITION_OPERATORS,
20
- conditionMet, buttonShown, buttonDisabled, screenButtons, buttonById, buttonInput, nodeFunctions, paramProblems, optionValues
20
+ conditionMet, buttonShown, buttonDisabled, buttonPressed, screenButtons, buttonById, buttonInput, nodeFunctions, paramProblems, optionValues
21
21
  } from './buttons.js'
22
22
  // Which pages are drawn from documents and which are web only (ui/pages.json).
23
23
  export { checkInventory, routesIn, pageWarnings, webOnlyMenus } from './pagesInventory.js'
@@ -5,7 +5,7 @@ import { validateDocument } from './validateDocument.js'
5
5
  import { initialValues, parseInput, saveState, fieldError, optionValue, recordValues, listSource, resolveBinding } from './values.js'
6
6
  import { hookMethod } from './hooks.js'
7
7
  import { useUrlSteps, useUrlListQuery } from './useUrlPlaces.js'
8
- import { buttonShown, buttonDisabled } from './buttons.js'
8
+ import { buttonShown, buttonDisabled, buttonPressed } from './buttons.js'
9
9
 
10
10
  // A saved screen, running. No JSX — designs/ScreenSample.jsx draws it.
11
11
  //
@@ -838,7 +838,7 @@ export function useFactoryScreen({
838
838
  record: buttonRecord,
839
839
  buttons: ((node.props && node.props.buttons) || [])
840
840
  .filter((b) => buttonShown(b, buttonRecord))
841
- .map((b) => ({ ...b, disabled: buttonDisabled(b, buttonRecord) || recordId === null || recordId === undefined })),
841
+ .map((b) => ({ ...b, pressed: buttonPressed(b, buttonRecord), disabled: buttonDisabled(b, buttonRecord) || recordId === null || recordId === undefined })),
842
842
  pressing,
843
843
  note: buttonNote,
844
844
  clearNote: () => setButtonNote(null),
@@ -522,7 +522,7 @@ function checkFedColumn(c, p, err) {
522
522
  }
523
523
  }
524
524
 
525
- const BUTTON_KEYS = ['id', 'label', 'action', 'params', 'ask', 'when', 'disabledWhen', 'variant']
525
+ const BUTTON_KEYS = ['id', 'label', 'action', 'params', 'ask', 'when', 'disabledWhen', 'pressedWhen', 'variant']
526
526
  const ASK_KEYS = ['title', 'message', 'note']
527
527
 
528
528
  /**
@@ -559,7 +559,7 @@ function checkButtons(props, at, err, ids) {
559
559
  }
560
560
  }
561
561
  if (b.variant !== undefined && !BUTTON_VARIANTS.includes(b.variant)) err(`${p}.variant`, `must be one of: ${BUTTON_VARIANTS.join(', ')}`)
562
- ;['when', 'disabledWhen'].forEach((k) => { if (b[k] !== undefined) checkRules(b[k], `${p}.${k}`, err) })
562
+ ;['when', 'disabledWhen', 'pressedWhen'].forEach((k) => { if (b[k] !== undefined) checkRules(b[k], `${p}.${k}`, err) })
563
563
  })
564
564
  }
565
565