@weasel-js/select 1.15.0 → 1.15.2

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/dist/index.d.ts CHANGED
@@ -35,6 +35,15 @@ declare function rangeOf<K>(order: readonly K[], from: K, to: K, eligible?: (id:
35
35
  * hands, so a caller can compare by reference. */
36
36
  declare function select<K>(state: Selection<K>, id: K, intent: SelectIntent, context?: SelectContext<K>): Selection<K>;
37
37
 
38
+ /** A tree as a selection reads it: each id's parent, or `null` (or `undefined`) at a root. */
39
+ type ParentOf<K> = (id: K) => K | null | undefined;
40
+ /** The selected id that holds `id`: `id` itself when it is selected, else its nearest selected
41
+ * ancestor, else `null`. A node is selected whenever something holds it. */
42
+ declare function holderOf<K>(selected: ReadonlySet<K>, id: K, parentOf: ParentOf<K>): K | null;
43
+ /** `ids` less every id an ancestor in `ids` already holds, in their order: what a selection over
44
+ * a tree keeps explicitly. Returns `ids` itself when none is dropped. */
45
+ declare function outermost<K>(ids: readonly K[], parentOf: ParentOf<K>): readonly K[];
46
+
38
47
  /** Somewhere a selection can live outside the component that edits it. core's `Scene` satisfies
39
48
  * it, and so does any store with the same three methods. */
40
49
  interface SelectionStore<K> {
@@ -45,4 +54,4 @@ interface SelectionStore<K> {
45
54
  /** A {@link SelectionStore} holding the ids itself. A listener reads the new value. */
46
55
  declare function createSelectionStore<K>(initial?: readonly K[]): SelectionStore<K>;
47
56
 
48
- export { type SelectContext, type SelectIntent, type SelectModifiers, type SelectPolicy, type Selection, type SelectionExtendKey, type SelectionMode, type SelectionStore, createSelectionStore, intentOf, rangeOf, select };
57
+ export { type ParentOf, type SelectContext, type SelectIntent, type SelectModifiers, type SelectPolicy, type Selection, type SelectionExtendKey, type SelectionMode, type SelectionStore, createSelectionStore, holderOf, intentOf, outermost, rangeOf, select };
package/dist/index.js CHANGED
@@ -35,6 +35,23 @@ function settle(state, next) {
35
35
  function always() {
36
36
  return true;
37
37
  }
38
+
39
+ // src/tree.ts
40
+ function holderOf(selected, id, parentOf) {
41
+ for (let at = id; at != null; at = parentOf(at)) {
42
+ if (selected.has(at)) return at;
43
+ }
44
+ return null;
45
+ }
46
+ function outermost(ids, parentOf) {
47
+ if (ids.length < 2) return ids;
48
+ const selected = new Set(ids);
49
+ const held = (id) => {
50
+ const parent = parentOf(id);
51
+ return parent != null && holderOf(selected, parent, parentOf) !== null;
52
+ };
53
+ return ids.some(held) ? ids.filter((id) => !held(id)) : ids;
54
+ }
38
55
  function createSelectionStore(initial = []) {
39
56
  let ids = initial;
40
57
  const changed = createEmitter();
@@ -48,6 +65,6 @@ function createSelectionStore(initial = []) {
48
65
  };
49
66
  }
50
67
 
51
- export { createSelectionStore, intentOf, rangeOf, select };
68
+ export { createSelectionStore, holderOf, intentOf, outermost, rangeOf, select };
52
69
  //# sourceMappingURL=index.js.map
53
70
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/select.ts","../src/store.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** `single` always replaces; `multi` lets a policy's keys toggle and range. */\nexport type SelectionMode = 'single' | 'multi';\n\n/** A modifier key a policy can assign to toggling or ranging. */\nexport type SelectionExtendKey = 'shift' | 'meta' | 'ctrl';\n\n/** The modifiers held during a press. */\nexport type SelectModifiers = Readonly<Record<SelectionExtendKey, boolean>>;\n\n/** What a press does to the selection. */\nexport type SelectIntent = 'replace' | 'toggle' | 'range';\n\n/** Which keys toggle and which range, so a surface states its convention: a list usually\n * toggles on Cmd/Ctrl and ranges on shift, a canvas toggles on shift. */\nexport interface SelectPolicy {\n readonly mode: SelectionMode;\n readonly toggle?: SelectionExtendKey | readonly SelectionExtendKey[];\n readonly range?: SelectionExtendKey;\n}\n\n/** What is selected, and the end a range extends from. */\nexport interface Selection<K> {\n readonly ids: readonly K[];\n readonly anchor: K | null;\n}\n\n/** What a press is resolved against besides the selection: the order a range walks, and which ids may be multi-selected. */\nexport interface SelectContext<K> {\n /** The sequence a range walks. A range without one replaces. */\n readonly order?: readonly K[];\n /** Whether an id may join a multi-selection. Ineligible ids are skipped by a range, dropped\n * when the selection grows, and selected alone when pressed. Default: every id. */\n readonly eligible?: (id: K) => boolean;\n}\n\n/** The intent a press carries under `policy`. A range key beats a toggle key. */\nexport function intentOf(modifiers: SelectModifiers, policy: SelectPolicy): SelectIntent {\n if (policy.mode === 'single') return 'replace';\n if (policy.range && modifiers[policy.range]) return 'range';\n const toggle = typeof policy.toggle === 'string' ? [policy.toggle] : (policy.toggle ?? []);\n return toggle.some((key) => modifiers[key]) ? 'toggle' : 'replace';\n}\n\n/** The eligible ids from `from` to `to` in `order`, ends included, in order. Empty when either\n * end is not in `order`. */\nexport function rangeOf<K>(order: readonly K[], from: K, to: K, eligible: (id: K) => boolean = always): K[] {\n const a = order.indexOf(from);\n const b = order.indexOf(to);\n if (a < 0 || b < 0) return [];\n return order.slice(Math.min(a, b), Math.max(a, b) + 1).filter(eligible);\n}\n\n/** The selection after pressing `id` with `intent`. Returns `state` itself when nothing changes\n * hands, so a caller can compare by reference. */\nexport function select<K>(state: Selection<K>, id: K, intent: SelectIntent, context: SelectContext<K> = {}): Selection<K> {\n const eligible = context.eligible ?? always;\n if (intent === 'toggle' && eligible(id)) {\n const kept = state.ids.filter(eligible);\n return { ids: kept.includes(id) ? kept.filter((x) => x !== id) : [...kept, id], anchor: id };\n }\n if (intent === 'range' && state.anchor !== null && context.order) {\n const { order } = context;\n if (order.includes(state.anchor) && order.includes(id)) {\n const ids = rangeOf(order, state.anchor, id, eligible);\n return ids.length > 0 ? settle(state, { ids, anchor: state.anchor }) : state;\n }\n }\n return settle(state, { ids: [id], anchor: id });\n}\n\nfunction settle<K>(state: Selection<K>, next: Selection<K>): Selection<K> {\n const same = next.anchor === state.anchor\n && next.ids.length === state.ids.length\n && next.ids.every((id, i) => id === state.ids[i]);\n return same ? state : next;\n}\n\nfunction always(): boolean {\n return true;\n}\n","import { createEmitter } from '@weasel-js/registry';\n\n/** Somewhere a selection can live outside the component that edits it. core's `Scene` satisfies\n * it, and so does any store with the same three methods. */\nexport interface SelectionStore<K> {\n getSelection(): readonly K[];\n setSelection(ids: readonly K[]): void;\n subscribe(listener: () => void): () => void;\n}\n\n/** A {@link SelectionStore} holding the ids itself. A listener reads the new value. */\nexport function createSelectionStore<K>(initial: readonly K[] = []): SelectionStore<K> {\n let ids = initial;\n const changed = createEmitter();\n return {\n getSelection: () => ids,\n setSelection(next) {\n ids = next;\n changed.emit();\n },\n subscribe: changed.subscribe,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/select.ts","../src/tree.ts","../src/store.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** `single` always replaces; `multi` lets a policy's keys toggle and range. */\nexport type SelectionMode = 'single' | 'multi';\n\n/** A modifier key a policy can assign to toggling or ranging. */\nexport type SelectionExtendKey = 'shift' | 'meta' | 'ctrl';\n\n/** The modifiers held during a press. */\nexport type SelectModifiers = Readonly<Record<SelectionExtendKey, boolean>>;\n\n/** What a press does to the selection. */\nexport type SelectIntent = 'replace' | 'toggle' | 'range';\n\n/** Which keys toggle and which range, so a surface states its convention: a list usually\n * toggles on Cmd/Ctrl and ranges on shift, a canvas toggles on shift. */\nexport interface SelectPolicy {\n readonly mode: SelectionMode;\n readonly toggle?: SelectionExtendKey | readonly SelectionExtendKey[];\n readonly range?: SelectionExtendKey;\n}\n\n/** What is selected, and the end a range extends from. */\nexport interface Selection<K> {\n readonly ids: readonly K[];\n readonly anchor: K | null;\n}\n\n/** What a press is resolved against besides the selection: the order a range walks, and which ids may be multi-selected. */\nexport interface SelectContext<K> {\n /** The sequence a range walks. A range without one replaces. */\n readonly order?: readonly K[];\n /** Whether an id may join a multi-selection. Ineligible ids are skipped by a range, dropped\n * when the selection grows, and selected alone when pressed. Default: every id. */\n readonly eligible?: (id: K) => boolean;\n}\n\n/** The intent a press carries under `policy`. A range key beats a toggle key. */\nexport function intentOf(modifiers: SelectModifiers, policy: SelectPolicy): SelectIntent {\n if (policy.mode === 'single') return 'replace';\n if (policy.range && modifiers[policy.range]) return 'range';\n const toggle = typeof policy.toggle === 'string' ? [policy.toggle] : (policy.toggle ?? []);\n return toggle.some((key) => modifiers[key]) ? 'toggle' : 'replace';\n}\n\n/** The eligible ids from `from` to `to` in `order`, ends included, in order. Empty when either\n * end is not in `order`. */\nexport function rangeOf<K>(order: readonly K[], from: K, to: K, eligible: (id: K) => boolean = always): K[] {\n const a = order.indexOf(from);\n const b = order.indexOf(to);\n if (a < 0 || b < 0) return [];\n return order.slice(Math.min(a, b), Math.max(a, b) + 1).filter(eligible);\n}\n\n/** The selection after pressing `id` with `intent`. Returns `state` itself when nothing changes\n * hands, so a caller can compare by reference. */\nexport function select<K>(state: Selection<K>, id: K, intent: SelectIntent, context: SelectContext<K> = {}): Selection<K> {\n const eligible = context.eligible ?? always;\n if (intent === 'toggle' && eligible(id)) {\n const kept = state.ids.filter(eligible);\n return { ids: kept.includes(id) ? kept.filter((x) => x !== id) : [...kept, id], anchor: id };\n }\n if (intent === 'range' && state.anchor !== null && context.order) {\n const { order } = context;\n if (order.includes(state.anchor) && order.includes(id)) {\n const ids = rangeOf(order, state.anchor, id, eligible);\n return ids.length > 0 ? settle(state, { ids, anchor: state.anchor }) : state;\n }\n }\n return settle(state, { ids: [id], anchor: id });\n}\n\nfunction settle<K>(state: Selection<K>, next: Selection<K>): Selection<K> {\n const same = next.anchor === state.anchor\n && next.ids.length === state.ids.length\n && next.ids.every((id, i) => id === state.ids[i]);\n return same ? state : next;\n}\n\nfunction always(): boolean {\n return true;\n}\n","/** A tree as a selection reads it: each id's parent, or `null` (or `undefined`) at a root. */\nexport type ParentOf<K> = (id: K) => K | null | undefined;\n\n/** The selected id that holds `id`: `id` itself when it is selected, else its nearest selected\n * ancestor, else `null`. A node is selected whenever something holds it. */\nexport function holderOf<K>(selected: ReadonlySet<K>, id: K, parentOf: ParentOf<K>): K | null {\n for (let at: K | null | undefined = id; at != null; at = parentOf(at)) {\n if (selected.has(at)) return at;\n }\n return null;\n}\n\n/** `ids` less every id an ancestor in `ids` already holds, in their order: what a selection over\n * a tree keeps explicitly. Returns `ids` itself when none is dropped. */\nexport function outermost<K>(ids: readonly K[], parentOf: ParentOf<K>): readonly K[] {\n if (ids.length < 2) return ids;\n const selected = new Set(ids);\n const held = (id: K): boolean => {\n const parent = parentOf(id);\n return parent != null && holderOf(selected, parent, parentOf) !== null;\n };\n return ids.some(held) ? ids.filter((id) => !held(id)) : ids;\n}\n","import { createEmitter } from '@weasel-js/registry';\n\n/** Somewhere a selection can live outside the component that edits it. core's `Scene` satisfies\n * it, and so does any store with the same three methods. */\nexport interface SelectionStore<K> {\n getSelection(): readonly K[];\n setSelection(ids: readonly K[]): void;\n subscribe(listener: () => void): () => void;\n}\n\n/** A {@link SelectionStore} holding the ids itself. A listener reads the new value. */\nexport function createSelectionStore<K>(initial: readonly K[] = []): SelectionStore<K> {\n let ids = initial;\n const changed = createEmitter();\n return {\n getSelection: () => ids,\n setSelection(next) {\n ids = next;\n changed.emit();\n },\n subscribe: changed.subscribe,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/select",
3
- "version": "1.15.0",
3
+ "version": "1.15.2",
4
4
  "description": "Selection as a value: which ids a press selects, with toggle, range and anchor rules a surface configures. No React, no DOM.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "dependencies": {
19
- "@weasel-js/registry": "1.15.0"
19
+ "@weasel-js/registry": "1.15.2"
20
20
  },
21
21
  "author": "orochi235",
22
22
  "homepage": "https://orochi235.github.io/weasel/",