@memberjunction/ng-search 5.33.0 → 5.34.1

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 (34) hide show
  1. package/dist/__tests__/search.service.test.js +101 -0
  2. package/dist/__tests__/search.service.test.js.map +1 -1
  3. package/dist/lib/module.d.ts +6 -4
  4. package/dist/lib/module.d.ts.map +1 -1
  5. package/dist/lib/module.js +14 -4
  6. package/dist/lib/module.js.map +1 -1
  7. package/dist/lib/search-composite.component.d.ts +29 -2
  8. package/dist/lib/search-composite.component.d.ts.map +1 -1
  9. package/dist/lib/search-composite.component.js +72 -7
  10. package/dist/lib/search-composite.component.js.map +1 -1
  11. package/dist/lib/search-overlay.component.d.ts +42 -1
  12. package/dist/lib/search-overlay.component.d.ts.map +1 -1
  13. package/dist/lib/search-overlay.component.js +301 -128
  14. package/dist/lib/search-overlay.component.js.map +1 -1
  15. package/dist/lib/search-scope-child-grid.component.d.ts +166 -0
  16. package/dist/lib/search-scope-child-grid.component.d.ts.map +1 -0
  17. package/dist/lib/search-scope-child-grid.component.js +575 -0
  18. package/dist/lib/search-scope-child-grid.component.js.map +1 -0
  19. package/dist/lib/search-scope-selector.component.d.ts +71 -0
  20. package/dist/lib/search-scope-selector.component.d.ts.map +1 -0
  21. package/dist/lib/search-scope-selector.component.js +354 -0
  22. package/dist/lib/search-scope-selector.component.js.map +1 -0
  23. package/dist/lib/search-types.d.ts +18 -0
  24. package/dist/lib/search-types.d.ts.map +1 -1
  25. package/dist/lib/search-types.js.map +1 -1
  26. package/dist/lib/search.service.d.ts +36 -2
  27. package/dist/lib/search.service.d.ts.map +1 -1
  28. package/dist/lib/search.service.js +81 -2
  29. package/dist/lib/search.service.js.map +1 -1
  30. package/dist/public-api.d.ts +2 -0
  31. package/dist/public-api.d.ts.map +1 -1
  32. package/dist/public-api.js +2 -0
  33. package/dist/public-api.js.map +1 -1
  34. package/package.json +7 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-scope-child-grid.component.js","sourceRoot":"","sources":["../../src/lib/search-scope-child-grid.component.ts","../../src/lib/search-scope-child-grid.component.html"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EACH,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,KAAK,EAEL,MAAM,EACN,MAAM,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAc,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;;;;;;;;ICnD5C,8BAAgC;IAAA,6BAAe;IAAA,iBAAM;;;IAErD,8BAA8B;IAC1B,uBAAmE;IACnE,YACJ;IAAA,iBAAM;;;IADF,eACJ;IADI,iDACJ;;;IAMgB,0BAAsC;IAAA,YAAe;IAAA,iBAAK;;;IAAtD,6CAAiC;IAAC,cAAe;IAAf,kCAAe;;;IAQrD,AADJ,0BAAI,YACiE;IAC7D,YACJ;IACJ,AADI,iBAAK,EACJ;;;IAHG,cAAmC;;IACnC,cACJ;IADI,oDACJ;;;;IASoB,iCAKmE;IAA/D,oUAAU,0DAAmD,KAAC;IALlE,iBAKmE;;;;;IAD/D,AADA,+DAAoC,gDACC;;;;IAIzC,iCAMyE;IAArE,oUAAU,gEAAyD,KAAC;IANxE,iBAMyE;;;;;IADrE,AADA,AADA,iEAA4C,yCACP,gDACA;;;;IAIzC,oCAMiE;IAA7D,uUAAU,wDAAiD,KAAC;IAAC,iBAAW;;;;;IADxE,AADA,AADA,iEAA4C,yCACP,gDACA;;;;IAIzC,oCAOiE;IAA7D,uUAAU,wDAAiD,KAAC;IAAC,iBAAW;;;;;IADxE,AADA,AADA,iEAA4C,yCACP,gDACA;;;IAWjC,kCAAyB;IAAA,YAAe;IAAA,iBAAS;;;IAAzC,kCAAgB;IAAC,cAAe;IAAf,mCAAe;;;;IAPhD,kCAIiE;IAA7D,sUAAU,wDAAiD,KAAC;IAC5D,kCAAiB;IAAA,oCAAU;IAAA,iBAAS;IACpC,mJAEC;IACL,iBAAS;;;;;IANL,AADA,iEAA4C,gDACP;IAGrC,eAEC;IAFD,cAAA,+BAAqB,CAEpB;;;IAUG,kCAAkC;IAAA,YAAe;IAAA,iBAAS;;;IAAlD,qCAAyB;IAAC,cAAe;IAAf,mCAAe;;;;IANzD,kCAIiE;IAA7D,sUAAU,wDAAiD,KAAC;IAC5D,mJAEC;IACL,iBAAS;;;;;IALL,AADA,iEAA4C,gDACP;IAErC,cAEC;IAFD,6BAEC;;;;IAIL,iCAMiE;IAA7D,qUAAU,wDAAiD,KAAC;IANhE,iBAMiE;;;;;IAD7D,AADA,AADA,iEAA4C,yCACP,gDACA;;;IAnErD,0BAAsC;IA6D9B,AAXA,AAZA,AAVA,AATA,AATA,AARA,mIAAoB,6GAQF,gHASE,gHASJ,8GAUE,8GAYA,6GAWR;IAUlB,iBAAK;;;;IAvED,6CAAiC;IACjC,cAqEC;IArED,8CAAA,UAAU,oBAAV,QAAQ,oBAAR,UAAU,oBAAV,MAAM,oBAAN,QAAQ,oBAAR,QAAQ,SAqEP;;;;IAxEb,0BAAgC;IAC5B,iIAyEC;IAEG,AADJ,8BAAmC,iBAMF;IAAzB,+OAAS,wBAAc,KAAC;IACxB,wBAAoD;IAGhE,AADI,AADI,iBAAS,EACR,EACJ;;;;IArFD,uCAA2B;IAC3B,cAyEC;IAzED,6BAyEC;IAMO,eAAmC;IAAnC,2DAAmC;;;IAjFnD,2HAuFC;;;IAvFD,0BAuFC;;;;IAOL,AADJ,8BAA+B,iBACyC;IAAnB,iNAAS,eAAQ,KAAC;IAC/D,YACJ;IACJ,AADI,iBAAS,EACP;;;IAFE,eACJ;IADI,sDACJ;;;IA/GA,AADJ,AADJ,gCAAgC,YACrB,SACC;IACA,6GAEC;IACD,wBAA2D;IAEnE,AADI,iBAAK,EACD;IACR,6BAAO;IAOD,AANF,yGAAyB,6EAMhB;IA2FjB,AADI,iBAAQ,EACJ;IAER,6GAAiB;;;IA1GL,eAEC;IAFD,6BAEC;IAKL,eA+FC;IA/FD,kDA+FC;IAIT,eAMC;IAND,2CAMC;;ADnBT,MAAM,OAAO,6BAA6B;IAC9B,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,6EAA6E;IAErE,SAAS,GAAkB,IAAI,CAAC;IACxC;;;;OAIG;IACH,IACI,QAAQ,CAAC,KAAoB;QAC7B,IAAI,KAAK,KAAK,IAAI,CAAC,SAAS;YAAE,OAAO;QACrC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,KAAK,EAAE,CAAC;YACR,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACnB,CAAC;IACL,CAAC;IACD,IAAI,QAAQ;QACR,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,6EAA6E;IACpE,eAAe,GAAW,EAAE,CAAC;IAEtC,qEAAqE;IAC5D,eAAe,GAAW,eAAe,CAAC;IAEnD,oDAAoD;IAC3C,OAAO,GAAiC,EAAE,CAAC;IAEpD,iDAAiD;IACxC,cAAc,GAAW,WAAW,CAAC;IAE9C,oDAAoD;IAC3C,YAAY,GAAW,cAAc,CAAC;IAE/C,2DAA2D;IAClD,OAAO,GAAW,EAAE,CAAC;IAE9B,4CAA4C;IACnC,QAAQ,GAAY,KAAK,CAAC;IAEnC,6EAA6E;IAE7E,+DAA+D;IACrD,WAAW,GAAG,IAAI,YAAY,EAAQ,CAAC;IAEjD,oCAAoC;IAC1B,QAAQ,GAAG,IAAI,YAAY,EAAc,CAAC;IAEpD,sCAAsC;IAC5B,UAAU,GAAG,IAAI,YAAY,EAAc,CAAC;IAEtD,6EAA6E;IAEtE,IAAI,GAAc,EAAE,CAAC;IACrB,SAAS,GAAY,KAAK,CAAC;IAC3B,SAAS,GAAkB,IAAI,CAAC;IAEvC,mDAAmD;IAC3C,WAAW,GAAG,IAAI,GAAG,EAAgD,CAAC;IAEvE,WAAW;QACd,mEAAmE;IACvE,CAAC;IAED,4EAA4E;IAE5E,uCAAuC;IAChC,KAAK,CAAC,QAAQ;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YACpE,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YACf,OAAO;QACX,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAa;gBACxC,UAAU,EAAE,IAAI,CAAC,eAAe;gBAChC,WAAW,EAAE,GAAG,IAAI,CAAC,eAAe,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;gBAChF,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,oBAAoB;gBAC7C,UAAU,EAAE,eAAe;aAC9B,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,IAAI,qBAAqB,CAAC;gBAC9D,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;gBAClC,4DAA4D;gBAC5D,8DAA8D;gBAC9D,2DAA2D;gBAC3D,kCAAkC;gBAClC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpC,CAAC;gBACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBACrD,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAChC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,IAAI,CAAC,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClE,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;IACL,CAAC;IAED,yDAAyD;IAClD,KAAK,CAAC,MAAM;QACf,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAO;QACtE,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC,CAAC,uIAAuI;YAClK,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,eAAe,CAAa,IAAI,CAAC,eAAe,CAAC,CAAC;YAC1E,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACjD,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,QAAQ,CAAC,8CAA8C,IAAI,CAAC,eAAe,UACvE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACnD,EAAE,CAAC,CAAC;QACR,CAAC;IACL,CAAC;IAED,yDAAyD;IAClD,KAAK,CAAC,OAAO,CAAC,GAAY;QAC7B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAChC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACN,QAAQ,CAAC,yCAAyC,IAAI,CAAC,eAAe,KAClE,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,IAAI,eAChD,EAAE,CAAC,CAAC;gBACJ,OAAO,KAAK,CAAC;YACjB,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACxB,8EAA8E;YAC9E,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QAChB,CAAC;gBAAS,CAAC;YACP,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;IACL,CAAC;IAED,wEAAwE;IACjE,KAAK,CAAC,SAAS,CAAC,GAAY;QAC/B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,EAAE,EAAE,CAAC;oBACN,QAAQ,CAAC,2CAA2C,IAAI,CAAC,eAAe,KACpE,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,IAAI,eAChD,EAAE,CAAC,CAAC;oBACJ,OAAO;gBACX,CAAC;YACL,CAAC;YACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;YAC7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,QAAQ,CAAC,wCAAwC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzG,CAAC;IACL,CAAC;IAED,2DAA2D;IACpD,YAAY,CAAC,GAAY,EAAE,MAAkC;QAChE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,YAAY,CACrB,GAAY,EACZ,MAAkC,EAClC,KAAc;QAEd,IAAI,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO;QAC7C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxD,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpG,MAAM,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;YAC1D,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;QACxD,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,kDAAkD;IAC3C,cAAc,CAAC,GAAY,EAAE,MAAkC;QAClE,OAAO,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAChD,CAAC;IAED,uDAAuD;IAChD,gBAAgB,CACnB,MAAkC;QAElC,IAAI,CAAC,MAAM,CAAC,gBAAgB;YAAE,OAAO,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;IAC/D,CAAC;IAED,4EAA4E;IAE5E;;;OAGG;IACK,KAAK,CAAC,cAAc;QACxB,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,gBAAgB,CAAC,CAAC;QAC1F,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAiB,CAAC,CAAC,CAAC;QACrF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,OAAO;QACX,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7B,UAAU,EAAE,CAAC,CAAC,gBAAiB;YAC/B,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE;YACjC,OAAO,EAAE,GAAG,CAAC,CAAC,kBAAkB,IAAI,MAAM,MAAM;YAChD,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,kBAAkB,IAAI,MAAM,CAAC;YAC9C,UAAU,EAAE,QAAiB;SAChC,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;gBACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;oBACf,QAAQ,CAAC,gDAAgD,GAAG,CAAC,gBAAgB,KAAK,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;oBACtG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAiB,EAAE,EAAE,CAAC,CAAC;oBAChD,OAAO;gBACX,CAAC;gBACD,MAAM,YAAY,GAAG,GAAG,CAAC,kBAAkB,IAAI,MAAM,CAAC;gBACtD,MAAM,OAAO,GAAI,GAAG,CAAC,OAA0C,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACtE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;oBACnB,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;iBAC5C,CAAC,CAAC,CAAC;gBACJ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAiB,EAAE,OAAO,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,QAAQ,CAAC,gDAAgD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjH,CAAC;QAED,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC/B,CAAC;IAEO,mBAAmB;QACvB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,gBAAgB;oBAAE,SAAS;gBAC7D,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,OAAO,EAAE,KAAK,QAAQ;oBAAE,SAAS;gBACrC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;gBACjE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;gBACtD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;YACrD,CAAC;QACL,CAAC;IACL,CAAC;IAEO,OAAO,CAAC,MAAkB;QAC9B,OAAO;YACH,MAAM,EAAE,MAAM;YACd,YAAY,EAAE,EAAE;YAChB,MAAM,EAAE,KAAK;SAChB,CAAC;IACN,CAAC;IAED,sEAAsE;IAC9D,mBAAmB,CAAC,MAAkB;QAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5D,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;QACL,CAAC;IACL,CAAC;IAED;;;;;;;;;;OAUG;IACK,iBAAiB,CAAC,MAAkB;QACxC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO;aACvB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;aACxB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;aACjB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACpD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,QAAQ,CACJ,gEAAgE;gBAChE,wBAAwB,IAAI,CAAC,eAAe,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBACxE,iEAAiE;gBACjE,+CAA+C,CAClD,CAAC;QACN,CAAC;IACL,CAAC;uHAxTQ,6BAA6B;6DAA7B,6BAA6B;YCzG1C,8BAAoD;YAQ9C,AALA,AAFF,+FAAiB,yEAEO,gEAKf;YAsHb,iBAAM;;YA9HkB,wCAA2B;YAC/C,cA4HC;YA5HD,2DA4HC;;;iFDpBQ,6BAA6B;cANzC,SAAS;6BACM,KAAK,YACP,4BAA4B;;kBAerC,KAAK;;kBAeL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAKL,MAAM;;kBAGN,MAAM;;kBAGN,MAAM;;kFAvDE,6BAA6B","sourcesContent":["/**\n * @fileoverview Generic Search Scope Child Grid\n *\n * A parameter-driven grid for editing the child-collection tabs of a Search Scope:\n * - Providers (`MJ: Search Scope Providers`)\n * - External Indexes (`MJ: Search Scope External Indexes`)\n * - Entities (`MJ: Search Scope Entities`)\n * - Storage Accounts (`MJ: Search Scope Storage Accounts`)\n *\n * Also reused on the AI Agent form for assigning scopes to an agent (the\n * `MJ: AI Agent Search Scopes` join table). The `ParentID` + `ParentFieldName`\n * inputs keep it generic — parent can be a SearchScope OR an AI Agent.\n *\n * One component, many configurations. The parent passes:\n * - `ParentID` — the parent record ID (SearchScope ID, AI Agent ID, etc.)\n * - `ChildEntityName` — the MJ entity name of the child table\n * - `ParentFieldName` — the FK column on the child that references the parent\n * - `Columns` — declarative column spec (type, label, optional lookup source)\n *\n * This is a **Generic component** (see `packages/Angular/Generic/CLAUDE.md`):\n * - No Router / ActivatedRoute imports\n * - All state flows through @Input/@Output\n * - Parent owns persistence coordination if needed — rows are saved in-place on edit\n *\n * ## Runtime-generic by design (re P6.2 in plans/search-scopes-rag-plus/RAG_plan.md)\n *\n * The plan asked whether this grid should adopt the strongly-typed entity API\n * (`record.Field` accessors) instead of `BaseEntity.Get/Set`. It cannot:\n * the component is parameterized by `ChildEntityName` and `Columns[].Field`,\n * both runtime strings, so the entity class is unknown at compile time.\n * Picking a concrete type would force one consumer per child-entity flavor\n * (SearchScopeProvider, SearchScopeEntity, SearchScopeExternalIndex,\n * SearchScopeStorageAccount, AIAgentSearchScope, plus future ones), defeating\n * the point of the abstraction.\n *\n * Mitigation: a runtime sanity check (`Entity.GetFieldByName`) confirms each\n * column's `Field` name exists on the loaded entity before any Get/Set is\n * issued. This catches typos / schema drift at first render rather than\n * silently writing to a non-existent property. See `assertFieldsExist()`.\n *\n * @module @memberjunction/ng-search\n */\n\nimport {\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n Output,\n inject\n} from '@angular/core';\nimport { BaseEntity, LogError, Metadata, RunView } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/** Supported inline-edit types for a scope-child grid column. */\nexport type SearchScopeChildGridColumnType =\n | 'text'\n | 'number'\n | 'checkbox'\n | 'code' // Monospace textarea (Nunjucks filters, SQL, JSON)\n | 'textarea'\n | 'lookup' // Dropdown populated from a related entity\n | 'select'; // Dropdown populated from a fixed option list\n\n/** Declarative spec for a single grid column. */\nexport interface SearchScopeChildGridColumn {\n /** BaseEntity field name (e.g. 'SearchProviderID'). */\n Field: string;\n /** Column header label. */\n Label: string;\n /** Editor type. */\n Type: SearchScopeChildGridColumnType;\n /** Placeholder text for empty cells. */\n Placeholder?: string;\n /** For 'lookup': entity name whose rows populate the dropdown. */\n LookupEntityName?: string;\n /** For 'lookup': which field of the lookup entity is displayed. Defaults to 'Name'. */\n LookupDisplayField?: string;\n /** For 'lookup': optional ExtraFilter to constrain lookup rows. */\n LookupFilter?: string;\n /** For 'select': fixed option list. */\n Options?: Array<{ Label: string; Value: string | number | boolean }>;\n /** CSS width (e.g. '140px'). */\n Width?: string;\n /** True to make the cell read-only. */\n ReadOnly?: boolean;\n}\n\n/** Row as rendered in the grid. Wraps a BaseEntity with a transient \"dirty\" flag. */\ninterface GridRow {\n /** The underlying BaseEntity record. */\n Entity: BaseEntity;\n /** Cached display value for lookup columns, keyed by field name. */\n LookupLabels: Record<string, string>;\n /** True while an inline save is in flight. */\n Saving: boolean;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-search-scope-child-grid',\n templateUrl: './search-scope-child-grid.component.html',\n styleUrls: ['./search-scope-child-grid.component.css']\n})\nexport class SearchScopeChildGridComponent implements OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n // ─── Inputs ───────────────────────────────────────────────────────────────\n\n private _parentID: string | null = null;\n /**\n * The parent record ID to filter / attach rows to. For a `MJ: Search Scopes` parent\n * this is the scope ID; for an `MJ: AI Agents` parent (assigning scopes to an agent)\n * this is the agent ID. Paired with `ParentFieldName` which names the FK column.\n */\n @Input()\n set ParentID(value: string | null) {\n if (value === this._parentID) return;\n this._parentID = value;\n if (value) {\n void this.LoadRows();\n } else {\n this.Rows = [];\n }\n }\n get ParentID(): string | null {\n return this._parentID;\n }\n\n /** MJ entity name of the child table (e.g. `MJ: Search Scope Providers`). */\n @Input() ChildEntityName: string = '';\n\n /** FK column on the child that references the parent SearchScope. */\n @Input() ParentFieldName: string = 'SearchScopeID';\n\n /** Column config — drives rendering of each row. */\n @Input() Columns: SearchScopeChildGridColumn[] = [];\n\n /** Button label for the \"add new row\" action. */\n @Input() AddButtonLabel: string = '+ Add row';\n\n /** Empty-state copy when the grid has zero rows. */\n @Input() EmptyMessage: string = 'No rows yet.';\n\n /** Optional OrderBy clause applied to the initial load. */\n @Input() OrderBy: string = '';\n\n /** Disable all editing (read-only mode). */\n @Input() Disabled: boolean = false;\n\n // ─── Outputs ──────────────────────────────────────────────────────────────\n\n /** Emitted whenever a row is successfully added or removed. */\n @Output() RowsChanged = new EventEmitter<void>();\n\n /** Emitted after a row is added. */\n @Output() RowAdded = new EventEmitter<BaseEntity>();\n\n /** Emitted after a row is removed. */\n @Output() RowRemoved = new EventEmitter<BaseEntity>();\n\n // ─── State ────────────────────────────────────────────────────────────────\n\n public Rows: GridRow[] = [];\n public IsLoading: boolean = false;\n public LoadError: string | null = null;\n\n /** Cached lookup options by lookup entity name. */\n private lookupCache = new Map<string, Array<{ ID: string; Label: string }>>();\n\n public ngOnDestroy(): void {\n // No subscriptions to clean up yet; kept for future compatibility.\n }\n\n // ─── Public API ──────────────────────────────────────────────────────────\n\n /** Reload the grid from the server. */\n public async LoadRows(): Promise<void> {\n if (!this._parentID || !this.ChildEntityName || !this.ParentFieldName) {\n this.Rows = [];\n return;\n }\n this.IsLoading = true;\n this.LoadError = null;\n try {\n const rv = new RunView();\n const result = await rv.RunView<BaseEntity>({\n EntityName: this.ChildEntityName,\n ExtraFilter: `${this.ParentFieldName} = '${this._parentID.replace(/'/g, \"''\")}'`,\n OrderBy: this.OrderBy || '__mj_CreatedAt ASC',\n ResultType: 'entity_object'\n });\n if (!result.Success) {\n this.LoadError = result.ErrorMessage ?? 'Failed to load rows';\n this.Rows = [];\n } else {\n const rows = result.Results || [];\n // Runtime sanity check: every column's Field must be a real\n // property on the loaded entity. Catches schema drift between\n // a parent form's Columns spec and the actual entity shape\n // before the first Get/Set fires.\n if (rows.length > 0) {\n this.assertFieldsExist(rows[0]);\n }\n this.Rows = rows.map(entity => this.makeRow(entity));\n await this.hydrateLookups();\n }\n } catch (err) {\n this.LoadError = err instanceof Error ? err.message : String(err);\n this.Rows = [];\n } finally {\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Add a new empty row attached to the current scope. */\n public async AddRow(): Promise<void> {\n if (this.Disabled || !this._parentID || !this.ChildEntityName) return;\n try {\n const md = new Metadata(); // global-provider-ok: child grid does not yet accept a Provider input — multi-provider threading is the team's separate Phase 6 effort\n const entity = await md.GetEntityObject<BaseEntity>(this.ChildEntityName);\n entity.Set(this.ParentFieldName, this._parentID);\n this.applyColumnDefaults(entity);\n this.Rows = [...this.Rows, this.makeRow(entity)];\n this.cdr.detectChanges();\n } catch (err) {\n LogError(`SearchScopeChildGrid: failed to create new ${this.ChildEntityName} row — ${\n err instanceof Error ? err.message : String(err)\n }`);\n }\n }\n\n /** Persist changes to a row. Returns true on success. */\n public async SaveRow(row: GridRow): Promise<boolean> {\n if (this.Disabled) return false;\n row.Saving = true;\n try {\n const ok = await row.Entity.Save();\n if (!ok) {\n LogError(`SearchScopeChildGrid: save failed for ${this.ChildEntityName}: ${\n row.Entity.LatestResult?.CompleteMessage ?? 'unknown error'\n }`);\n return false;\n }\n this.RowsChanged.emit();\n // If this was a brand-new row, emit the Added event on first successful save.\n if (!row.Entity.IsSaved) return true;\n this.RowAdded.emit(row.Entity);\n return true;\n } finally {\n row.Saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Remove a row (deletes from DB if saved, drops from state if new). */\n public async RemoveRow(row: GridRow): Promise<void> {\n if (this.Disabled) return;\n try {\n if (row.Entity.IsSaved) {\n const ok = await row.Entity.Delete();\n if (!ok) {\n LogError(`SearchScopeChildGrid: delete failed for ${this.ChildEntityName}: ${\n row.Entity.LatestResult?.CompleteMessage ?? 'unknown error'\n }`);\n return;\n }\n }\n this.Rows = this.Rows.filter(r => r !== row);\n this.RowRemoved.emit(row.Entity);\n this.RowsChanged.emit();\n this.cdr.detectChanges();\n } catch (err) {\n LogError(`SearchScopeChildGrid: remove error — ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /** Read a cell value from a row (used by the template). */\n public GetCellValue(row: GridRow, column: SearchScopeChildGridColumn): unknown {\n return row.Entity.Get(column.Field);\n }\n\n /**\n * Write a cell value back to the row and persist. Lookup columns store the ID but\n * we also refresh the cached display label so the UI stays in sync without a reload.\n */\n public async SetCellValue(\n row: GridRow,\n column: SearchScopeChildGridColumn,\n value: unknown\n ): Promise<void> {\n if (this.Disabled || column.ReadOnly) return;\n row.Entity.Set(column.Field, value);\n if (column.Type === 'lookup' && typeof value === 'string') {\n const options = column.LookupEntityName ? this.lookupCache.get(column.LookupEntityName) : undefined;\n const match = options?.find(o => UUIDsEqual(o.ID, value));\n row.LookupLabels[column.Field] = match?.Label ?? '';\n }\n await this.SaveRow(row);\n }\n\n /** Return the display label for a lookup cell. */\n public GetLookupLabel(row: GridRow, column: SearchScopeChildGridColumn): string {\n return row.LookupLabels[column.Field] ?? '';\n }\n\n /** Return the dropdown options for a lookup column. */\n public GetLookupOptions(\n column: SearchScopeChildGridColumn\n ): Array<{ ID: string; Label: string }> {\n if (!column.LookupEntityName) return [];\n return this.lookupCache.get(column.LookupEntityName) ?? [];\n }\n\n // ─── Internals ───────────────────────────────────────────────────────────\n\n /**\n * Load the set of lookup-option arrays referenced by `Columns` so dropdowns render\n * without triggering additional RunView calls per row.\n */\n private async hydrateLookups(): Promise<void> {\n const lookupColumns = this.Columns.filter(c => c.Type === 'lookup' && c.LookupEntityName);\n const toLoad = lookupColumns.filter(c => !this.lookupCache.has(c.LookupEntityName!));\n if (toLoad.length === 0) {\n this.refreshLookupLabels();\n return;\n }\n\n const rv = new RunView();\n const queries = toLoad.map(c => ({\n EntityName: c.LookupEntityName!,\n ExtraFilter: c.LookupFilter || '',\n OrderBy: `${c.LookupDisplayField ?? 'Name'} ASC`,\n Fields: ['ID', c.LookupDisplayField ?? 'Name'],\n ResultType: 'simple' as const,\n }));\n\n try {\n const results = await rv.RunViews(queries);\n toLoad.forEach((col, idx) => {\n const res = results[idx];\n if (!res.Success) {\n LogError(`SearchScopeChildGrid: lookup load failed for ${col.LookupEntityName}: ${res.ErrorMessage}`);\n this.lookupCache.set(col.LookupEntityName!, []);\n return;\n }\n const displayField = col.LookupDisplayField ?? 'Name';\n const options = (res.Results as Array<Record<string, unknown>>).map(r => ({\n ID: String(r['ID']),\n Label: String(r[displayField] ?? r['ID']),\n }));\n this.lookupCache.set(col.LookupEntityName!, options);\n });\n } catch (err) {\n LogError(`SearchScopeChildGrid: hydrateLookups error — ${err instanceof Error ? err.message : String(err)}`);\n }\n\n this.refreshLookupLabels();\n }\n\n private refreshLookupLabels(): void {\n for (const row of this.Rows) {\n for (const col of this.Columns) {\n if (col.Type !== 'lookup' || !col.LookupEntityName) continue;\n const id = row.Entity.Get(col.Field);\n if (typeof id !== 'string') continue;\n const options = this.lookupCache.get(col.LookupEntityName) ?? [];\n const match = options.find(o => UUIDsEqual(o.ID, id));\n row.LookupLabels[col.Field] = match?.Label ?? '';\n }\n }\n }\n\n private makeRow(entity: BaseEntity): GridRow {\n return {\n Entity: entity,\n LookupLabels: {},\n Saving: false,\n };\n }\n\n /** Apply `DefaultValue` from column specs when creating a new row. */\n private applyColumnDefaults(entity: BaseEntity): void {\n for (const col of this.Columns) {\n if (col.Type === 'checkbox' && entity.Get(col.Field) === null) {\n entity.Set(col.Field, true);\n }\n }\n }\n\n /**\n * Sanity-check that every column's `Field` exists on the loaded entity.\n * The component is runtime-generic by design, but each consumer's `Columns`\n * spec must match the actual entity shape; otherwise `entity.Get(col.Field)`\n * silently returns null and `entity.Set(col.Field, ...)` silently no-ops,\n * masking schema drift. Logging here surfaces the mismatch at first render.\n *\n * `entity.Fields` is the canonical metadata-driven list of fields the\n * BaseEntity subclass exposes. Comparing the column spec's `Field` against\n * that list catches typos and stale Columns arrays in custom forms.\n */\n private assertFieldsExist(sample: BaseEntity): void {\n const knownFields = new Set(sample.Fields.map(f => f.Name.toLowerCase()));\n const unknown = this.Columns\n .filter(c => !c.ReadOnly)\n .map(c => c.Field)\n .filter(f => !knownFields.has(f.toLowerCase()));\n if (unknown.length > 0) {\n LogError(\n `SearchScopeChildGridComponent: Columns spec references fields ` +\n `not found on entity '${this.ChildEntityName}': ${unknown.join(', ')}. ` +\n `This indicates schema drift — update the parent form's Columns ` +\n `array to match the entity's typed properties.`,\n );\n }\n }\n}\n","<div class=\"scope-grid\" [class.disabled]=\"Disabled\">\n @if (IsLoading) {\n <div class=\"scope-grid-loading\">Loading&hellip;</div>\n } @else if (LoadError) {\n <div class=\"scope-grid-error\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n {{ LoadError }}\n </div>\n } @else {\n <table class=\"scope-grid-table\">\n <thead>\n <tr>\n @for (col of Columns; track col.Field) {\n <th [style.width]=\"col.Width || null\">{{ col.Label }}</th>\n }\n <th class=\"scope-grid-actions-col\" aria-hidden=\"true\"></th>\n </tr>\n </thead>\n <tbody>\n @if (Rows.length === 0) {\n <tr>\n <td [attr.colspan]=\"Columns.length + 1\" class=\"scope-grid-empty\">\n {{ EmptyMessage }}\n </td>\n </tr>\n } @else {\n @for (row of Rows; track row.Entity) {\n <tr [class.saving]=\"row.Saving\">\n @for (col of Columns; track col.Field) {\n <td [style.width]=\"col.Width || null\">\n @switch (col.Type) {\n @case ('checkbox') {\n <input\n type=\"checkbox\"\n class=\"mj-checkbox\"\n [checked]=\"!!GetCellValue(row, col)\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).checked)\">\n }\n @case ('number') {\n <input\n type=\"number\"\n class=\"mj-input scope-grid-input\"\n [value]=\"$any(GetCellValue(row, col)) ?? ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).valueAsNumber)\">\n }\n @case ('textarea') {\n <textarea\n class=\"mj-textarea scope-grid-textarea\"\n rows=\"2\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\"></textarea>\n }\n @case ('code') {\n <textarea\n class=\"mj-textarea scope-grid-code\"\n rows=\"2\"\n spellcheck=\"false\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\"></textarea>\n }\n @case ('lookup') {\n <select\n class=\"mj-input scope-grid-select\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\">\n <option value=\"\">— select —</option>\n @for (opt of GetLookupOptions(col); track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Label }}</option>\n }\n </select>\n }\n @case ('select') {\n <select\n class=\"mj-input scope-grid-select\"\n [value]=\"$any(GetCellValue(row, col)) ?? ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\">\n @for (opt of col.Options; track opt.Value) {\n <option [value]=\"$any(opt.Value)\">{{ opt.Label }}</option>\n }\n </select>\n }\n @default {\n <input\n type=\"text\"\n class=\"mj-input scope-grid-input\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\">\n }\n }\n </td>\n }\n <td class=\"scope-grid-actions-col\">\n <button\n type=\"button\"\n class=\"scope-grid-icon-btn\"\n title=\"Remove row\"\n [disabled]=\"Disabled || row.Saving\"\n (click)=\"RemoveRow(row)\">\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n </button>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (!Disabled) {\n <div class=\"scope-grid-footer\">\n <button type=\"button\" class=\"scope-grid-add-btn\" (click)=\"AddRow()\">\n {{ AddButtonLabel }}\n </button>\n </div>\n }\n }\n</div>\n"]}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @fileoverview Search Scope Selector Component
