@nnkogift/dhis2-form-utils-devtools 0.1.0-alpha.5 → 0.1.0-alpha.6
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/EffectBadge.d.ts +17 -0
- package/dist/EffectBadge.js +55 -0
- package/dist/EffectBadge.js.map +1 -0
- package/dist/RuleDetailsModal.d.ts +30 -0
- package/dist/RuleDetailsModal.js +403 -0
- package/dist/RuleDetailsModal.js.map +1 -0
- package/dist/RuleDevtoolsScope.d.ts +14 -0
- package/dist/RuleDevtoolsScope.js +25 -0
- package/dist/RuleDevtoolsScope.js.map +1 -0
- package/dist/RuleGraphModal.d.ts +17 -0
- package/dist/RuleGraphModal.js +35 -0
- package/dist/RuleGraphModal.js.map +1 -0
- package/dist/RuleGraphView.d.ts +43 -0
- package/dist/RuleGraphView.js +401 -0
- package/dist/RuleGraphView.js.map +1 -0
- package/dist/RulesPanel.d.ts +19 -0
- package/dist/RulesPanel.js +463 -0
- package/dist/RulesPanel.js.map +1 -0
- package/dist/RulesPanelGraphFallback.d.ts +5 -0
- package/dist/RulesPanelGraphFallback.js +19 -0
- package/dist/RulesPanelGraphFallback.js.map +1 -0
- package/dist/TraceTimeline.d.ts +16 -0
- package/dist/TraceTimeline.js +227 -0
- package/dist/TraceTimeline.js.map +1 -0
- package/dist/attach.d.ts +12 -0
- package/dist/attach.js +14 -0
- package/dist/attach.js.map +1 -0
- package/dist/buildGraph.d.ts +25 -0
- package/dist/buildGraph.js +67 -0
- package/dist/buildGraph.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -0
- package/dist/createLabelLookup.d.ts +25 -0
- package/dist/createLabelLookup.js +81 -0
- package/dist/createLabelLookup.js.map +1 -0
- package/dist/effectStyles.d.ts +28 -0
- package/dist/effectStyles.js +142 -0
- package/dist/effectStyles.js.map +1 -0
- package/dist/formatAgo.d.ts +3 -0
- package/dist/formatAgo.js +25 -0
- package/dist/formatAgo.js.map +1 -0
- package/dist/formatRuleActionSummary.d.ts +33 -0
- package/dist/formatRuleActionSummary.js +61 -0
- package/dist/formatRuleActionSummary.js.map +1 -0
- package/dist/graphLayout.d.ts +48 -0
- package/dist/graphLayout.js +137 -0
- package/dist/graphLayout.js.map +1 -0
- package/dist/graphNodeStyles.d.ts +9 -0
- package/dist/graphNodeStyles.js +22 -0
- package/dist/graphNodeStyles.js.map +1 -0
- package/dist/i18n.d.ts +4 -0
- package/dist/i18n.js +9 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.cjs +1230 -1089
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +12 -90
- package/dist/index.d.ts +12 -90
- package/dist/index.js +5 -2240
- package/dist/index.js.map +1 -1
- package/dist/programRuleDetailQuery.d.ts +70 -0
- package/dist/programRuleDetailQuery.js +42 -0
- package/dist/programRuleDetailQuery.js.map +1 -0
- package/dist/resolveProgramRulesList.d.ts +21 -0
- package/dist/resolveProgramRulesList.js +45 -0
- package/dist/resolveProgramRulesList.js.map +1 -0
- package/dist/traceEntry.d.ts +12 -0
- package/dist/traceEntry.js +20 -0
- package/dist/traceEntry.js.map +1 -0
- package/dist/traceStore.d.ts +12 -0
- package/dist/traceStore.js +39 -0
- package/dist/traceStore.js.map +1 -0
- package/dist/useFlipReorder.d.ts +8 -0
- package/dist/useFlipReorder.js +52 -0
- package/dist/useFlipReorder.js.map +1 -0
- package/dist/useProgramRuleDetail.d.ts +17 -0
- package/dist/useProgramRuleDetail.js +30 -0
- package/dist/useProgramRuleDetail.js.map +1 -0
- package/package.json +24 -4
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RuleDevtoolsMetadata } from './createLabelLookup.js';
|
|
2
|
+
import { RuleActionLike } from './formatRuleActionSummary.js';
|
|
3
|
+
import '@nnkogift/dhis2-form-utils-metadata';
|
|
4
|
+
|
|
5
|
+
type CatalogRule = {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
condition?: string;
|
|
9
|
+
priority?: number;
|
|
10
|
+
programRuleActions: RuleActionLike[];
|
|
11
|
+
/** Stage the rule applies to, or `null` for attribute-only/global rules. */
|
|
12
|
+
programStageId: string | null;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The full program rule catalog — every rule regardless of which slot is currently
|
|
16
|
+
* being viewed. `RulesPanel` derives in-scope/out-of-scope status from `programStageId`
|
|
17
|
+
* against the slot it renders, so this must not pre-filter by stage.
|
|
18
|
+
*/
|
|
19
|
+
declare function resolveProgramRulesList(metadata: RuleDevtoolsMetadata): CatalogRule[];
|
|
20
|
+
|
|
21
|
+
export { type CatalogRule, resolveProgramRulesList };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
function sortByPriority(rules) {
|
|
2
|
+
return [...rules].sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));
|
|
3
|
+
}
|
|
4
|
+
function toCatalogRule(rule) {
|
|
5
|
+
return {
|
|
6
|
+
id: rule.id,
|
|
7
|
+
name: rule.name,
|
|
8
|
+
condition: rule.condition,
|
|
9
|
+
priority: rule.priority,
|
|
10
|
+
programRuleActions: [...rule.programRuleActions ?? []],
|
|
11
|
+
programStageId: rule.programStageId
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function resolveProgramRulesList(metadata) {
|
|
15
|
+
if (metadata.formKind === "event") {
|
|
16
|
+
return sortByPriority(
|
|
17
|
+
metadata.metadata.programRules.filter((rule) => Boolean(rule.id)).map(
|
|
18
|
+
(rule) => toCatalogRule({
|
|
19
|
+
id: rule.id,
|
|
20
|
+
name: rule.displayName ?? rule.id,
|
|
21
|
+
condition: rule.condition,
|
|
22
|
+
priority: rule.priority,
|
|
23
|
+
programRuleActions: rule.programRuleActions,
|
|
24
|
+
programStageId: rule.programStage?.id ?? null
|
|
25
|
+
})
|
|
26
|
+
)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return sortByPriority(
|
|
30
|
+
metadata.metadata.programRules.filter((rule) => Boolean(rule.id)).map(
|
|
31
|
+
(rule) => toCatalogRule({
|
|
32
|
+
id: rule.id,
|
|
33
|
+
name: rule.name ?? rule.id,
|
|
34
|
+
condition: rule.condition,
|
|
35
|
+
priority: rule.priority,
|
|
36
|
+
programRuleActions: rule.programRuleActions,
|
|
37
|
+
programStageId: rule.programStage?.id ?? null
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { resolveProgramRulesList };
|
|
44
|
+
//# sourceMappingURL=resolveProgramRulesList.js.map
|
|
45
|
+
//# sourceMappingURL=resolveProgramRulesList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/resolveProgramRulesList.ts"],"names":[],"mappings":"AAaA,SAAS,eAAe,KAAA,EAAqC;AACzD,EAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,IAAA,CAAK,CAAC,IAAA,EAAM,KAAA,KAAA,CAAW,IAAA,CAAK,QAAA,IAAY,CAAA,KAAM,KAAA,CAAM,YAAY,CAAA,CAAE,CAAA;AACxF;AAEA,SAAS,cAAc,IAAA,EAOP;AACZ,EAAA,OAAO;AAAA,IACH,IAAI,IAAA,CAAK,EAAA;AAAA,IACT,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,WAAW,IAAA,CAAK,SAAA;AAAA,IAChB,UAAU,IAAA,CAAK,QAAA;AAAA,IACf,oBAAoB,CAAC,GAAI,IAAA,CAAK,kBAAA,IAAsB,EAAG,CAAA;AAAA,IACvD,gBAAgB,IAAA,CAAK;AAAA,GACzB;AACJ;AAOO,SAAS,wBAAwB,QAAA,EAA+C;AACnF,EAAA,IAAI,QAAA,CAAS,aAAa,OAAA,EAAS;AAC/B,IAAA,OAAO,cAAA;AAAA,MACH,QAAA,CAAS,QAAA,CAAS,YAAA,CACb,MAAA,CAAO,CAAC,SAA+C,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAC,CAAA,CACvE,GAAA;AAAA,QAAI,CAAC,SACF,aAAA,CAAc;AAAA,UACV,IAAI,IAAA,CAAK,EAAA;AAAA,UACT,IAAA,EAAM,IAAA,CAAK,WAAA,IAAe,IAAA,CAAK,EAAA;AAAA,UAC/B,WAAW,IAAA,CAAK,SAAA;AAAA,UAChB,UAAU,IAAA,CAAK,QAAA;AAAA,UACf,oBAAoB,IAAA,CAAK,kBAAA;AAAA,UACzB,cAAA,EAAgB,IAAA,CAAK,YAAA,EAAc,EAAA,IAAM;AAAA,SAC5C;AAAA;AACL,KACR;AAAA,EACJ;AAEA,EAAA,OAAO,cAAA;AAAA,IACH,QAAA,CAAS,QAAA,CAAS,YAAA,CACb,MAAA,CAAO,CAAC,SAA+C,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAC,CAAA,CACvE,GAAA;AAAA,MAAI,CAAC,SACF,aAAA,CAAc;AAAA,QACV,IAAI,IAAA,CAAK,EAAA;AAAA,QACT,IAAA,EAAM,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,EAAA;AAAA,QACxB,WAAW,IAAA,CAAK,SAAA;AAAA,QAChB,UAAU,IAAA,CAAK,QAAA;AAAA,QACf,oBAAoB,IAAA,CAAK,kBAAA;AAAA,QACzB,cAAA,EAAgB,IAAA,CAAK,YAAA,EAAc,EAAA,IAAM;AAAA,OAC5C;AAAA;AACL,GACR;AACJ","file":"resolveProgramRulesList.js","sourcesContent":["import type { RuleDevtoolsMetadata } from './createLabelLookup.js';\nimport type { RuleActionLike } from './formatRuleActionSummary.js';\n\nexport type CatalogRule = {\n id: string;\n name: string;\n condition?: string;\n priority?: number;\n programRuleActions: RuleActionLike[];\n /** Stage the rule applies to, or `null` for attribute-only/global rules. */\n programStageId: string | null;\n};\n\nfunction sortByPriority(rules: CatalogRule[]): CatalogRule[] {\n return [...rules].sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));\n}\n\nfunction toCatalogRule(rule: {\n id: string;\n name: string;\n condition?: string;\n priority?: number;\n programRuleActions?: readonly RuleActionLike[] | null;\n programStageId: string | null;\n}): CatalogRule {\n return {\n id: rule.id,\n name: rule.name,\n condition: rule.condition,\n priority: rule.priority,\n programRuleActions: [...(rule.programRuleActions ?? [])],\n programStageId: rule.programStageId,\n };\n}\n\n/**\n * The full program rule catalog — every rule regardless of which slot is currently\n * being viewed. `RulesPanel` derives in-scope/out-of-scope status from `programStageId`\n * against the slot it renders, so this must not pre-filter by stage.\n */\nexport function resolveProgramRulesList(metadata: RuleDevtoolsMetadata): CatalogRule[] {\n if (metadata.formKind === 'event') {\n return sortByPriority(\n metadata.metadata.programRules\n .filter((rule): rule is typeof rule & { id: string } => Boolean(rule.id))\n .map((rule) =>\n toCatalogRule({\n id: rule.id,\n name: rule.displayName ?? rule.id,\n condition: rule.condition,\n priority: rule.priority,\n programRuleActions: rule.programRuleActions,\n programStageId: rule.programStage?.id ?? null,\n })\n )\n );\n }\n\n return sortByPriority(\n metadata.metadata.programRules\n .filter((rule): rule is typeof rule & { id: string } => Boolean(rule.id))\n .map((rule) =>\n toCatalogRule({\n id: rule.id,\n name: rule.name ?? rule.id,\n condition: rule.condition,\n priority: rule.priority,\n programRuleActions: rule.programRuleActions,\n programStageId: rule.programStage?.id ?? null,\n })\n )\n );\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RuleTraceEntry } from '@nnkogift/dhis2-form-utils-hooks';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolves which trace entry the dependency graph should render.
|
|
5
|
+
* Defaults to the latest evaluation (active rules); uses the selected
|
|
6
|
+
* trace entry when the user is inspecting a past evaluation.
|
|
7
|
+
*/
|
|
8
|
+
declare function resolveGraphTraceEntry(entries: readonly RuleTraceEntry[], selectedEntryId?: string | null): RuleTraceEntry | null;
|
|
9
|
+
/** Rule IDs that fired in the latest evaluation cycle. */
|
|
10
|
+
declare function getActiveRuleIds(entries: readonly RuleTraceEntry[]): ReadonlySet<string>;
|
|
11
|
+
|
|
12
|
+
export { getActiveRuleIds, resolveGraphTraceEntry };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function resolveGraphTraceEntry(entries, selectedEntryId) {
|
|
2
|
+
if (!entries.length) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
if (selectedEntryId) {
|
|
6
|
+
return entries.find((entry) => entry.id === selectedEntryId) ?? entries[entries.length - 1];
|
|
7
|
+
}
|
|
8
|
+
return entries[entries.length - 1];
|
|
9
|
+
}
|
|
10
|
+
function getActiveRuleIds(entries) {
|
|
11
|
+
const latest = resolveGraphTraceEntry(entries);
|
|
12
|
+
if (!latest) {
|
|
13
|
+
return /* @__PURE__ */ new Set();
|
|
14
|
+
}
|
|
15
|
+
return new Set(latest.ruleResults.map((result) => result.ruleId));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { getActiveRuleIds, resolveGraphTraceEntry };
|
|
19
|
+
//# sourceMappingURL=traceEntry.js.map
|
|
20
|
+
//# sourceMappingURL=traceEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/traceEntry.ts"],"names":[],"mappings":"AAOO,SAAS,sBAAA,CACZ,SACA,eAAA,EACqB;AACrB,EAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACjB,IAAA,OAAO,IAAA;AAAA,EACX;AACA,EAAA,IAAI,eAAA,EAAiB;AACjB,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,CAAC,KAAA,KAAU,KAAA,CAAM,EAAA,KAAO,eAAe,CAAA,IAAK,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AAAA,EAC9F;AACA,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AACrC;AAGO,SAAS,iBAAiB,OAAA,EAAyD;AACtF,EAAA,MAAM,MAAA,GAAS,uBAAuB,OAAO,CAAA;AAC7C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACT,IAAA,2BAAW,GAAA,EAAY;AAAA,EAC3B;AACA,EAAA,OAAO,IAAI,IAAI,MAAA,CAAO,WAAA,CAAY,IAAI,CAAC,MAAA,KAAW,MAAA,CAAO,MAAM,CAAC,CAAA;AACpE","file":"traceEntry.js","sourcesContent":["import type { RuleTraceEntry } from '@nnkogift/dhis2-form-utils-hooks';\n\n/**\n * Resolves which trace entry the dependency graph should render.\n * Defaults to the latest evaluation (active rules); uses the selected\n * trace entry when the user is inspecting a past evaluation.\n */\nexport function resolveGraphTraceEntry(\n entries: readonly RuleTraceEntry[],\n selectedEntryId?: string | null\n): RuleTraceEntry | null {\n if (!entries.length) {\n return null;\n }\n if (selectedEntryId) {\n return entries.find((entry) => entry.id === selectedEntryId) ?? entries[entries.length - 1];\n }\n return entries[entries.length - 1];\n}\n\n/** Rule IDs that fired in the latest evaluation cycle. */\nexport function getActiveRuleIds(entries: readonly RuleTraceEntry[]): ReadonlySet<string> {\n const latest = resolveGraphTraceEntry(entries);\n if (!latest) {\n return new Set<string>();\n }\n return new Set(latest.ruleResults.map((result) => result.ruleId));\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RuleTraceEntry } from '@nnkogift/dhis2-form-utils-hooks';
|
|
2
|
+
|
|
3
|
+
type Listener = () => void;
|
|
4
|
+
type RuleTraceStore = {
|
|
5
|
+
record: (entry: RuleTraceEntry) => void;
|
|
6
|
+
getSnapshot: () => readonly RuleTraceEntry[];
|
|
7
|
+
subscribe: (listener: Listener) => () => void;
|
|
8
|
+
dispose: () => void;
|
|
9
|
+
};
|
|
10
|
+
declare function createRuleTraceStore(maxEntries?: number): RuleTraceStore;
|
|
11
|
+
|
|
12
|
+
export { type RuleTraceStore, createRuleTraceStore };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DEFAULT_TRACE_MAX_ENTRIES } from './constants.js';
|
|
2
|
+
|
|
3
|
+
function createRuleTraceStore(maxEntries = DEFAULT_TRACE_MAX_ENTRIES) {
|
|
4
|
+
let entries = [];
|
|
5
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
6
|
+
let disposeTraceSubscription = null;
|
|
7
|
+
const notify = () => {
|
|
8
|
+
for (const listener of listeners) {
|
|
9
|
+
listener();
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
record(entry) {
|
|
14
|
+
entries = [...entries, entry].slice(-maxEntries);
|
|
15
|
+
notify();
|
|
16
|
+
},
|
|
17
|
+
getSnapshot() {
|
|
18
|
+
return entries;
|
|
19
|
+
},
|
|
20
|
+
subscribe(listener) {
|
|
21
|
+
listeners.add(listener);
|
|
22
|
+
return () => {
|
|
23
|
+
listeners.delete(listener);
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
dispose() {
|
|
27
|
+
disposeTraceSubscription?.();
|
|
28
|
+
disposeTraceSubscription = null;
|
|
29
|
+
listeners.clear();
|
|
30
|
+
},
|
|
31
|
+
bindTraceSubscription(unsubscribe) {
|
|
32
|
+
disposeTraceSubscription = unsubscribe;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { createRuleTraceStore };
|
|
38
|
+
//# sourceMappingURL=traceStore.js.map
|
|
39
|
+
//# sourceMappingURL=traceStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/traceStore.ts"],"names":[],"mappings":";;AAYO,SAAS,oBAAA,CAAqB,aAAa,yBAAA,EAA2C;AACzF,EAAA,IAAI,UAA4B,EAAC;AACjC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAc;AACpC,EAAA,IAAI,wBAAA,GAAgD,IAAA;AAEpD,EAAA,MAAM,SAAS,MAAM;AACjB,IAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAC9B,MAAA,QAAA,EAAS;AAAA,IACb;AAAA,EACJ,CAAA;AAEA,EAAA,OAAO;AAAA,IACH,OAAO,KAAA,EAAO;AACV,MAAA,OAAA,GAAU,CAAC,GAAG,OAAA,EAAS,KAAK,CAAA,CAAE,KAAA,CAAM,CAAC,UAAU,CAAA;AAC/C,MAAA,MAAA,EAAO;AAAA,IACX,CAAA;AAAA,IACA,WAAA,GAAc;AACV,MAAA,OAAO,OAAA;AAAA,IACX,CAAA;AAAA,IACA,UAAU,QAAA,EAAU;AAChB,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,MAAA,OAAO,MAAM;AACT,QAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,MAC7B,CAAA;AAAA,IACJ,CAAA;AAAA,IACA,OAAA,GAAU;AACN,MAAA,wBAAA,IAA2B;AAC3B,MAAA,wBAAA,GAA2B,IAAA;AAC3B,MAAA,SAAA,CAAU,KAAA,EAAM;AAAA,IACpB,CAAA;AAAA,IACA,sBAAsB,WAAA,EAAyB;AAC3C,MAAA,wBAAA,GAA2B,WAAA;AAAA,IAC/B;AAAA,GACJ;AAGJ","file":"traceStore.js","sourcesContent":["import type { RuleTraceEntry } from '@nnkogift/dhis2-form-utils-hooks';\nimport { DEFAULT_TRACE_MAX_ENTRIES } from './constants.js';\n\ntype Listener = () => void;\n\nexport type RuleTraceStore = {\n record: (entry: RuleTraceEntry) => void;\n getSnapshot: () => readonly RuleTraceEntry[];\n subscribe: (listener: Listener) => () => void;\n dispose: () => void;\n};\n\nexport function createRuleTraceStore(maxEntries = DEFAULT_TRACE_MAX_ENTRIES): RuleTraceStore {\n let entries: RuleTraceEntry[] = [];\n const listeners = new Set<Listener>();\n let disposeTraceSubscription: (() => void) | null = null;\n\n const notify = () => {\n for (const listener of listeners) {\n listener();\n }\n };\n\n return {\n record(entry) {\n entries = [...entries, entry].slice(-maxEntries);\n notify();\n },\n getSnapshot() {\n return entries;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n disposeTraceSubscription?.();\n disposeTraceSubscription = null;\n listeners.clear();\n },\n bindTraceSubscription(unsubscribe: () => void) {\n disposeTraceSubscription = unsubscribe;\n },\n } as RuleTraceStore & {\n bindTraceSubscription: (unsubscribe: () => void) => void;\n };\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animates list-item reordering via the FLIP technique (First-Last-Invert-Play):
|
|
3
|
+
* captures each item's position before the order changes, then on the next
|
|
4
|
+
* paint inverts the delta with a transform and plays it out to zero.
|
|
5
|
+
*/
|
|
6
|
+
declare function useFlipReorder(orderedIds: readonly string[], containerRef: React.RefObject<HTMLElement | null>): void;
|
|
7
|
+
|
|
8
|
+
export { useFlipReorder };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useRef, useLayoutEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const REORDER_DURATION_MS = 220;
|
|
4
|
+
const REORDER_EASING = "ease-out";
|
|
5
|
+
function prefersReducedMotion() {
|
|
6
|
+
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
7
|
+
}
|
|
8
|
+
function useFlipReorder(orderedIds, containerRef) {
|
|
9
|
+
const positionsRef = useRef(/* @__PURE__ */ new Map());
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
const container = containerRef.current;
|
|
12
|
+
if (!container) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const previousPositions = positionsRef.current;
|
|
16
|
+
const items = container.querySelectorAll("[data-rule-id]");
|
|
17
|
+
if (!prefersReducedMotion()) {
|
|
18
|
+
items.forEach((item) => {
|
|
19
|
+
const id = item.dataset.ruleId;
|
|
20
|
+
if (!id) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const previousRect = previousPositions.get(id);
|
|
24
|
+
if (!previousRect) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const currentRect = item.getBoundingClientRect();
|
|
28
|
+
const deltaY = previousRect.top - currentRect.top;
|
|
29
|
+
if (deltaY === 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
item.style.transition = "none";
|
|
33
|
+
item.style.transform = `translateY(${String(deltaY)}px)`;
|
|
34
|
+
item.getBoundingClientRect();
|
|
35
|
+
item.style.transition = `transform ${String(REORDER_DURATION_MS)}ms ${REORDER_EASING}`;
|
|
36
|
+
item.style.transform = "";
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const nextPositions = /* @__PURE__ */ new Map();
|
|
40
|
+
items.forEach((item) => {
|
|
41
|
+
const id = item.dataset.ruleId;
|
|
42
|
+
if (id) {
|
|
43
|
+
nextPositions.set(id, item.getBoundingClientRect());
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
positionsRef.current = nextPositions;
|
|
47
|
+
}, [orderedIds.join("|")]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { useFlipReorder };
|
|
51
|
+
//# sourceMappingURL=useFlipReorder.js.map
|
|
52
|
+
//# sourceMappingURL=useFlipReorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useFlipReorder.ts"],"names":[],"mappings":";;AAEA,MAAM,mBAAA,GAAsB,GAAA;AAC5B,MAAM,cAAA,GAAiB,UAAA;AAEvB,SAAS,oBAAA,GAAgC;AACrC,EAAA,OACI,OAAO,MAAA,KAAW,WAAA,IAClB,MAAA,CAAO,UAAA,CAAW,kCAAkC,CAAA,CAAE,OAAA;AAE9D;AAOO,SAAS,cAAA,CACZ,YACA,YAAA,EACF;AACE,EAAA,MAAM,YAAA,GAAe,MAAA,iBAA6B,IAAI,GAAA,EAAK,CAAA;AAE3D,EAAA,eAAA,CAAgB,MAAM;AAClB,IAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,IAAA,IAAI,CAAC,SAAA,EAAW;AACZ,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,oBAAoB,YAAA,CAAa,OAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,gBAAA,CAA8B,gBAAgB,CAAA;AAEtE,IAAA,IAAI,CAAC,sBAAqB,EAAG;AACzB,MAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,KAAS;AACpB,QAAA,MAAM,EAAA,GAAK,KAAK,OAAA,CAAQ,MAAA;AACxB,QAAA,IAAI,CAAC,EAAA,EAAI;AACL,UAAA;AAAA,QACJ;AACA,QAAA,MAAM,YAAA,GAAe,iBAAA,CAAkB,GAAA,CAAI,EAAE,CAAA;AAC7C,QAAA,IAAI,CAAC,YAAA,EAAc;AACf,UAAA;AAAA,QACJ;AACA,QAAA,MAAM,WAAA,GAAc,KAAK,qBAAA,EAAsB;AAC/C,QAAA,MAAM,MAAA,GAAS,YAAA,CAAa,GAAA,GAAM,WAAA,CAAY,GAAA;AAC9C,QAAA,IAAI,WAAW,CAAA,EAAG;AACd,UAAA;AAAA,QACJ;AACA,QAAA,IAAA,CAAK,MAAM,UAAA,GAAa,MAAA;AACxB,QAAA,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,CAAA,WAAA,EAAc,MAAA,CAAO,MAAM,CAAC,CAAA,GAAA,CAAA;AAEnD,QAAA,IAAA,CAAK,qBAAA,EAAsB;AAC3B,QAAA,IAAA,CAAK,MAAM,UAAA,GAAa,CAAA,UAAA,EAAa,OAAO,mBAAmB,CAAC,MAAM,cAAc,CAAA,CAAA;AACpF,QAAA,IAAA,CAAK,MAAM,SAAA,GAAY,EAAA;AAAA,MAC3B,CAAC,CAAA;AAAA,IACL;AAEA,IAAA,MAAM,aAAA,uBAAoB,GAAA,EAAqB;AAC/C,IAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,KAAS;AACpB,MAAA,MAAM,EAAA,GAAK,KAAK,OAAA,CAAQ,MAAA;AACxB,MAAA,IAAI,EAAA,EAAI;AACJ,QAAA,aAAA,CAAc,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,qBAAA,EAAuB,CAAA;AAAA,MACtD;AAAA,IACJ,CAAC,CAAA;AACD,IAAA,YAAA,CAAa,OAAA,GAAU,aAAA;AAAA,EAE3B,GAAG,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAC,CAAC,CAAA;AAC7B","file":"useFlipReorder.js","sourcesContent":["import { useLayoutEffect, useRef } from 'react';\n\nconst REORDER_DURATION_MS = 220;\nconst REORDER_EASING = 'ease-out';\n\nfunction prefersReducedMotion(): boolean {\n return (\n typeof window !== 'undefined' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n );\n}\n\n/**\n * Animates list-item reordering via the FLIP technique (First-Last-Invert-Play):\n * captures each item's position before the order changes, then on the next\n * paint inverts the delta with a transform and plays it out to zero.\n */\nexport function useFlipReorder(\n orderedIds: readonly string[],\n containerRef: React.RefObject<HTMLElement | null>\n) {\n const positionsRef = useRef<Map<string, DOMRect>>(new Map());\n\n useLayoutEffect(() => {\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n const previousPositions = positionsRef.current;\n const items = container.querySelectorAll<HTMLElement>('[data-rule-id]');\n\n if (!prefersReducedMotion()) {\n items.forEach((item) => {\n const id = item.dataset.ruleId;\n if (!id) {\n return;\n }\n const previousRect = previousPositions.get(id);\n if (!previousRect) {\n return;\n }\n const currentRect = item.getBoundingClientRect();\n const deltaY = previousRect.top - currentRect.top;\n if (deltaY === 0) {\n return;\n }\n item.style.transition = 'none';\n item.style.transform = `translateY(${String(deltaY)}px)`;\n // Force reflow so the inverted transform applies before we animate it away.\n item.getBoundingClientRect();\n item.style.transition = `transform ${String(REORDER_DURATION_MS)}ms ${REORDER_EASING}`;\n item.style.transform = '';\n });\n }\n\n const nextPositions = new Map<string, DOMRect>();\n items.forEach((item) => {\n const id = item.dataset.ruleId;\n if (id) {\n nextPositions.set(id, item.getBoundingClientRect());\n }\n });\n positionsRef.current = nextPositions;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [orderedIds.join('|')]);\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ProgramRuleDetail } from './programRuleDetailQuery.js';
|
|
2
|
+
import '@dhis2/data-engine';
|
|
3
|
+
|
|
4
|
+
type UseProgramRuleDetailResult = {
|
|
5
|
+
detail: ProgramRuleDetail | undefined;
|
|
6
|
+
loading: boolean;
|
|
7
|
+
error: Error | undefined;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Lazily fetches a single program rule's full detail (actions with resolved target names,
|
|
11
|
+
* code/description/lastUpdated) when `ruleId` is set — the catalog list already has enough
|
|
12
|
+
* (id/name/condition/priority/scope) to render rule cards, so this only runs when the details
|
|
13
|
+
* modal opens.
|
|
14
|
+
*/
|
|
15
|
+
declare function useProgramRuleDetail(ruleId: string | null): UseProgramRuleDetailResult;
|
|
16
|
+
|
|
17
|
+
export { type UseProgramRuleDetailResult, useProgramRuleDetail };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useDataQuery } from '@dhis2/app-runtime';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { programRuleDetailQuery } from './programRuleDetailQuery.js';
|
|
4
|
+
|
|
5
|
+
function isCurrentDetail(ruleId, detail) {
|
|
6
|
+
return ruleId != null && detail?.id === ruleId;
|
|
7
|
+
}
|
|
8
|
+
function resolveDetailState(ruleId, detail, hasError) {
|
|
9
|
+
const isCurrent = isCurrentDetail(ruleId, detail);
|
|
10
|
+
return {
|
|
11
|
+
detail: isCurrent ? detail : void 0,
|
|
12
|
+
loading: ruleId != null && !isCurrent && !hasError
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function useProgramRuleDetail(ruleId) {
|
|
16
|
+
const { data, error, refetch } = useDataQuery(
|
|
17
|
+
programRuleDetailQuery,
|
|
18
|
+
{ lazy: true }
|
|
19
|
+
);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (ruleId) {
|
|
22
|
+
void refetch({ ruleId });
|
|
23
|
+
}
|
|
24
|
+
}, [ruleId, refetch]);
|
|
25
|
+
return { ...resolveDetailState(ruleId, data?.programRule, Boolean(error)), error };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { useProgramRuleDetail };
|
|
29
|
+
//# sourceMappingURL=useProgramRuleDetail.js.map
|
|
30
|
+
//# sourceMappingURL=useProgramRuleDetail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useProgramRuleDetail.ts"],"names":[],"mappings":";;;;AAcA,SAAS,eAAA,CAAgB,QAAuB,MAAA,EAAgD;AAC5F,EAAA,OAAO,MAAA,IAAU,IAAA,IAAQ,MAAA,EAAQ,EAAA,KAAO,MAAA;AAC5C;AAEA,SAAS,kBAAA,CACL,MAAA,EACA,MAAA,EACA,QAAA,EACsD;AACtD,EAAA,MAAM,SAAA,GAAY,eAAA,CAAgB,MAAA,EAAQ,MAAM,CAAA;AAChD,EAAA,OAAO;AAAA,IACH,MAAA,EAAQ,YAAY,MAAA,GAAS,MAAA;AAAA,IAC7B,OAAA,EAAS,MAAA,IAAU,IAAA,IAAQ,CAAC,aAAa,CAAC;AAAA,GAC9C;AACJ;AAQO,SAAS,qBAAqB,MAAA,EAAmD;AACpF,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,OAAA,EAAQ,GAAI,YAAA;AAAA,IAC7B,sBAAA;AAAA,IACA,EAAE,MAAM,IAAA;AAAK,GACjB;AAEA,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,KAAK,OAAA,CAAQ,EAAE,MAAA,EAAQ,CAAA;AAAA,IAC3B;AAAA,EACJ,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAO,CAAC,CAAA;AAEpB,EAAA,OAAO,EAAE,GAAG,kBAAA,CAAmB,MAAA,EAAQ,IAAA,EAAM,aAAa,OAAA,CAAQ,KAAK,CAAC,CAAA,EAAG,KAAA,EAAM;AACrF","file":"useProgramRuleDetail.js","sourcesContent":["import { useDataQuery } from '@dhis2/app-runtime';\nimport { useEffect } from 'react';\nimport {\n programRuleDetailQuery,\n type ProgramRuleDetail,\n type ProgramRuleDetailQueryResult,\n} from './programRuleDetailQuery.js';\n\nexport type UseProgramRuleDetailResult = {\n detail: ProgramRuleDetail | undefined;\n loading: boolean;\n error: Error | undefined;\n};\n\nfunction isCurrentDetail(ruleId: string | null, detail: ProgramRuleDetail | undefined): boolean {\n return ruleId != null && detail?.id === ruleId;\n}\n\nfunction resolveDetailState(\n ruleId: string | null,\n detail: ProgramRuleDetail | undefined,\n hasError: boolean\n): Pick<UseProgramRuleDetailResult, 'detail' | 'loading'> {\n const isCurrent = isCurrentDetail(ruleId, detail);\n return {\n detail: isCurrent ? detail : undefined,\n loading: ruleId != null && !isCurrent && !hasError,\n };\n}\n\n/**\n * Lazily fetches a single program rule's full detail (actions with resolved target names,\n * code/description/lastUpdated) when `ruleId` is set — the catalog list already has enough\n * (id/name/condition/priority/scope) to render rule cards, so this only runs when the details\n * modal opens.\n */\nexport function useProgramRuleDetail(ruleId: string | null): UseProgramRuleDetailResult {\n const { data, error, refetch } = useDataQuery<ProgramRuleDetailQueryResult>(\n programRuleDetailQuery,\n { lazy: true }\n );\n\n useEffect(() => {\n if (ruleId) {\n void refetch({ ruleId });\n }\n }, [ruleId, refetch]);\n\n return { ...resolveDetailState(ruleId, data?.programRule, Boolean(error)), error };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nnkogift/dhis2-form-utils-devtools",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.6",
|
|
4
4
|
"description": "Developer tools for debugging DHIS2 program rules in dhis2-form-utils forms",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,6 +27,26 @@
|
|
|
27
27
|
"default": "./dist/index.cjs"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
|
+
"./effect-styles": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/effectStyles.d.ts",
|
|
33
|
+
"default": "./dist/effectStyles.js"
|
|
34
|
+
},
|
|
35
|
+
"require": {
|
|
36
|
+
"types": "./dist/index.d.cts",
|
|
37
|
+
"default": "./dist/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"./scope": {
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./dist/RuleDevtoolsScope.d.ts",
|
|
43
|
+
"default": "./dist/RuleDevtoolsScope.js"
|
|
44
|
+
},
|
|
45
|
+
"require": {
|
|
46
|
+
"types": "./dist/index.d.cts",
|
|
47
|
+
"default": "./dist/index.cjs"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
30
50
|
"./style.css": "./dist/index.css"
|
|
31
51
|
},
|
|
32
52
|
"files": [
|
|
@@ -38,8 +58,8 @@
|
|
|
38
58
|
},
|
|
39
59
|
"dependencies": {
|
|
40
60
|
"@xyflow/react": "^12.11.1",
|
|
41
|
-
"@nnkogift/dhis2-form-utils-hooks": "0.1.0-alpha.
|
|
42
|
-
"@nnkogift/dhis2-form-utils-metadata": "0.1.0-alpha.
|
|
61
|
+
"@nnkogift/dhis2-form-utils-hooks": "0.1.0-alpha.6",
|
|
62
|
+
"@nnkogift/dhis2-form-utils-metadata": "0.1.0-alpha.6"
|
|
43
63
|
},
|
|
44
64
|
"peerDependencies": {
|
|
45
65
|
"@dhis2/app-runtime": ">=3.0.0",
|
|
@@ -71,7 +91,7 @@
|
|
|
71
91
|
"node": ">=18"
|
|
72
92
|
},
|
|
73
93
|
"scripts": {
|
|
74
|
-
"build": "tsup && tailwindcss -i ./src/tailwind.css -o ./dist/index.css --minify",
|
|
94
|
+
"build": "tsup && tailwindcss -i ./src/tailwind.css -o ./dist/index.css --minify && node ../../packages/config/copyEntryDtsCts.mjs",
|
|
75
95
|
"dev": "concurrently -n tsup,css -c blue,magenta \"tsup --watch\" \"tailwindcss -i ./src/tailwind.css -o ./dist/index.css --watch\"",
|
|
76
96
|
"typecheck": "tsc --noEmit",
|
|
77
97
|
"test": "vitest run"
|