3
+ *
4
+ * Chip-group / dropdown selector that lets users pick one or more `MJ: Search Scopes`
5
+ * to constrain a search. When nothing is selected (or "Global" is chosen), behavior
6
+ * is identical to the pre-scope unconstrained search. Selection is emitted to the
7
+ * parent which feeds it into `SearchRequest.ScopeIDs`.
8
+ *
9
+ * This component is intentionally **routing-agnostic**: the parent composite component
10
+ * owns any URL/state sync. See `packages/Angular/Generic/CLAUDE.md`.
11
+ *
12
+ * @module @memberjunction/ng-search
13
+ */
14
+ import { EventEmitter, OnInit } from '@angular/core';
15
+ import { SearchScopeInfo } from './search-types';
16
+ import * as i0 from "@angular/core";
17
+ /**
18
+ * Parent passes the current selection in via `SelectedScopeIDs` and listens for
19
+ * `SelectionChange` events. The component also exposes a convenience `IsOpen`
20
+ * flag for the dropdown panel.
21
+ */
22
+ export declare class SearchScopeSelectorComponent implements OnInit {
23
+ private cdr;
24
+ private searchService;
25
+ /** Currently selected scope IDs (parent-owned state). */
26
+ SelectedScopeIDs: string[];
27
+ /** Disables interaction (e.g., while a search is running). */
28
+ Disabled: boolean;
29
+ /** Emits whenever the selection changes. Parent should mirror into `SearchRequest.ScopeIDs`. */
30
+ SelectionChange: EventEmitter<string[]>;
31
+ /** All scopes the current user can see. Populated via `SearchService.LoadScopes()`. */
32
+ AvailableScopes: SearchScopeInfo[];
33
+ /** Scopes owned by the current user (personal). */
34
+ PersonalScopes: SearchScopeInfo[];
35
+ /** Org-wide scopes. */
36
+ OrgScopes: SearchScopeInfo[];
37
+ /** The Global scope (shown separately as the default option). */
38
+ GlobalScope: SearchScopeInfo | null;
39
+ /** Whether the dropdown panel is open. */
40
+ IsOpen: boolean;
41
+ /** True while scopes are being loaded for the first time. */
42
+ IsLoading: boolean;
43
+ private destroy$;
44
+ ngOnInit(): Promise<void>;
45
+ ngOnDestroy(): void;
46
+ /**
47
+ * Toggle selection for a specific scope ID. The Global scope acts as a reset —
48
+ * choosing it clears all other selections.
49
+ */
50
+ ToggleScope(scope: SearchScopeInfo): void;
51
+ IsSelected(scopeID: string): boolean;
52
+ /**
53
+ * The current selection's display summary. Falls back to "Global" when no
54
+ * explicit scope is selected.
55
+ */
56
+ get SelectionLabel(): string;
57
+ TogglePanel(): void;
58
+ ClosePanel(): void;
59
+ /** Clear all selections (back to Global). */
60
+ ClearSelection(): void;
61
+ /**
62
+ * The icon used on the selector button — falls back to a globe for Global, or
63
+ * the first selected scope's icon for single-selection, or a generic magnifier
64
+ * otherwise.
65
+ */
66
+ get SelectionIcon(): string;
67
+ private setScopes;
68
+ static ɵfac: i0.ɵɵFactoryDeclaration<SearchScopeSelectorComponent, never>;
69
+ static ɵcmp: i0.ɵɵComponentDeclaration<SearchScopeSelectorComponent, "mj-search-scope-selector", never, { "SelectedScopeIDs": { "alias": "SelectedScopeIDs"; "required": false; }; "Disabled": { "alias": "Disabled"; "required": false; }; }, { "SelectionChange": "SelectionChange"; }, never, never, false, never>;
70
+ }
71
+ //# sourceMappingURL=search-scope-selector.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-scope-selector.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-scope-selector.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAEH,YAAY,EAGZ,MAAM,EAGT,MAAM,eAAe,CAAC;AAKvB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;;AAEjD;;;;GAIG;AACH,qBAMa,4BAA6B,YAAW,MAAM;IACvD,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,aAAa,CAAyB;IAE9C,yDAAyD;IAChD,gBAAgB,EAAE,MAAM,EAAE,CAAM;IAEzC,8DAA8D;IACrD,QAAQ,EAAE,OAAO,CAAS;IAEnC,gGAAgG;IACtF,eAAe,yBAAgC;IAEzD,uFAAuF;IAChF,eAAe,EAAE,eAAe,EAAE,CAAM;IAE/C,mDAAmD;IAC5C,cAAc,EAAE,eAAe,EAAE,CAAM;IAE9C,uBAAuB;IAChB,SAAS,EAAE,eAAe,EAAE,CAAM;IAEzC,iEAAiE;IAC1D,WAAW,EAAE,eAAe,GAAG,IAAI,CAAQ;IAElD,0CAA0C;IACnC,MAAM,EAAE,OAAO,CAAS;IAE/B,6DAA6D;IACtD,SAAS,EAAE,OAAO,CAAS;IAElC,OAAO,CAAC,QAAQ,CAAuB;IAE1B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB/B,WAAW,IAAI,IAAI;IAK1B;;;OAGG;IACI,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IA6BzC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACH,IAAW,cAAc,IAAI,MAAM,CAOlC;IAEM,WAAW,IAAI,IAAI;IAKnB,UAAU,IAAI,IAAI;IAIzB,6CAA6C;IACtC,cAAc,IAAI,IAAI;IAM7B;;;;OAIG;IACH,IAAW,aAAa,IAAI,MAAM,CAOjC;IAED,OAAO,CAAC,SAAS;yCAtIR,4BAA4B;2CAA5B,4BAA4B;CA4IxC"}
@@ -0,0 +1,354 @@
1
+ /**
2
+ * @fileoverview Search Scope Selector Component
3
+ *
4
+ * Chip-group / dropdown selector that lets users pick one or more `MJ: Search Scopes`
5
+ * to constrain a search. When nothing is selected (or "Global" is chosen), behavior
6
+ * is identical to the pre-scope unconstrained search. Selection is emitted to the
7
+ * parent which feeds it into `SearchRequest.ScopeIDs`.
8
+ *
9
+ * This component is intentionally **routing-agnostic**: the parent composite component
10
+ * owns any URL/state sync. See `packages/Angular/Generic/CLAUDE.md`.
11
+ *
12
+ * @module @memberjunction/ng-search
13
+ */
14
+ import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';
15
+ import { Subject } from 'rxjs';
16
+ import { takeUntil } from 'rxjs/operators';
17
+ import { UUIDsEqual } from '@memberjunction/global';
18
+ import { SearchService } from './search.service';
19
+ import * as i0 from "@angular/core";
20
+ const _forTrack0 = ($index, $item) => $item.ID;
21
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_1_Template(rf, ctx) { if (rf & 1) {
22
+ i0.ɵɵelementStart(0, "div", 6);
23
+ i0.ɵɵtext(1, "Loading scopes\u2026");
24
+ i0.ɵɵelementEnd();
25
+ } }
26
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
27
+ i0.ɵɵelementStart(0, "span", 15);
28
+ i0.ɵɵtext(1);
29
+ i0.ɵɵelementEnd();
30
+ } if (rf & 2) {
31
+ const ctx_r2 = i0.ɵɵnextContext(4);
32
+ i0.ɵɵadvance();
33
+ i0.ɵɵtextInterpolate(ctx_r2.GlobalScope.Description);
34
+ } }
35
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
36
+ const _r2 = i0.ɵɵgetCurrentView();
37
+ i0.ɵɵelementStart(0, "div", 7)(1, "div", 11);
38
+ i0.ɵɵtext(2, "Default");
39
+ i0.ɵɵelementEnd();
40
+ i0.ɵɵelementStart(3, "label", 12);
41
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Template_label_click_3_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.ToggleScope(ctx_r2.GlobalScope)); });
42
+ i0.ɵɵelementStart(4, "input", 13);
43
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Template_input_click_4_listener($event) { i0.ɵɵrestoreView(_r2); return i0.ɵɵresetView($event.stopPropagation()); })("change", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Template_input_change_4_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.ToggleScope(ctx_r2.GlobalScope)); });
44
+ i0.ɵɵelementEnd();
45
+ i0.ɵɵelement(5, "i", 2);
46
+ i0.ɵɵelementStart(6, "span", 14);
47
+ i0.ɵɵtext(7);
48
+ i0.ɵɵelementEnd();
49
+ i0.ɵɵconditionalCreate(8, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Conditional_8_Template, 2, 1, "span", 15);
50
+ i0.ɵɵelementEnd()();
51
+ } if (rf & 2) {
52
+ const ctx_r2 = i0.ɵɵnextContext(3);
53
+ i0.ɵɵadvance(4);
54
+ i0.ɵɵproperty("checked", ctx_r2.IsSelected(ctx_r2.GlobalScope.ID) || ctx_r2.SelectedScopeIDs.length === 0)("disabled", ctx_r2.Disabled);
55
+ i0.ɵɵadvance();
56
+ i0.ɵɵclassMap(ctx_r2.GlobalScope.Icon || "fa-solid fa-globe");
57
+ i0.ɵɵadvance(2);
58
+ i0.ɵɵtextInterpolate(ctx_r2.GlobalScope.Name);
59
+ i0.ɵɵadvance();
60
+ i0.ɵɵconditional(ctx_r2.GlobalScope.Description ? 8 : -1);
61
+ } }
62
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
63
+ i0.ɵɵelementStart(0, "span", 17);
64
+ i0.ɵɵtext(1, "Default");
65
+ i0.ɵɵelementEnd();
66
+ } }
67
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Conditional_6_Template(rf, ctx) { if (rf & 1) {
68
+ i0.ɵɵelementStart(0, "span", 15);
69
+ i0.ɵɵtext(1);
70
+ i0.ɵɵelementEnd();
71
+ } if (rf & 2) {
72
+ const s_r5 = i0.ɵɵnextContext().$implicit;
73
+ i0.ɵɵadvance();
74
+ i0.ɵɵtextInterpolate(s_r5.Description);
75
+ } }
76
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Template(rf, ctx) { if (rf & 1) {
77
+ const _r4 = i0.ɵɵgetCurrentView();
78
+ i0.ɵɵelementStart(0, "label", 12);
79
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Template_label_click_0_listener() { const s_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.ToggleScope(s_r5)); });
80
+ i0.ɵɵelementStart(1, "input", 13);
81
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Template_input_click_1_listener($event) { i0.ɵɵrestoreView(_r4); return i0.ɵɵresetView($event.stopPropagation()); })("change", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Template_input_change_1_listener() { const s_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.ToggleScope(s_r5)); });
82
+ i0.ɵɵelementEnd();
83
+ i0.ɵɵelement(2, "i", 2);
84
+ i0.ɵɵelementStart(3, "span", 14);
85
+ i0.ɵɵtext(4);
86
+ i0.ɵɵelementEnd();
87
+ i0.ɵɵconditionalCreate(5, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Conditional_5_Template, 2, 0, "span", 17);
88
+ i0.ɵɵconditionalCreate(6, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Conditional_6_Template, 2, 1, "span", 15);
89
+ i0.ɵɵelementEnd();
90
+ } if (rf & 2) {
91
+ const s_r5 = ctx.$implicit;
92
+ const ctx_r2 = i0.ɵɵnextContext(4);
93
+ i0.ɵɵadvance();
94
+ i0.ɵɵproperty("checked", ctx_r2.IsSelected(s_r5.ID))("disabled", ctx_r2.Disabled);
95
+ i0.ɵɵadvance();
96
+ i0.ɵɵclassMap(s_r5.Icon || "fa-solid fa-filter");
97
+ i0.ɵɵadvance(2);
98
+ i0.ɵɵtextInterpolate(s_r5.Name);
99
+ i0.ɵɵadvance();
100
+ i0.ɵɵconditional(s_r5.IsDefault ? 5 : -1);
101
+ i0.ɵɵadvance();
102
+ i0.ɵɵconditional(s_r5.Description ? 6 : -1);
103
+ } }
104
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
105
+ i0.ɵɵelementStart(0, "div", 7)(1, "div", 11);
106
+ i0.ɵɵtext(2, "Organization");
107
+ i0.ɵɵelementEnd();
108
+ i0.ɵɵrepeaterCreate(3, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_For_4_Template, 7, 7, "label", 16, _forTrack0);
109
+ i0.ɵɵelementEnd();
110
+ } if (rf & 2) {
111
+ const ctx_r2 = i0.ɵɵnextContext(3);
112
+ i0.ɵɵadvance(3);
113
+ i0.ɵɵrepeater(ctx_r2.OrgScopes);
114
+ } }
115
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
116
+ i0.ɵɵelementStart(0, "span", 15);
117
+ i0.ɵɵtext(1);
118
+ i0.ɵɵelementEnd();
119
+ } if (rf & 2) {
120
+ const s_r7 = i0.ɵɵnextContext().$implicit;
121
+ i0.ɵɵadvance();
122
+ i0.ɵɵtextInterpolate(s_r7.Description);
123
+ } }
124
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Template(rf, ctx) { if (rf & 1) {
125
+ const _r6 = i0.ɵɵgetCurrentView();
126
+ i0.ɵɵelementStart(0, "label", 12);
127
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Template_label_click_0_listener() { const s_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.ToggleScope(s_r7)); });
128
+ i0.ɵɵelementStart(1, "input", 13);
129
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Template_input_click_1_listener($event) { i0.ɵɵrestoreView(_r6); return i0.ɵɵresetView($event.stopPropagation()); })("change", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Template_input_change_1_listener() { const s_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.ToggleScope(s_r7)); });
130
+ i0.ɵɵelementEnd();
131
+ i0.ɵɵelement(2, "i", 2);
132
+ i0.ɵɵelementStart(3, "span", 14);
133
+ i0.ɵɵtext(4);
134
+ i0.ɵɵelementEnd();
135
+ i0.ɵɵconditionalCreate(5, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Conditional_5_Template, 2, 1, "span", 15);
136
+ i0.ɵɵelementEnd();
137
+ } if (rf & 2) {
138
+ const s_r7 = ctx.$implicit;
139
+ const ctx_r2 = i0.ɵɵnextContext(4);
140
+ i0.ɵɵadvance();
141
+ i0.ɵɵproperty("checked", ctx_r2.IsSelected(s_r7.ID))("disabled", ctx_r2.Disabled);
142
+ i0.ɵɵadvance();
143
+ i0.ɵɵclassMap(s_r7.Icon || "fa-solid fa-user");
144
+ i0.ɵɵadvance(2);
145
+ i0.ɵɵtextInterpolate(s_r7.Name);
146
+ i0.ɵɵadvance();
147
+ i0.ɵɵconditional(s_r7.Description ? 5 : -1);
148
+ } }
149
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
150
+ i0.ɵɵelementStart(0, "div", 7)(1, "div", 11);
151
+ i0.ɵɵtext(2, "Personal");
152
+ i0.ɵɵelementEnd();
153
+ i0.ɵɵrepeaterCreate(3, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_For_4_Template, 6, 6, "label", 16, _forTrack0);
154
+ i0.ɵɵelementEnd();
155
+ } if (rf & 2) {
156
+ const ctx_r2 = i0.ɵɵnextContext(3);
157
+ i0.ɵɵadvance(3);
158
+ i0.ɵɵrepeater(ctx_r2.PersonalScopes);
159
+ } }
160
+ function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Template(rf, ctx) { if (rf & 1) {
161
+ const _r1 = i0.ɵɵgetCurrentView();
162
+ i0.ɵɵconditionalCreate(0, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_0_Template, 9, 6, "div", 7);
163
+ i0.ɵɵconditionalCreate(1, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_1_Template, 5, 0, "div", 7);
164
+ i0.ɵɵconditionalCreate(2, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Conditional_2_Template, 5, 0, "div", 7);
165
+ i0.ɵɵelementStart(3, "div", 8)(4, "button", 9);
166
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r1); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.ClearSelection()); });
167
+ i0.ɵɵtext(5, "Reset to Global");
168
+ i0.ɵɵelementEnd();
169
+ i0.ɵɵelementStart(6, "button", 10);
170
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Conditional_6_Conditional_2_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.ClosePanel()); });
171
+ i0.ɵɵtext(7, "Done");
172
+ i0.ɵɵelementEnd()();
173
+ } if (rf & 2) {
174
+ const ctx_r2 = i0.ɵɵnextContext(2);
175
+ i0.ɵɵconditional(ctx_r2.GlobalScope ? 0 : -1);
176
+ i0.ɵɵadvance();
177
+ i0.ɵɵconditional(ctx_r2.OrgScopes.length > 0 ? 1 : -1);
178
+ i0.ɵɵadvance();
179
+ i0.ɵɵconditional(ctx_r2.PersonalScopes.length > 0 ? 2 : -1);
180
+ i0.ɵɵadvance(2);
181
+ i0.ɵɵproperty("disabled", ctx_r2.SelectedScopeIDs.length === 0);
182
+ } }
183
+ function SearchScopeSelectorComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
184
+ i0.ɵɵelementStart(0, "div", 5);
185
+ i0.ɵɵconditionalCreate(1, SearchScopeSelectorComponent_Conditional_6_Conditional_1_Template, 2, 0, "div", 6)(2, SearchScopeSelectorComponent_Conditional_6_Conditional_2_Template, 8, 4);
186
+ i0.ɵɵelementEnd();
187
+ } if (rf & 2) {
188
+ const ctx_r2 = i0.ɵɵnextContext();
189
+ i0.ɵɵadvance();
190
+ i0.ɵɵconditional(ctx_r2.IsLoading ? 1 : 2);
191
+ } }
192
+ /**
193
+ * Parent passes the current selection in via `SelectedScopeIDs` and listens for
194
+ * `SelectionChange` events. The component also exposes a convenience `IsOpen`
195
+ * flag for the dropdown panel.
196
+ */
197
+ export class SearchScopeSelectorComponent {
198
+ cdr = inject(ChangeDetectorRef);
199
+ searchService = inject(SearchService);
200
+ /** Currently selected scope IDs (parent-owned state). */
201
+ SelectedScopeIDs = [];
202
+ /** Disables interaction (e.g., while a search is running). */
203
+ Disabled = false;
204
+ /** Emits whenever the selection changes. Parent should mirror into `SearchRequest.ScopeIDs`. */
205
+ SelectionChange = new EventEmitter();
206
+ /** All scopes the current user can see. Populated via `SearchService.LoadScopes()`. */
207
+ AvailableScopes = [];
208
+ /** Scopes owned by the current user (personal). */
209
+ PersonalScopes = [];
210
+ /** Org-wide scopes. */
211
+ OrgScopes = [];
212
+ /** The Global scope (shown separately as the default option). */
213
+ GlobalScope = null;
214
+ /** Whether the dropdown panel is open. */
215
+ IsOpen = false;
216
+ /** True while scopes are being loaded for the first time. */
217
+ IsLoading = false;
218
+ destroy$ = new Subject();
219
+ async ngOnInit() {
220
+ this.IsLoading = true;
221
+ try {
222
+ await this.searchService.LoadScopes();
223
+ }
224
+ finally {
225
+ this.IsLoading = false;
226
+ }
227
+ this.searchService.Scopes$
228
+ .pipe(takeUntil(this.destroy$))
229
+ .subscribe(scopes => {
230
+ this.setScopes(scopes);
231
+ this.cdr.markForCheck();
232
+ });
233
+ }
234
+ ngOnDestroy() {
235
+ this.destroy$.next();
236
+ this.destroy$.complete();
237
+ }
238
+ /**
239
+ * Toggle selection for a specific scope ID. The Global scope acts as a reset —
240
+ * choosing it clears all other selections.
241
+ */
242
+ ToggleScope(scope) {
243
+ if (this.Disabled)
244
+ return;
245
+ const isSelected = this.IsSelected(scope.ID);
246
+ if (scope.IsGlobal) {
247
+ // Global is mutually exclusive — selecting it clears all others.
248
+ const next = isSelected ? [] : [scope.ID];
249
+ this.SelectedScopeIDs = next;
250
+ this.SelectionChange.emit(next);
251
+ return;
252
+ }
253
+ let next;
254
+ if (isSelected) {
255
+ next = this.SelectedScopeIDs.filter(id => !UUIDsEqual(id, scope.ID));
256
+ }
257
+ else {
258
+ // Selecting a specific scope removes any Global selection.
259
+ const withoutGlobal = this.SelectedScopeIDs.filter(id => {
260
+ const s = this.AvailableScopes.find(x => UUIDsEqual(x.ID, id));
261
+ return s && !s.IsGlobal;
262
+ });
263
+ next = [...withoutGlobal, scope.ID];
264
+ }
265
+ this.SelectedScopeIDs = next;
266
+ this.SelectionChange.emit(next);
267
+ }
268
+ IsSelected(scopeID) {
269
+ return this.SelectedScopeIDs.includes(scopeID);
270
+ }
271
+ /**
272
+ * The current selection's display summary. Falls back to "Global" when no
273
+ * explicit scope is selected.
274
+ */
275
+ get SelectionLabel() {
276
+ if (this.SelectedScopeIDs.length === 0)
277
+ return 'Global';
278
+ if (this.SelectedScopeIDs.length === 1) {
279
+ const s = this.AvailableScopes.find(x => UUIDsEqual(x.ID, this.SelectedScopeIDs[0]));
280
+ return s?.Name ?? 'Scoped';
281
+ }
282
+ return `${this.SelectedScopeIDs.length} scopes`;
283
+ }
284
+ TogglePanel() {
285
+ if (this.Disabled)
286
+ return;
287
+ this.IsOpen = !this.IsOpen;
288
+ }
289
+ ClosePanel() {
290
+ this.IsOpen = false;
291
+ }
292
+ /** Clear all selections (back to Global). */
293
+ ClearSelection() {
294
+ if (this.Disabled)
295
+ return;
296
+ this.SelectedScopeIDs = [];
297
+ this.SelectionChange.emit([]);
298
+ }
299
+ /**
300
+ * The icon used on the selector button — falls back to a globe for Global, or
301
+ * the first selected scope's icon for single-selection, or a generic magnifier
302
+ * otherwise.
303
+ */
304
+ get SelectionIcon() {
305
+ if (this.SelectedScopeIDs.length === 0)
306
+ return 'fa-solid fa-globe';
307
+ if (this.SelectedScopeIDs.length === 1) {
308
+ const s = this.AvailableScopes.find(x => UUIDsEqual(x.ID, this.SelectedScopeIDs[0]));
309
+ return s?.Icon || 'fa-solid fa-filter';
310
+ }
311
+ return 'fa-solid fa-layer-group';
312
+ }
313
+ setScopes(scopes) {
314
+ this.AvailableScopes = scopes;
315
+ this.GlobalScope = scopes.find(s => s.IsGlobal) ?? null;
316
+ this.PersonalScopes = scopes.filter(s => s.IsPersonal && !s.IsGlobal);
317
+ this.OrgScopes = scopes.filter(s => !s.IsPersonal && !s.IsGlobal);
318
+ }
319
+ static ɵfac = function SearchScopeSelectorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchScopeSelectorComponent)(); };
320
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchScopeSelectorComponent, selectors: [["mj-search-scope-selector"]], inputs: { SelectedScopeIDs: "SelectedScopeIDs", Disabled: "Disabled" }, outputs: { SelectionChange: "SelectionChange" }, standalone: false, decls: 7, vars: 8, consts: [[1, "scope-selector"], ["type", "button", 1, "scope-trigger", 3, "click"], ["aria-hidden", "true"], [1, "scope-label"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down", "scope-chevron"], ["role", "dialog", "aria-label", "Search scopes", 1, "scope-panel"], [1, "scope-loading"], [1, "scope-section"], [1, "scope-panel-footer"], ["type", "button", 1, "scope-link", 3, "click", "disabled"], ["type", "button", 1, "scope-link", "primary", 3, "click"], [1, "scope-section-header"], [1, "scope-option", 3, "click"], ["type", "checkbox", 3, "click", "change", "checked", "disabled"], [1, "scope-option-name"], [1, "scope-option-desc"], [1, "scope-option"], [1, "scope-option-badge"]], template: function SearchScopeSelectorComponent_Template(rf, ctx) { if (rf & 1) {
321
+ i0.ɵɵelementStart(0, "div", 0)(1, "button", 1);
322
+ i0.ɵɵlistener("click", function SearchScopeSelectorComponent_Template_button_click_1_listener() { return ctx.TogglePanel(); });
323
+ i0.ɵɵelement(2, "i", 2);
324
+ i0.ɵɵelementStart(3, "span", 3);
325
+ i0.ɵɵtext(4);
326
+ i0.ɵɵelementEnd();
327
+ i0.ɵɵelement(5, "i", 4);
328
+ i0.ɵɵelementEnd();
329
+ i0.ɵɵconditionalCreate(6, SearchScopeSelectorComponent_Conditional_6_Template, 3, 1, "div", 5);
330
+ i0.ɵɵelementEnd();
331
+ } if (rf & 2) {
332
+ i0.ɵɵclassProp("disabled", ctx.Disabled);
333
+ i0.ɵɵadvance();
334
+ i0.ɵɵattribute("aria-expanded", ctx.IsOpen)("aria-disabled", ctx.Disabled);
335
+ i0.ɵɵadvance();
336
+ i0.ɵɵclassMap(ctx.SelectionIcon);
337
+ i0.ɵɵadvance(2);
338
+ i0.ɵɵtextInterpolate(ctx.SelectionLabel);
339
+ i0.ɵɵadvance(2);
340
+ i0.ɵɵconditional(ctx.IsOpen ? 6 : -1);
341
+ } }, styles: ["[_nghost-%COMP%] {\n display: inline-block;\n position: relative;\n font-family: inherit;\n}\n\n.scope-selector[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.scope-selector.disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n pointer-events: none;\n}\n\n.scope-trigger[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.875rem;\n line-height: 1.25rem;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.scope-trigger[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n}\n\n.scope-trigger[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 1px;\n}\n\n.scope-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.scope-chevron[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.scope-panel[_ngcontent-%COMP%] {\n position: absolute;\n z-index: 1000;\n top: calc(100% + 4px);\n left: 0;\n min-width: 340px;\n max-width: 440px;\n max-height: 480px;\n overflow-y: auto;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n padding: 8px 0;\n}\n\n.scope-loading[_ngcontent-%COMP%] {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.875rem;\n}\n\n.scope-section[_ngcontent-%COMP%] {\n padding: 4px 0;\n}\n\n.scope-section[_ngcontent-%COMP%] + .scope-section[_ngcontent-%COMP%] {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.scope-section-header[_ngcontent-%COMP%] {\n padding: 6px 16px 4px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.scope-option[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 16px 18px 1fr auto;\n align-items: center;\n gap: 8px;\n padding: 6px 16px;\n cursor: pointer;\n font-size: 0.875rem;\n color: var(--mj-text-primary);\n transition: background 120ms ease;\n}\n\n.scope-option[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.scope-option[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%] {\n accent-color: var(--mj-brand-primary);\n margin: 0;\n}\n\n.scope-option[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n.scope-option-name[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.scope-option-desc[_ngcontent-%COMP%] {\n grid-column: 3 / -1;\n margin-top: 2px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n line-height: 1.2rem;\n}\n\n.scope-option-badge[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.scope-panel-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 12px 4px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 4px;\n}\n\n.scope-link[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n padding: 6px 10px;\n color: var(--mj-text-link);\n font-size: 0.8125rem;\n cursor: pointer;\n border-radius: 4px;\n}\n\n.scope-link[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n}\n\n.scope-link[_ngcontent-%COMP%]:disabled {\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n}\n\n.scope-link.primary[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}"] });
342
+ }
343
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchScopeSelectorComponent, [{
344
+ type: Component,
345
+ args: [{ standalone: false, selector: 'mj-search-scope-selector', template: "<div class=\"scope-selector\" [class.disabled]=\"Disabled\">\n <button\n type=\"button\"\n class=\"scope-trigger\"\n [attr.aria-expanded]=\"IsOpen\"\n [attr.aria-disabled]=\"Disabled\"\n (click)=\"TogglePanel()\">\n <i [class]=\"SelectionIcon\" aria-hidden=\"true\"></i>\n <span class=\"scope-label\">{{ SelectionLabel }}</span>\n <i class=\"fa-solid fa-chevron-down scope-chevron\" aria-hidden=\"true\"></i>\n </button>\n\n @if (IsOpen) {\n <div class=\"scope-panel\" role=\"dialog\" aria-label=\"Search scopes\">\n @if (IsLoading) {\n <div class=\"scope-loading\">Loading scopes&hellip;</div>\n } @else {\n @if (GlobalScope) {\n <div class=\"scope-section\">\n <div class=\"scope-section-header\">Default</div>\n <label class=\"scope-option\" (click)=\"ToggleScope(GlobalScope)\">\n <input\n type=\"checkbox\"\n [checked]=\"IsSelected(GlobalScope.ID) || SelectedScopeIDs.length === 0\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"ToggleScope(GlobalScope)\"\n [disabled]=\"Disabled\">\n <i [class]=\"GlobalScope.Icon || 'fa-solid fa-globe'\" aria-hidden=\"true\"></i>\n <span class=\"scope-option-name\">{{ GlobalScope.Name }}</span>\n @if (GlobalScope.Description) {\n <span class=\"scope-option-desc\">{{ GlobalScope.Description }}</span>\n }\n </label>\n </div>\n }\n\n @if (OrgScopes.length > 0) {\n <div class=\"scope-section\">\n <div class=\"scope-section-header\">Organization</div>\n @for (s of OrgScopes; track s.ID) {\n <label class=\"scope-option\" (click)=\"ToggleScope(s)\">\n <input\n type=\"checkbox\"\n [checked]=\"IsSelected(s.ID)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"ToggleScope(s)\"\n [disabled]=\"Disabled\">\n <i [class]=\"s.Icon || 'fa-solid fa-filter'\" aria-hidden=\"true\"></i>\n <span class=\"scope-option-name\">{{ s.Name }}</span>\n @if (s.IsDefault) {\n <span class=\"scope-option-badge\">Default</span>\n }\n @if (s.Description) {\n <span class=\"scope-option-desc\">{{ s.Description }}</span>\n }\n </label>\n }\n </div>\n }\n\n @if (PersonalScopes.length > 0) {\n <div class=\"scope-section\">\n <div class=\"scope-section-header\">Personal</div>\n @for (s of PersonalScopes; track s.ID) {\n <label class=\"scope-option\" (click)=\"ToggleScope(s)\">\n <input\n type=\"checkbox\"\n [checked]=\"IsSelected(s.ID)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"ToggleScope(s)\"\n [disabled]=\"Disabled\">\n <i [class]=\"s.Icon || 'fa-solid fa-user'\" aria-hidden=\"true\"></i>\n <span class=\"scope-option-name\">{{ s.Name }}</span>\n @if (s.Description) {\n <span class=\"scope-option-desc\">{{ s.Description }}</span>\n }\n </label>\n }\n </div>\n }\n\n <div class=\"scope-panel-footer\">\n <button type=\"button\" class=\"scope-link\" (click)=\"ClearSelection()\" [disabled]=\"SelectedScopeIDs.length === 0\">Reset to Global</button>\n <button type=\"button\" class=\"scope-link primary\" (click)=\"ClosePanel()\">Done</button>\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host {\n display: inline-block;\n position: relative;\n font-family: inherit;\n}\n\n.scope-selector {\n position: relative;\n}\n\n.scope-selector.disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n.scope-trigger {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.875rem;\n line-height: 1.25rem;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.scope-trigger:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n}\n\n.scope-trigger:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 1px;\n}\n\n.scope-label {\n font-weight: 500;\n}\n\n.scope-chevron {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.scope-panel {\n position: absolute;\n z-index: 1000;\n top: calc(100% + 4px);\n left: 0;\n min-width: 340px;\n max-width: 440px;\n max-height: 480px;\n overflow-y: auto;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n padding: 8px 0;\n}\n\n.scope-loading {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.875rem;\n}\n\n.scope-section {\n padding: 4px 0;\n}\n\n.scope-section + .scope-section {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.scope-section-header {\n padding: 6px 16px 4px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.scope-option {\n display: grid;\n grid-template-columns: 16px 18px 1fr auto;\n align-items: center;\n gap: 8px;\n padding: 6px 16px;\n cursor: pointer;\n font-size: 0.875rem;\n color: var(--mj-text-primary);\n transition: background 120ms ease;\n}\n\n.scope-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.scope-option input[type=\"checkbox\"] {\n accent-color: var(--mj-brand-primary);\n margin: 0;\n}\n\n.scope-option i {\n color: var(--mj-text-secondary);\n}\n\n.scope-option-name {\n font-weight: 500;\n}\n\n.scope-option-desc {\n grid-column: 3 / -1;\n margin-top: 2px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n line-height: 1.2rem;\n}\n\n.scope-option-badge {\n font-size: 0.65rem;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.scope-panel-footer {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 12px 4px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 4px;\n}\n\n.scope-link {\n background: transparent;\n border: none;\n padding: 6px 10px;\n color: var(--mj-text-link);\n font-size: 0.8125rem;\n cursor: pointer;\n border-radius: 4px;\n}\n\n.scope-link:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n}\n\n.scope-link:disabled {\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n}\n\n.scope-link.primary {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n"] }]
346
+ }], null, { SelectedScopeIDs: [{
347
+ type: Input
348
+ }], Disabled: [{
349
+ type: Input
350
+ }], SelectionChange: [{
351
+ type: Output
352
+ }] }); })();
353
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchScopeSelectorComponent, { className: "SearchScopeSelectorComponent", filePath: "src/lib/search-scope-selector.component.ts", lineNumber: 41 }); })();
354
+ //# sourceMappingURL=search-scope-selector.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-scope-selector.component.js","sourceRoot":"","sources":["../../src/lib/search-scope-selector.component.ts","../../src/lib/search-scope-selector.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Scope Selector Component\n *\n * Chip-group / dropdown selector that lets users pick one or more `MJ: Search Scopes`\n * to constrain a search. When nothing is selected (or \"Global\" is chosen), behavior\n * is identical to the pre-scope unconstrained search. Selection is emitted to the\n * parent which feeds it into `SearchRequest.ScopeIDs`.\n *\n * This component is intentionally **routing-agnostic**: the parent composite component\n * owns any URL/state sync. See `packages/Angular/Generic/CLAUDE.md`.\n *\n * @module @memberjunction/ng-search\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n OnInit,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { SearchService } from './search.service';\nimport { SearchScopeInfo } from './search-types';\n\n/**\n * Parent passes the current selection in via `SelectedScopeIDs` and listens for\n * `SelectionChange` events. The component also exposes a convenience `IsOpen`\n * flag for the dropdown panel.\n */\n@Component({\n standalone: false,\n selector: 'mj-search-scope-selector',\n templateUrl: './search-scope-selector.component.html',\n styleUrls: ['./search-scope-selector.component.css']\n})\nexport class SearchScopeSelectorComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n private searchService = inject(SearchService);\n\n /** Currently selected scope IDs (parent-owned state). */\n @Input() SelectedScopeIDs: string[] = [];\n\n /** Disables interaction (e.g., while a search is running). */\n @Input() Disabled: boolean = false;\n\n /** Emits whenever the selection changes. Parent should mirror into `SearchRequest.ScopeIDs`. */\n @Output() SelectionChange = new EventEmitter<string[]>();\n\n /** All scopes the current user can see. Populated via `SearchService.LoadScopes()`. */\n public AvailableScopes: SearchScopeInfo[] = [];\n\n /** Scopes owned by the current user (personal). */\n public PersonalScopes: SearchScopeInfo[] = [];\n\n /** Org-wide scopes. */\n public OrgScopes: SearchScopeInfo[] = [];\n\n /** The Global scope (shown separately as the default option). */\n public GlobalScope: SearchScopeInfo | null = null;\n\n /** Whether the dropdown panel is open. */\n public IsOpen: boolean = false;\n\n /** True while scopes are being loaded for the first time. */\n public IsLoading: boolean = false;\n\n private destroy$ = new Subject<void>();\n\n public async ngOnInit(): Promise<void> {\n this.IsLoading = true;\n try {\n await this.searchService.LoadScopes();\n } finally {\n this.IsLoading = false;\n }\n\n this.searchService.Scopes$\n .pipe(takeUntil(this.destroy$))\n .subscribe(scopes => {\n this.setScopes(scopes);\n this.cdr.markForCheck();\n });\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Toggle selection for a specific scope ID. The Global scope acts as a reset —\n * choosing it clears all other selections.\n */\n public ToggleScope(scope: SearchScopeInfo): void {\n if (this.Disabled) return;\n\n const isSelected = this.IsSelected(scope.ID);\n\n if (scope.IsGlobal) {\n // Global is mutually exclusive — selecting it clears all others.\n const next = isSelected ? [] : [scope.ID];\n this.SelectedScopeIDs = next;\n this.SelectionChange.emit(next);\n return;\n }\n\n let next: string[];\n if (isSelected) {\n next = this.SelectedScopeIDs.filter(id => !UUIDsEqual(id, scope.ID));\n } else {\n // Selecting a specific scope removes any Global selection.\n const withoutGlobal = this.SelectedScopeIDs.filter(id => {\n const s = this.AvailableScopes.find(x => UUIDsEqual(x.ID, id));\n return s && !s.IsGlobal;\n });\n next = [...withoutGlobal, scope.ID];\n }\n\n this.SelectedScopeIDs = next;\n this.SelectionChange.emit(next);\n }\n\n public IsSelected(scopeID: string): boolean {\n return this.SelectedScopeIDs.includes(scopeID);\n }\n\n /**\n * The current selection's display summary. Falls back to \"Global\" when no\n * explicit scope is selected.\n */\n public get SelectionLabel(): string {\n if (this.SelectedScopeIDs.length === 0) return 'Global';\n if (this.SelectedScopeIDs.length === 1) {\n const s = this.AvailableScopes.find(x => UUIDsEqual(x.ID, this.SelectedScopeIDs[0]));\n return s?.Name ?? 'Scoped';\n }\n return `${this.SelectedScopeIDs.length} scopes`;\n }\n\n public TogglePanel(): void {\n if (this.Disabled) return;\n this.IsOpen = !this.IsOpen;\n }\n\n public ClosePanel(): void {\n this.IsOpen = false;\n }\n\n /** Clear all selections (back to Global). */\n public ClearSelection(): void {\n if (this.Disabled) return;\n this.SelectedScopeIDs = [];\n this.SelectionChange.emit([]);\n }\n\n /**\n * The icon used on the selector button — falls back to a globe for Global, or\n * the first selected scope's icon for single-selection, or a generic magnifier\n * otherwise.\n */\n public get SelectionIcon(): string {\n if (this.SelectedScopeIDs.length === 0) return 'fa-solid fa-globe';\n if (this.SelectedScopeIDs.length === 1) {\n const s = this.AvailableScopes.find(x => UUIDsEqual(x.ID, this.SelectedScopeIDs[0]));\n return s?.Icon || 'fa-solid fa-filter';\n }\n return 'fa-solid fa-layer-group';\n }\n\n private setScopes(scopes: SearchScopeInfo[]): void {\n this.AvailableScopes = scopes;\n this.GlobalScope = scopes.find(s => s.IsGlobal) ?? null;\n this.PersonalScopes = scopes.filter(s => s.IsPersonal && !s.IsGlobal);\n this.OrgScopes = scopes.filter(s => !s.IsPersonal && !s.IsGlobal);\n }\n}\n","<div class=\"scope-selector\" [class.disabled]=\"Disabled\">\n <button\n type=\"button\"\n class=\"scope-trigger\"\n [attr.aria-expanded]=\"IsOpen\"\n [attr.aria-disabled]=\"Disabled\"\n (click)=\"TogglePanel()\">\n <i [class]=\"SelectionIcon\" aria-hidden=\"true\"></i>\n <span class=\"scope-label\">{{ SelectionLabel }}</span>\n <i class=\"fa-solid fa-chevron-down scope-chevron\" aria-hidden=\"true\"></i>\n </button>\n\n @if (IsOpen) {\n <div class=\"scope-panel\" role=\"dialog\" aria-label=\"Search scopes\">\n @if (IsLoading) {\n <div class=\"scope-loading\">Loading scopes&hellip;</div>\n } @else {\n @if (GlobalScope) {\n <div class=\"scope-section\">\n <div class=\"scope-section-header\">Default</div>\n <label class=\"scope-option\" (click)=\"ToggleScope(GlobalScope)\">\n <input\n type=\"checkbox\"\n [checked]=\"IsSelected(GlobalScope.ID) || SelectedScopeIDs.length === 0\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"ToggleScope(GlobalScope)\"\n [disabled]=\"Disabled\">\n <i [class]=\"GlobalScope.Icon || 'fa-solid fa-globe'\" aria-hidden=\"true\"></i>\n <span class=\"scope-option-name\">{{ GlobalScope.Name }}</span>\n @if (GlobalScope.Description) {\n <span class=\"scope-option-desc\">{{ GlobalScope.Description }}</span>\n }\n </label>\n </div>\n }\n\n @if (OrgScopes.length > 0) {\n <div class=\"scope-section\">\n <div class=\"scope-section-header\">Organization</div>\n @for (s of OrgScopes; track s.ID) {\n <label class=\"scope-option\" (click)=\"ToggleScope(s)\">\n <input\n type=\"checkbox\"\n [checked]=\"IsSelected(s.ID)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"ToggleScope(s)\"\n [disabled]=\"Disabled\">\n <i [class]=\"s.Icon || 'fa-solid fa-filter'\" aria-hidden=\"true\"></i>\n <span class=\"scope-option-name\">{{ s.Name }}</span>\n @if (s.IsDefault) {\n <span class=\"scope-option-badge\">Default</span>\n }\n @if (s.Description) {\n <span class=\"scope-option-desc\">{{ s.Description }}</span>\n }\n </label>\n }\n </div>\n }\n\n @if (PersonalScopes.length > 0) {\n <div class=\"scope-section\">\n <div class=\"scope-section-header\">Personal</div>\n @for (s of PersonalScopes; track s.ID) {\n <label class=\"scope-option\" (click)=\"ToggleScope(s)\">\n <input\n type=\"checkbox\"\n [checked]=\"IsSelected(s.ID)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"ToggleScope(s)\"\n [disabled]=\"Disabled\">\n <i [class]=\"s.Icon || 'fa-solid fa-user'\" aria-hidden=\"true\"></i>\n <span class=\"scope-option-name\">{{ s.Name }}</span>\n @if (s.Description) {\n <span class=\"scope-option-desc\">{{ s.Description }}</span>\n }\n </label>\n }\n </div>\n }\n\n <div class=\"scope-panel-footer\">\n <button type=\"button\" class=\"scope-link\" (click)=\"ClearSelection()\" [disabled]=\"SelectedScopeIDs.length === 0\">Reset to Global</button>\n <button type=\"button\" class=\"scope-link primary\" (click)=\"ClosePanel()\">Done</button>\n </div>\n }\n </div>\n }\n</div>\n"]}
@@ -100,6 +100,24 @@ export interface SearchRequest {
100
100
  * Default: 0.35 (35%).
101
101
  */
102
102
  MinScore?: number;
103
+ /**
104
+ * Optional array of `MJ: Search Scopes` record IDs selected in the UI scope selector.
105
+ * When provided, the server resolves each scope and runs only those constrained providers.
106
+ * When omitted (or all selected scopes are `IsGlobal`), behavior is equivalent to the
107
+ * pre-scope unconstrained search.
108
+ */
109
+ ScopeIDs?: string[];
110
+ }
111
+ /** Lightweight scope metadata returned by the `SearchScopes` GraphQL query. */
112
+ export interface SearchScopeInfo {
113
+ ID: string;
114
+ Name: string;
115
+ Description?: string;
116
+ Icon?: string;
117
+ IsGlobal: boolean;
118
+ IsDefault: boolean;
119
+ /** True when the scope has an OwnerUserID — renders as a personal scope in the UI. */
120
+ IsPersonal: boolean;
103
121
  }
104
122
  /** Metadata about an active search provider from the server */
105
123
  export interface SearchProviderInfo {