@internetarchive/collection-browser 4.9.0 → 4.9.1-webdev-9670.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +11 -0
  2. package/dist/src/app-root.d.ts +11 -0
  3. package/dist/src/app-root.js +95 -1
  4. package/dist/src/app-root.js.map +1 -1
  5. package/dist/src/collection-browser.d.ts +42 -0
  6. package/dist/src/collection-browser.js +186 -2
  7. package/dist/src/collection-browser.js.map +1 -1
  8. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +12 -0
  9. package/dist/src/sort-filter-bar/sort-filter-bar.js +64 -0
  10. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  11. package/dist/src/tiles/grid/collection-tile.js +5 -0
  12. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  13. package/dist/src/tiles/grid/item-tile.d.ts +4 -0
  14. package/dist/src/tiles/grid/item-tile.js +16 -0
  15. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  16. package/dist/src/tiles/hover/hover-pane-controller.d.ts +2 -1
  17. package/dist/src/tiles/hover/hover-pane-controller.js +1 -1
  18. package/dist/src/tiles/hover/hover-pane-controller.js.map +1 -1
  19. package/dist/src/tiles/image-block.d.ts +5 -0
  20. package/dist/src/tiles/image-block.js +29 -0
  21. package/dist/src/tiles/image-block.js.map +1 -1
  22. package/dist/src/tiles/models.d.ts +2 -1
  23. package/dist/src/tiles/models.js.map +1 -1
  24. package/dist/src/tiles/preview/tile-preview-controller.d.ts +61 -0
  25. package/dist/src/tiles/preview/tile-preview-controller.js +216 -0
  26. package/dist/src/tiles/preview/tile-preview-controller.js.map +1 -0
  27. package/dist/src/tiles/preview/tile-preview-media.d.ts +44 -0
  28. package/dist/src/tiles/preview/tile-preview-media.js +151 -0
  29. package/dist/src/tiles/preview/tile-preview-media.js.map +1 -0
  30. package/dist/src/tiles/preview/tile-preview.d.ts +66 -0
  31. package/dist/src/tiles/preview/tile-preview.js +494 -0
  32. package/dist/src/tiles/preview/tile-preview.js.map +1 -0
  33. package/dist/src/tiles/tile-dispatcher.d.ts +7 -0
  34. package/dist/src/tiles/tile-dispatcher.js +31 -0
  35. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  36. package/dist/src/utils/grid-zoom-controller.d.ts +69 -0
  37. package/dist/src/utils/grid-zoom-controller.js +261 -0
  38. package/dist/src/utils/grid-zoom-controller.js.map +1 -0
  39. package/dist/src/utils/grid-zoom.d.ts +31 -0
  40. package/dist/src/utils/grid-zoom.js +109 -0
  41. package/dist/src/utils/grid-zoom.js.map +1 -0
  42. package/dist/src/utils/log.d.ts +1 -4
  43. package/package.json +5 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tile-dispatcher.js","sourceRoot":"","sources":["../../../src/tiles/tile-dispatcher.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAkB,MAAM,KAAK,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAOpC,OAAO,wBAAwB,CAAC;AAChC,OAAO,kBAAkB,CAAC;AAC1B,OAAO,qBAAqB,CAAC;AAC7B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,yBAAyB,CAAC;AACjC,OAAO,kBAAkB,CAAC;AAC1B,OAAO,0BAA0B,CAAC;AAClC,OAAO,iCAAiC,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EACL,mBAAmB,GAIpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGzD,IAAM,cAAc,GAApB,MAAM,cACX,SAAQ,iBAAiB;IADpB;QAML;;;;;;;;;;;;;;;;WAgBG;;QAI0B,iBAAY,GAAG,KAAK,CAAC;QAOrB,gBAAW,GAAG,KAAK,CAAC;QAEjD,iEAAiE;QACrC,eAAU,GAAe,SAAS,CAAC;QAE/D,uFAAuF;QAC1D,oBAAe,GAAG,KAAK,CAAC;QAEzB,qBAAgB,GAAG,GAAG,CAChD,gCAAgC,CACjC,CAAC;QAwRF;;;;WAIG;QACK,gCAA2B,GAAG,GAAS,EAAE;YAC/C,IAAI,CAAC,aAAa,CAAC,IAAI,UAAU,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC,CAAC;IAkRJ,CAAC;;IApiBC,YAAY;QACV,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,CAAC;IAChC,CAAC;IAED,YAAY;QACV,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,+EAA+E;aACvD,6BAAwB,GAG5C;QACF,IAAI,EAAE,IAAI;QACV,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;QACpB,aAAa,EAAE,KAAK;KACrB,AAR+C,CAQ9C;IAEF,MAAM;QACJ,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,KAAK,MAAM,CAAC;QACnD,MAAM,cAAc,GAAG,UAAU,IAAI,IAAI,CAAC,mBAAmB,CAAC;QAC9D,MAAM,iBAAiB,GACrB,IAAI,CAAC,mBAAmB,EAAE,WAAW,EAAE,IAAI,OAAO,CAAC;QACrD,MAAM,gBAAgB,GAAG,QAAQ,CAAC;YAChC,SAAS,EAAE,UAAU;YACrB,kBAAkB,EAAE,cAAc;SACnC,CAAC,CAAC;QACH,OAAO,IAAI,CAAA;kCACmB,gBAAgB;UACxC,IAAI,CAAC,eAAe,KAAK,aAAa;YACtC,CAAC,CAAC,IAAI,CAAC,cAAc;YACrB,CAAC,CAAC,IAAI,CAAC,YAAY;UACnB,IAAI,CAAC,uBAAuB,IAAI,IAAI,CAAC,mBAAmB;UACxD,iBAAiB;;KAEtB,CAAC;IACJ,CAAC;IAES,YAAY;QACpB,IAAI,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAChC,IAAI,CAAC,mBAAmB,GAAG,IAAI,mBAAmB,CAAC,IAAI,EAAE;gBACvD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;gBACvC,eAAe,EAAE,KAAK;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAY,cAAc;QACxB,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GACnE,IAAI,CAAC;QACP,OAAO,IAAI,CAAA;;;wBAGS,YAAY;qBACf,SAAS,IAAI,gBAAgB;4BACtB,gBAAgB;uBACrB,IAAI,CAAC,WAAW;;;KAGlC,CAAC;IACJ,CAAC;IAED,IAAY,YAAY;QACtB,OAAO,IAAI,CAAA;QACP,IAAI,CAAC,eAAe,KAAK,aAAa;YACtC,CAAC,CAAC,IAAI,CAAC,IAAI;YACX,CAAC,CAAC,IAAI,CAAC,gBAAgB;KAC1B,CAAC;IACJ,CAAC;IAED,IAAY,gBAAgB;QAC1B,OAAO,IAAI,CAAA;;eAEA,IAAI,CAAC,YAAY;qBACX,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,eAAe;;wBAEjC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;gBACxD,IAAI,CAAC,sBAAsB;YACjC,CAAC,CAAC,OAAO,CAAC,8DAA8D;YACxE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;iBACvB,IAAI,CAAC,iBAAiB;uBAChB,IAAI,CAAC,qBAAqB;;;UAGvC,IAAI,CAAC,IAAI;;;UAGT,GAAG,CAAC,0CAA0C,CAAC;;KAEpD,CAAC;IACJ,CAAC;IAED,IAAY,YAAY;QACtB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI;YAC3D,OAAO,OAAO,CAAC;QAEjB,8CAA8C;QAC9C,oEAAoE;QACpE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,GAAG,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACvD,CAAC;QAED,OAAO,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAC1C,IAAI,CAAC,KAAK,CAAC,UAAU,EACrB,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,YAAY,CACtC,CAAC;IACJ,CAAC;IAED,IAAY,mBAAmB;QAC7B,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM;YAAE,OAAO,OAAO,CAAC;QAE1E,OAAO,IAAI,CAAA;;;;kBAIG,IAAI,CAAC,gBAAgB;qBAClB,IAAI,CAAC,KAAK,EAAE,OAAO;oBACpB,IAAI,CAAC,iBAAiB;;;KAGrC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,IAAY,sBAAsB;QAChC,OAAO,CACL,IAAI,CAAC,eAAe;YACpB,CAAC,CAAC,IAAI,CAAC,eAAe;YACtB,gBAAc,CAAC,wBAAwB,CAAC,IAAI,CAAC,eAAe,CAAC;YAC7D,IAAI,CAAC,KAAK,EAAE,SAAS,KAAK,QAAQ,IAAI,yCAAyC;YAC/E,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,8CAA8C;SACzE,CAAC;IACJ,CAAC;IAED,IAAY,cAAc;QACxB,OAAO,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACH,IAAY,oBAAoB;QAC9B,OAAO,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,sBAAsB,CAAC;IAC7D,CAAC;IAED,kBAAkB;IAClB,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,kBAAkB;IAClB,iBAAiB;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,CAAC,KAA0B;QACrC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC;QAC5C,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;IAChD,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAClD,CAAC;IAEO,qBAAqB,CAAC,QAAwC;QACpE,QAAQ,EAAE,cAAc,CAAC;YACvB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,IAAI,CAAC,SAAS;SACvB,CAAC,CAAC;IACL,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAChD,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC;YAC/B,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,IAAI,CAAC,SAAS;SACvB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,KAAqB;QAC3B,IAAI,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAChC,MAAM,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAChC,gBAAgB,CACgB,CAAC;YACnC,IAAI,CAAC,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;YAC7C,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAChC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,iBAAiB,CAAC,CAAQ;QAChC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,IAAI,CAAC,KAAK;gBAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAC1D,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,CAAQ;QACpC,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,KAAK,OAAO,EAAE,CAAC;YACvD,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAEO,qBAAqB,CAC3B,CAAwC;QAExC,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC;YACxC,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,mBAAmB,EAAE,IAAI;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,kEAAkE;IAClE,IAAY,mBAAmB;QAC7B,OAAO,CACL,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;YAC3B,CAAC,IAAI,CAAC,YAAY;YAClB,IAAI,CAAC,eAAe,KAAK,MAAM,CAChC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,IAAY,uBAAuB;QACjC,IAAI,CAAC,IAAI,CAAC,mBAAmB;YAAE,OAAO,OAAO,CAAC;QAE9C,OAAO,IAAI,CAAA;;;sBAGO,IAAI,CAAC,2BAA2B;qBACjC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE;;UAE5C,IAAI,CAAC,WAAW,CAAC,GAAG,CACpB,MAAM,CAAC,EAAE,CAAC,IAAI,CAAA;;;uBAGD,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,EAAE,MAAM,CAAC;;gBAE1D,MAAM,CAAC,KAAK;;WAEjB,CACF;;KAEJ,CAAC;IACJ,CAAC;IAWD,IAAY,IAAI;QACd,MAAM,EACJ,KAAK,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,gBAAgB,GACjB,GAAG,IAAI,CAAC;QAET,IAAI,CAAC,KAAK;YAAE,OAAO,OAAO,CAAC;QAE3B,QAAQ,IAAI,CAAC,eAAe,EAAE,CAAC;YAC7B,KAAK,MAAM;gBACT,QAAQ,KAAK,CAAC,SAAS,EAAE,CAAC;oBACxB,KAAK,YAAY;wBACf,OAAO,IAAI,CAAA;uBACA,KAAK;oCACQ,kBAAkB;8BACxB,IAAI,CAAC,YAAY;8BACjB,YAAY;+BACX,aAAa;+BACb,aAAa;kCACV,IAAI,CAAC,gBAAgB;8BACzB,IAAI,CAAC,YAAY;4BACnB,IAAI,CAAC,UAAU;gCACX,IAAI,CAAC,oBAAoB;mCACtB,IAAI,CAAC,qBAAqB;;+BAE9B,CAAC;oBACtB,KAAK,SAAS;wBACZ,OAAO,IAAI,CAAA;uBACA,KAAK;oCACQ,kBAAkB;8BACxB,IAAI,CAAC,YAAY;8BACjB,YAAY;+BACX,aAAa;+BACb,aAAa;kCACV,IAAI,CAAC,gBAAgB;8BACzB,IAAI,CAAC,YAAY;gCACf,IAAI,CAAC,oBAAoB;mCACtB,IAAI,CAAC,qBAAqB;;4BAEjC,CAAC;oBACnB,KAAK,QAAQ;wBACX,OAAO,IAAI,CAAA;uBACA,KAAK;oCACQ,kBAAkB;8BACxB,IAAI,CAAC,YAAY;8BACjB,YAAY;+BACX,aAAa;+BACb,aAAa;kCACV,IAAI,CAAC,gBAAgB;8BACzB,IAAI,CAAC,YAAY;gCACf,KAAK;mCACF,IAAI,CAAC,qBAAqB;;2BAElC,CAAC;oBAClB;wBACE,OAAO,IAAI,CAAA;uBACA,KAAK;oCACQ,kBAAkB;8BACxB,IAAI,CAAC,YAAY;+BAChB,IAAI,CAAC,aAAa;8BACnB,IAAI,CAAC,YAAY;2BACpB,SAAS;kCACF,gBAAgB;+BACnB,aAAa;0BAClB,IAAI,CAAC,QAAQ;kCACL,IAAI,CAAC,gBAAgB;8BACzB,IAAI,CAAC,YAAY;4BACnB,IAAI,CAAC,UAAU;6BACd,IAAI,CAAC,WAAW;gCACb,IAAI,CAAC,oBAAoB;8BAC3B,IAAI,CAAC,YAAY;mCACZ,IAAI,CAAC,qBAAqB;;yBAEpC,CAAC;gBAClB,CAAC;YACH,KAAK,cAAc;gBACjB,OAAO,IAAI,CAAA;mBACA,KAAK;gCACQ,kBAAkB;0BACxB,YAAY;2BACX,aAAa;+BACT,iBAAiB;uBACzB,SAAS;8BACF,gBAAgB;2BACnB,aAAa;8BACV,gBAAgB;0BACpB,IAAI,CAAC,YAAY;sBACrB,IAAI,CAAC,QAAQ;8BACL,IAAI,CAAC,gBAAgB;yBAC1B,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW;0BACxC,IAAI,CAAC,YAAY;;6BAEd,CAAC;YACxB,KAAK,aAAa;gBAChB,OAAO,IAAI,CAAA;mBACA,KAAK;gCACQ,kBAAkB;8BACpB,IAAI,CAAC,gBAAgB;0BACzB,YAAY;2BACX,aAAa;+BACT,iBAAiB;uBACzB,SAAS;8BACF,gBAAgB;2BACnB,aAAa;8BACV,gBAAgB;0BACpB,IAAI,CAAC,YAAY;sBACrB,IAAI,CAAC,QAAQ;8BACL,IAAI,CAAC,gBAAgB;yBAC1B,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW;0BACxC,IAAI,CAAC,YAAY;;qBAEtB,CAAC;YAChB;gBACE,OAAO,OAAO,CAAC;QACnB,CAAC;IACH,CAAC;IAED,MAAM,KAAK,MAAM;QACf,OAAO;YACL,WAAW;YACX,gBAAgB;YAChB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6IF;SACF,CAAC;IACJ,CAAC;;AAnkB2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;uDAAmC;AAEjC;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oDAAsB;AAEtB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;sDAAgD;AAG3E;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;wDACS;AAEP;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;mDAAqB;AAGrB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;kDAAoC;AAGlC;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;uDAAyB;AAEzB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;wDAEzB;AAKM;IADP,KAAK,CAAC,YAAY,CAAC;iDACe;AAG3B;IADP,KAAK,CAAC,iBAAiB,CAAC;iDACS;AAG1B;IADP,KAAK,CAAC,YAAY,CAAC;uDACwB;AAtDjC,cAAc;IAD1B,aAAa,CAAC,iBAAiB,CAAC;GACpB,cAAc,CA4lB1B","sourcesContent":["import { css, html, nothing, PropertyValues } from 'lit';\nimport { customElement, property, query } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { msg } from '@lit/localize';\nimport type {\n SharedResizeObserverInterface,\n SharedResizeObserverResizeHandlerInterface,\n} from '@internetarchive/shared-resize-observer';\nimport type { TileDisplayMode } from '../models';\nimport type { CollectionTitles } from '../data-source/models';\nimport './grid/collection-tile';\nimport './grid/item-tile';\nimport './grid/account-tile';\nimport './grid/search-tile';\nimport './hover/tile-hover-pane';\nimport './list/tile-list';\nimport './list/tile-list-compact';\nimport './list/tile-list-compact-header';\nimport type { TileHoverPane } from './hover/tile-hover-pane';\nimport { BaseTileComponent } from './base-tile-component';\nimport { LayoutType } from './models';\nimport {\n HoverPaneController,\n HoverPaneControllerInterface,\n HoverPaneProperties,\n HoverPaneProviderInterface,\n} from './hover/hover-pane-controller';\nimport { srOnlyStyle } from '../styles/sr-only';\nimport { tileActionStyles } from '../styles/tile-action-styles';\n\n@customElement('tile-dispatcher')\nexport class TileDispatcher\n extends BaseTileComponent\n implements\n SharedResizeObserverResizeHandlerInterface,\n HoverPaneProviderInterface\n{\n /*\n * Reactive properties inherited from BaseTileComponent:\n * - model?: TileModel;\n * - tileActions: TileAction[] = [];\n * - currentWidth?: number;\n * - currentHeight?: number;\n * - baseNavigationUrl?: string;\n * - baseImageUrl?: string;\n * - collectionPagePath?: string;\n * - sortParam: SortParam | null = null;\n * - defaultSortParam: SortParam | null = null;\n * - creatorFilter?: string;\n * - mobileBreakpoint?: number;\n * - loggedIn = false;\n * - suppressTileBlurring = false;\n * - useLocalTime = false;\n */\n\n @property({ type: String }) tileDisplayMode?: TileDisplayMode;\n\n @property({ type: Boolean }) isManageView = false;\n\n @property({ type: Object }) resizeObserver?: SharedResizeObserverInterface;\n\n @property({ type: Object })\n collectionTitles?: CollectionTitles;\n\n @property({ type: Boolean }) showTvClips = false;\n\n /** What type of simplified layout to use in grid mode, if any */\n @property({ type: String }) layoutType: LayoutType = 'default';\n\n /** Whether this tile should include a hover pane at all (for applicable tile modes) */\n @property({ type: Boolean }) enableHoverPane = false;\n\n @property({ type: String }) manageCheckTitle = msg(\n 'Remove this item from the list',\n );\n\n private hoverPaneController?: HoverPaneControllerInterface;\n\n @query('#container')\n private container!: HTMLDivElement;\n\n @query('tile-hover-pane')\n private hoverPane?: TileHoverPane;\n\n @query('.tile-link')\n private tileLinkElement?: HTMLAnchorElement;\n\n acquireFocus(): void {\n this.tileLinkElement?.focus();\n }\n\n releaseFocus(): void {\n this.tileLinkElement?.blur();\n }\n\n /** Maps each display mode to whether hover panes should appear in that mode */\n private static readonly HOVER_PANE_DISPLAY_MODES: Record<\n TileDisplayMode,\n boolean\n > = {\n grid: true,\n 'list-compact': true,\n 'list-detail': false,\n 'list-header': false,\n };\n\n render() {\n const isGridMode = this.tileDisplayMode === 'grid';\n const hasTileActions = isGridMode && this.showGridTileActions;\n const hoverPaneTemplate =\n this.hoverPaneController?.getTemplate() ?? nothing;\n const containerClasses = classMap({\n hoverable: isGridMode,\n 'has-tile-actions': hasTileActions,\n });\n return html`\n <div id=\"container\" class=${containerClasses}>\n ${this.tileDisplayMode === 'list-header'\n ? this.headerTemplate\n : this.tileTemplate}\n ${this.gridTileActionsTemplate} ${this.manageCheckTemplate}\n ${hoverPaneTemplate}\n </div>\n `;\n }\n\n protected firstUpdated(): void {\n if (this.shouldPrepareHoverPane) {\n this.hoverPaneController = new HoverPaneController(this, {\n mobileBreakpoint: this.mobileBreakpoint,\n enableLongPress: false,\n });\n }\n }\n\n private get headerTemplate() {\n const { currentWidth, sortParam, defaultSortParam, mobileBreakpoint } =\n this;\n return html`\n <tile-list-compact-header\n class=\"header\"\n .currentWidth=${currentWidth}\n .sortParam=${sortParam ?? defaultSortParam}\n .mobileBreakpoint=${mobileBreakpoint}\n .tileActions=${this.tileActions}\n >\n </tile-list-compact-header>\n `;\n }\n\n private get tileTemplate() {\n return html`\n ${this.tileDisplayMode === 'list-detail'\n ? this.tile\n : this.linkTileTemplate}\n `;\n }\n\n private get linkTileTemplate() {\n return html`\n <a\n href=${this.linkTileHref}\n aria-label=${this.model?.title ?? 'Untitled item'}\n aria-describedby=\"link-aria-description\"\n aria-haspopup=${this.shouldPrepareHoverPane ? 'dialog' : 'false'}\n title=${this.shouldPrepareHoverPane\n ? nothing // Don't show title tooltips when we have the tile info popups\n : ifDefined(this.model?.title)}\n @click=${this.handleLinkClicked}\n @contextmenu=${this.handleLinkContextMenu}\n class=\"tile-link\"\n >\n ${this.tile}\n </a>\n <div id=\"link-aria-description\" class=\"sr-only\">\n ${msg('Press Down Arrow to preview item details')}\n </div>\n `;\n }\n\n private get linkTileHref(): string | typeof nothing {\n if (!this.model?.identifier || this.baseNavigationUrl == null)\n return nothing;\n\n // Use the server-specified href if available.\n // Otherwise, construct a details page URL from the item identifier.\n if (this.model.href) {\n return `${this.baseNavigationUrl}${this.model.href}`;\n }\n\n return this.displayValueProvider.itemPageUrl(\n this.model.identifier,\n this.model.mediatype === 'collection',\n );\n }\n\n private get manageCheckTemplate() {\n if (!this.isManageView || this.tileDisplayMode !== 'grid') return nothing;\n\n return html`\n <div class=\"manage-check\">\n <input\n type=\"checkbox\"\n title=${this.manageCheckTitle}\n ?checked=${this.model?.checked}\n @change=${this.handleLinkClicked}\n />\n </div>\n `;\n }\n\n /**\n * Whether hover pane behavior should be prepared for this tile\n * (e.g., whether mouse listeners should be attached, etc.)\n */\n private get shouldPrepareHoverPane(): boolean {\n return (\n this.enableHoverPane &&\n !!this.tileDisplayMode &&\n TileDispatcher.HOVER_PANE_DISPLAY_MODES[this.tileDisplayMode] &&\n this.model?.mediatype !== 'search' && // don't show hover panes on search tiles\n !this.model?.captureDates // don't show hover panes on web archive tiles\n );\n }\n\n private get isHoverEnabled(): boolean {\n return window.matchMedia('(hover: hover)').matches;\n }\n\n /**\n * Whether the info button should be shown on this tile.\n * Only shown on touch/non-hover devices where a hover pane is available,\n * so the button always has something to toggle.\n */\n private get shouldShowInfoButton(): boolean {\n return !this.isHoverEnabled && this.shouldPrepareHoverPane;\n }\n\n /** @inheritdoc */\n getHoverPane(): TileHoverPane | undefined {\n return this.hoverPane;\n }\n\n /** @inheritdoc */\n getHoverPaneProps(): HoverPaneProperties {\n return this;\n }\n\n handleResize(entry: ResizeObserverEntry): void {\n this.currentWidth = entry.contentRect.width;\n this.currentHeight = entry.contentRect.height;\n }\n\n disconnectedCallback(): void {\n this.stopResizeObservation(this.resizeObserver);\n }\n\n private stopResizeObservation(observer?: SharedResizeObserverInterface) {\n observer?.removeObserver({\n handler: this,\n target: this.container,\n });\n }\n\n private startResizeObservation() {\n this.stopResizeObservation(this.resizeObserver);\n this.resizeObserver?.addObserver({\n handler: this,\n target: this.container,\n });\n }\n\n updated(props: PropertyValues) {\n if (props.has('resizeObserver')) {\n const previousObserver = props.get(\n 'resizeObserver',\n ) as SharedResizeObserverInterface;\n this.stopResizeObservation(previousObserver);\n this.startResizeObservation();\n }\n }\n\n /**\n * Handler for when the tile link is left-clicked. Emits the `resultSelected` event.\n * In manage view, it also checks/unchecks the tile.\n */\n private handleLinkClicked(e: Event): void {\n if (this.isManageView) {\n e.preventDefault();\n if (this.model) this.model.checked = !this.model.checked;\n }\n\n this.dispatchEvent(\n new CustomEvent('resultSelected', { detail: this.model }),\n );\n }\n\n /**\n * Handler for when the tile link is right-clicked.\n * In manage view, it opens the item in a new tab. Otherwise, does nothing.\n */\n private handleLinkContextMenu(e: Event): void {\n if (this.isManageView && this.linkTileHref !== nothing) {\n e.preventDefault();\n window.open(this.linkTileHref, '_blank');\n }\n }\n\n private tileInfoButtonPressed(\n e: CustomEvent<{ x: number; y: number }>,\n ): void {\n this.hoverPaneController?.toggleHoverPane({\n coords: e.detail,\n enableTouchBackdrop: true,\n });\n }\n\n /** Whether tile action buttons should be rendered in grid mode */\n private get showGridTileActions(): boolean {\n return (\n this.tileActions.length > 0 &&\n !this.isManageView &&\n this.tileDisplayMode === 'grid'\n );\n }\n\n /**\n * Template for the grid-mode action buttons. Rendered alongside the inner\n * tile link inside the dispatcher's shadow root so the action buttons can\n * suppress the hover pane on hover.\n */\n private get gridTileActionsTemplate() {\n if (!this.showGridTileActions) return nothing;\n\n return html`\n <div\n class=\"tile-actions grid-tile-actions\"\n @mouseenter=${this.handleGridActionsMouseEnter}\n @mousemove=${(e: Event) => e.stopPropagation()}\n >\n ${this.tileActions.map(\n action => html`\n <button\n class=\"tile-action-btn\"\n @click=${(e: Event) => this.handleTileActionClick(e, action)}\n >\n ${action.label}\n </button>\n `,\n )}\n </div>\n `;\n }\n\n /**\n * When the mouse enters the grid-mode tile actions area, dispatch a\n * synthetic mouseleave on the host to cancel the hover pane's show timer\n * and hide any visible pane.\n */\n private handleGridActionsMouseEnter = (): void => {\n this.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }));\n };\n\n private get tile() {\n const {\n model,\n collectionPagePath,\n baseNavigationUrl,\n currentWidth,\n currentHeight,\n sortParam,\n creatorFilter,\n mobileBreakpoint,\n defaultSortParam,\n } = this;\n\n if (!model) return nothing;\n\n switch (this.tileDisplayMode) {\n case 'grid':\n switch (model.mediatype) {\n case 'collection':\n return html`<collection-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .baseImageUrl=${this.baseImageUrl}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .creatorFilter=${creatorFilter}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n .layoutType=${this.layoutType}\n ?showInfoButton=${this.shouldShowInfoButton}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </collection-tile>`;\n case 'account':\n return html`<account-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .baseImageUrl=${this.baseImageUrl}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .creatorFilter=${creatorFilter}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n ?showInfoButton=${this.shouldShowInfoButton}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </account-tile>`;\n case 'search':\n return html`<search-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .baseImageUrl=${this.baseImageUrl}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .creatorFilter=${creatorFilter}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n ?showInfoButton=${false}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </search-tile>`;\n default:\n return html`<item-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .currentWidth=${this.currentWidth}\n .currentHeight=${this.currentHeight}\n .baseImageUrl=${this.baseImageUrl}\n .sortParam=${sortParam}\n .defaultSortParam=${defaultSortParam}\n .creatorFilter=${creatorFilter}\n .loggedIn=${this.loggedIn}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n .layoutType=${this.layoutType}\n ?showTvClips=${this.showTvClips}\n ?showInfoButton=${this.shouldShowInfoButton}\n ?useLocalTime=${this.useLocalTime}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </item-tile>`;\n }\n case 'list-compact':\n return html`<tile-list-compact\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .baseNavigationUrl=${baseNavigationUrl}\n .sortParam=${sortParam}\n .defaultSortParam=${defaultSortParam}\n .creatorFilter=${creatorFilter}\n .mobileBreakpoint=${mobileBreakpoint}\n .baseImageUrl=${this.baseImageUrl}\n .loggedIn=${this.loggedIn}\n .suppressBlurring=${this.suppressBlurring}\n .tileActions=${this.isManageView ? [] : this.tileActions}\n ?useLocalTime=${this.useLocalTime}\n >\n </tile-list-compact>`;\n case 'list-detail':\n return html`<tile-list\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .collectionTitles=${this.collectionTitles}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .baseNavigationUrl=${baseNavigationUrl}\n .sortParam=${sortParam}\n .defaultSortParam=${defaultSortParam}\n .creatorFilter=${creatorFilter}\n .mobileBreakpoint=${mobileBreakpoint}\n .baseImageUrl=${this.baseImageUrl}\n .loggedIn=${this.loggedIn}\n .suppressBlurring=${this.suppressBlurring}\n .tileActions=${this.isManageView ? [] : this.tileActions}\n ?useLocalTime=${this.useLocalTime}\n >\n </tile-list>`;\n default:\n return nothing;\n }\n }\n\n static get styles() {\n return [\n srOnlyStyle,\n tileActionStyles,\n css`\n :host {\n display: block;\n height: 100%;\n }\n\n collection-tile {\n --tileBorderColor: #555555;\n --tileBackgroundColor: #666666;\n --imageBlockBackgroundColor: #666666;\n }\n\n account-tile {\n --tileBorderColor: #dddddd;\n --imageBlockBackgroundColor: #fcf5e6;\n }\n\n item-tile {\n --tileBorderColor: #dddddd;\n --imageBlockBackgroundColor: #f1f1f4;\n }\n\n search-tile {\n --tileBorderColor: #555555;\n --tileBackgroundColor: #666666;\n --imageBlockBackgroundColor: #666666;\n --iconFillColor: #2c2c2c;\n }\n\n #container {\n position: relative;\n height: 100%;\n border-radius: 4px;\n }\n\n /*\n * When tile actions are present, the container takes on the role of\n * the tile's visual card so the tile content and action row appear\n * as a single unified element. The inner tile's own shadow/radius\n * are disabled via CSS variable overrides to avoid visual\n * duplication, and the action row sits as a footer inside the same\n * card.\n */\n #container.has-tile-actions {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-shadow: var(--tileShadow, 1px 1px 2px 0);\n --tileBoxShadow: none;\n --tileCornerRadius: 0;\n }\n\n #container.has-tile-actions .tile-link {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n border-radius: 0;\n }\n\n /* Normal hover shadow lives on the inner anchor for plain tiles */\n #container.hoverable:not(.has-tile-actions) a:focus,\n #container.hoverable:not(.has-tile-actions) a:hover {\n box-shadow: var(\n --tileHoverBoxShadow,\n 0 0 6px 2px rgba(8, 8, 32, 0.8)\n );\n transition: box-shadow 0.1s ease;\n }\n\n /*\n * When the container owns the card visuals, the hover shadow needs\n * to move up to the container so it wraps the action row too.\n */\n #container.hoverable.has-tile-actions:hover {\n box-shadow: var(\n --tileHoverBoxShadow,\n 0 0 6px 2px rgba(8, 8, 32, 0.8)\n );\n transition: box-shadow 0.1s ease;\n }\n\n a {\n display: block;\n height: 100%;\n color: unset;\n text-decoration: none;\n transition: transform 0.05s ease;\n border-radius: 4px;\n outline: none;\n }\n\n a :first-child {\n display: block;\n height: 100%;\n }\n\n .manage-check {\n position: absolute;\n right: 0;\n top: 0;\n border: 5px solid #2c2c2c;\n border-radius: 3px;\n background-color: #2c2c2c;\n z-index: 1;\n }\n\n .manage-check > input[type='checkbox'] {\n display: block;\n margin: 0;\n }\n\n #touch-backdrop {\n position: fixed;\n width: 100vw;\n height: 100vh;\n top: 0;\n left: 0;\n z-index: 2;\n background: transparent;\n }\n\n tile-hover-pane {\n position: absolute;\n top: 0;\n left: -9999px;\n z-index: 2;\n }\n\n /*\n * Grid-mode action row sits flush against the bottom of the card —\n * the buttons' own borders form the visible bottom edge. The outer\n * buttons get rounded bottom corners to match the container so the\n * red border traces cleanly around the card's bottom corners.\n */\n .grid-tile-actions .tile-action-btn:first-child {\n border-bottom-left-radius: 4px;\n }\n\n .grid-tile-actions .tile-action-btn:last-child {\n border-bottom-right-radius: 4px;\n }\n `,\n ];\n }\n}\n"]}
1
+ {"version":3,"file":"tile-dispatcher.js","sourceRoot":"","sources":["../../../src/tiles/tile-dispatcher.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, nothing, PropertyValues } from 'lit';\nimport { customElement, property, query } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { msg } from '@lit/localize';\nimport type {\n SharedResizeObserverInterface,\n SharedResizeObserverResizeHandlerInterface,\n} from '@internetarchive/shared-resize-observer';\nimport type { TileDisplayMode } from '../models';\nimport type { CollectionTitles } from '../data-source/models';\nimport './grid/collection-tile';\nimport './grid/item-tile';\nimport './grid/account-tile';\nimport './grid/search-tile';\nimport './hover/tile-hover-pane';\nimport './list/tile-list';\nimport './list/tile-list-compact';\nimport './list/tile-list-compact-header';\nimport type { TileHoverPane } from './hover/tile-hover-pane';\nimport { BaseTileComponent } from './base-tile-component';\nimport { LayoutType } from './models';\nimport {\n HoverPaneController,\n HoverPaneControllerInterface,\n HoverPaneProperties,\n HoverPaneProviderInterface,\n} from './hover/hover-pane-controller';\nimport { srOnlyStyle } from '../styles/sr-only';\nimport { tileActionStyles } from '../styles/tile-action-styles';\nimport {\n PREVIEW_PANE_DELAY_MS,\n TilePreviewController,\n} from './preview/tile-preview-controller';\nimport type { TilePreviewOptions } from './preview/tile-preview-media';\n\n@customElement('tile-dispatcher')\nexport class TileDispatcher\n extends BaseTileComponent\n implements\n SharedResizeObserverResizeHandlerInterface,\n HoverPaneProviderInterface\n{\n /*\n * Reactive properties inherited from BaseTileComponent:\n * - model?: TileModel;\n * - tileActions: TileAction[] = [];\n * - currentWidth?: number;\n * - currentHeight?: number;\n * - baseNavigationUrl?: string;\n * - baseImageUrl?: string;\n * - collectionPagePath?: string;\n * - sortParam: SortParam | null = null;\n * - defaultSortParam: SortParam | null = null;\n * - creatorFilter?: string;\n * - mobileBreakpoint?: number;\n * - loggedIn = false;\n * - suppressTileBlurring = false;\n * - useLocalTime = false;\n */\n\n @property({ type: String }) tileDisplayMode?: TileDisplayMode;\n\n @property({ type: Boolean }) isManageView = false;\n\n @property({ type: Object }) resizeObserver?: SharedResizeObserverInterface;\n\n @property({ type: Object })\n collectionTitles?: CollectionTitles;\n\n @property({ type: Boolean }) showTvClips = false;\n\n /** What type of simplified layout to use in grid mode, if any */\n @property({ type: String }) layoutType: LayoutType = 'default';\n\n /** Whether this tile should include a hover pane at all (for applicable tile modes) */\n @property({ type: Boolean }) enableHoverPane = false;\n\n /** Whether hovering, focusing or long-pressing this tile starts a live preview */\n @property({ type: Boolean }) enableTilePreview = false;\n\n @property({ type: Object }) tilePreviewOptions: TilePreviewOptions = {};\n\n private previewController = new TilePreviewController(this, {\n enabled: () => this.shouldPreview,\n });\n\n @property({ type: String }) manageCheckTitle = msg(\n 'Remove this item from the list',\n );\n\n private hoverPaneController?: HoverPaneControllerInterface;\n\n @query('#container')\n private container!: HTMLDivElement;\n\n @query('tile-hover-pane')\n private hoverPane?: TileHoverPane;\n\n @query('.tile-link')\n private tileLinkElement?: HTMLAnchorElement;\n\n acquireFocus(): void {\n this.tileLinkElement?.focus();\n }\n\n releaseFocus(): void {\n this.tileLinkElement?.blur();\n }\n\n /** Maps each display mode to whether hover panes should appear in that mode */\n private static readonly HOVER_PANE_DISPLAY_MODES: Record<\n TileDisplayMode,\n boolean\n > = {\n grid: true,\n 'list-compact': true,\n 'list-detail': false,\n 'list-header': false,\n };\n\n render() {\n const isGridMode = this.tileDisplayMode === 'grid';\n const hasTileActions = isGridMode && this.showGridTileActions;\n const hoverPaneTemplate =\n this.hoverPaneController?.getTemplate() ?? nothing;\n const containerClasses = classMap({\n hoverable: isGridMode,\n 'has-tile-actions': hasTileActions,\n });\n return html`\n <div id=\"container\" class=${containerClasses}>\n ${this.tileDisplayMode === 'list-header'\n ? this.headerTemplate\n : this.tileTemplate}\n ${this.gridTileActionsTemplate} ${this.manageCheckTemplate}\n ${hoverPaneTemplate}\n </div>\n `;\n }\n\n protected firstUpdated(): void {\n if (this.shouldPrepareHoverPane) {\n this.hoverPaneController = new HoverPaneController(this, {\n mobileBreakpoint: this.mobileBreakpoint,\n enableLongPress: false,\n // A playing preview gets the tile to itself; the info pane follows later.\n showDelay: () => (this.shouldPreview ? PREVIEW_PANE_DELAY_MS : 300),\n });\n }\n }\n\n private get headerTemplate() {\n const { currentWidth, sortParam, defaultSortParam, mobileBreakpoint } =\n this;\n return html`\n <tile-list-compact-header\n class=\"header\"\n .currentWidth=${currentWidth}\n .sortParam=${sortParam ?? defaultSortParam}\n .mobileBreakpoint=${mobileBreakpoint}\n .tileActions=${this.tileActions}\n >\n </tile-list-compact-header>\n `;\n }\n\n private get tileTemplate() {\n return html`\n ${this.tileDisplayMode === 'list-detail'\n ? this.tile\n : this.linkTileTemplate}\n `;\n }\n\n private get linkTileTemplate() {\n return html`\n <a\n href=${this.linkTileHref}\n aria-label=${this.model?.title ?? 'Untitled item'}\n aria-describedby=\"link-aria-description\"\n aria-haspopup=${this.shouldPrepareHoverPane ? 'dialog' : 'false'}\n title=${this.shouldPrepareHoverPane\n ? nothing // Don't show title tooltips when we have the tile info popups\n : ifDefined(this.model?.title)}\n @click=${this.handleLinkClicked}\n @contextmenu=${this.handleLinkContextMenu}\n class=\"tile-link\"\n >\n ${this.tile}\n </a>\n <div id=\"link-aria-description\" class=\"sr-only\">\n ${msg('Press Down Arrow to preview item details')}\n </div>\n `;\n }\n\n private get linkTileHref(): string | typeof nothing {\n if (!this.model?.identifier || this.baseNavigationUrl == null)\n return nothing;\n\n // Use the server-specified href if available.\n // Otherwise, construct a details page URL from the item identifier.\n if (this.model.href) {\n return `${this.baseNavigationUrl}${this.model.href}`;\n }\n\n return this.displayValueProvider.itemPageUrl(\n this.model.identifier,\n this.model.mediatype === 'collection',\n );\n }\n\n private get manageCheckTemplate() {\n if (!this.isManageView || this.tileDisplayMode !== 'grid') return nothing;\n\n return html`\n <div class=\"manage-check\">\n <input\n type=\"checkbox\"\n title=${this.manageCheckTitle}\n ?checked=${this.model?.checked}\n @change=${this.handleLinkClicked}\n />\n </div>\n `;\n }\n\n /**\n * Whether hover pane behavior should be prepared for this tile\n * (e.g., whether mouse listeners should be attached, etc.)\n */\n private get shouldPrepareHoverPane(): boolean {\n return (\n this.enableHoverPane &&\n !!this.tileDisplayMode &&\n TileDispatcher.HOVER_PANE_DISPLAY_MODES[this.tileDisplayMode] &&\n this.model?.mediatype !== 'search' && // don't show hover panes on search tiles\n !this.model?.captureDates // don't show hover panes on web archive tiles\n );\n }\n\n /** Only plain item tiles preview: not collections, accounts, searches or gated items. */\n private get shouldPreview(): boolean {\n const { model } = this;\n return (\n this.enableTilePreview &&\n this.tileDisplayMode === 'grid' &&\n !this.isManageView &&\n !!model?.identifier &&\n !['collection', 'account', 'search'].includes(model.mediatype) &&\n !model.captureDates &&\n (this.suppressBlurring ||\n (!model.contentWarning && !(model.loginRequired && !this.loggedIn)))\n );\n }\n\n private get isHoverEnabled(): boolean {\n return window.matchMedia('(hover: hover)').matches;\n }\n\n /**\n * Whether the info button should be shown on this tile.\n * Only shown on touch/non-hover devices where a hover pane is available,\n * so the button always has something to toggle.\n */\n private get shouldShowInfoButton(): boolean {\n return !this.isHoverEnabled && this.shouldPrepareHoverPane;\n }\n\n /** @inheritdoc */\n getHoverPane(): TileHoverPane | undefined {\n return this.hoverPane;\n }\n\n /** @inheritdoc */\n getHoverPaneProps(): HoverPaneProperties {\n return this;\n }\n\n handleResize(entry: ResizeObserverEntry): void {\n this.currentWidth = entry.contentRect.width;\n this.currentHeight = entry.contentRect.height;\n }\n\n disconnectedCallback(): void {\n this.stopResizeObservation(this.resizeObserver);\n }\n\n private stopResizeObservation(observer?: SharedResizeObserverInterface) {\n observer?.removeObserver({\n handler: this,\n target: this.container,\n });\n }\n\n private startResizeObservation() {\n this.stopResizeObservation(this.resizeObserver);\n this.resizeObserver?.addObserver({\n handler: this,\n target: this.container,\n });\n }\n\n updated(props: PropertyValues) {\n if (!this.shouldPreview) this.previewController.deactivate();\n\n if (props.has('resizeObserver')) {\n const previousObserver = props.get(\n 'resizeObserver',\n ) as SharedResizeObserverInterface;\n this.stopResizeObservation(previousObserver);\n this.startResizeObservation();\n }\n }\n\n /**\n * Handler for when the tile link is left-clicked. Emits the `resultSelected` event.\n * In manage view, it also checks/unchecks the tile.\n */\n private handleLinkClicked(e: Event): void {\n if (this.isManageView) {\n e.preventDefault();\n if (this.model) this.model.checked = !this.model.checked;\n }\n\n this.dispatchEvent(\n new CustomEvent('resultSelected', { detail: this.model }),\n );\n }\n\n /**\n * Handler for when the tile link is right-clicked.\n * In manage view, it opens the item in a new tab. Otherwise, does nothing.\n */\n private handleLinkContextMenu(e: Event): void {\n if (this.isManageView && this.linkTileHref !== nothing) {\n e.preventDefault();\n window.open(this.linkTileHref, '_blank');\n }\n }\n\n private tileInfoButtonPressed(\n e: CustomEvent<{ x: number; y: number }>,\n ): void {\n this.hoverPaneController?.toggleHoverPane({\n coords: e.detail,\n enableTouchBackdrop: true,\n });\n }\n\n /** Whether tile action buttons should be rendered in grid mode */\n private get showGridTileActions(): boolean {\n return (\n this.tileActions.length > 0 &&\n !this.isManageView &&\n this.tileDisplayMode === 'grid'\n );\n }\n\n /**\n * Template for the grid-mode action buttons. Rendered alongside the inner\n * tile link inside the dispatcher's shadow root so the action buttons can\n * suppress the hover pane on hover.\n */\n private get gridTileActionsTemplate() {\n if (!this.showGridTileActions) return nothing;\n\n return html`\n <div\n class=\"tile-actions grid-tile-actions\"\n @mouseenter=${this.handleGridActionsMouseEnter}\n @mousemove=${(e: Event) => e.stopPropagation()}\n >\n ${this.tileActions.map(\n action => html`\n <button\n class=\"tile-action-btn\"\n @click=${(e: Event) => this.handleTileActionClick(e, action)}\n >\n ${action.label}\n </button>\n `,\n )}\n </div>\n `;\n }\n\n /**\n * When the mouse enters the grid-mode tile actions area, dispatch a\n * synthetic mouseleave on the host to cancel the hover pane's show timer\n * and hide any visible pane.\n */\n private handleGridActionsMouseEnter = (): void => {\n this.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }));\n };\n\n private get tile() {\n const {\n model,\n collectionPagePath,\n baseNavigationUrl,\n currentWidth,\n currentHeight,\n sortParam,\n creatorFilter,\n mobileBreakpoint,\n defaultSortParam,\n } = this;\n\n if (!model) return nothing;\n\n switch (this.tileDisplayMode) {\n case 'grid':\n switch (model.mediatype) {\n case 'collection':\n return html`<collection-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .baseImageUrl=${this.baseImageUrl}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .creatorFilter=${creatorFilter}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n .layoutType=${this.layoutType}\n ?showInfoButton=${this.shouldShowInfoButton}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </collection-tile>`;\n case 'account':\n return html`<account-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .baseImageUrl=${this.baseImageUrl}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .creatorFilter=${creatorFilter}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n ?showInfoButton=${this.shouldShowInfoButton}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </account-tile>`;\n case 'search':\n return html`<search-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .baseImageUrl=${this.baseImageUrl}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .creatorFilter=${creatorFilter}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n ?showInfoButton=${false}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </search-tile>`;\n default:\n return html`<item-tile\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .currentWidth=${this.currentWidth}\n .currentHeight=${this.currentHeight}\n .baseImageUrl=${this.baseImageUrl}\n .sortParam=${sortParam}\n .defaultSortParam=${defaultSortParam}\n .creatorFilter=${creatorFilter}\n .loggedIn=${this.loggedIn}\n .suppressBlurring=${this.suppressBlurring}\n .isManageView=${this.isManageView}\n .layoutType=${this.layoutType}\n ?showTvClips=${this.showTvClips}\n ?showInfoButton=${this.shouldShowInfoButton}\n ?useLocalTime=${this.useLocalTime}\n ?previewActive=${this.previewController.active}\n .previewOptions=${this.tilePreviewOptions}\n @infoButtonPressed=${this.tileInfoButtonPressed}\n >\n </item-tile>`;\n }\n case 'list-compact':\n return html`<tile-list-compact\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .baseNavigationUrl=${baseNavigationUrl}\n .sortParam=${sortParam}\n .defaultSortParam=${defaultSortParam}\n .creatorFilter=${creatorFilter}\n .mobileBreakpoint=${mobileBreakpoint}\n .baseImageUrl=${this.baseImageUrl}\n .loggedIn=${this.loggedIn}\n .suppressBlurring=${this.suppressBlurring}\n .tileActions=${this.isManageView ? [] : this.tileActions}\n ?useLocalTime=${this.useLocalTime}\n >\n </tile-list-compact>`;\n case 'list-detail':\n return html`<tile-list\n .model=${model}\n .collectionPagePath=${collectionPagePath}\n .collectionTitles=${this.collectionTitles}\n .currentWidth=${currentWidth}\n .currentHeight=${currentHeight}\n .baseNavigationUrl=${baseNavigationUrl}\n .sortParam=${sortParam}\n .defaultSortParam=${defaultSortParam}\n .creatorFilter=${creatorFilter}\n .mobileBreakpoint=${mobileBreakpoint}\n .baseImageUrl=${this.baseImageUrl}\n .loggedIn=${this.loggedIn}\n .suppressBlurring=${this.suppressBlurring}\n .tileActions=${this.isManageView ? [] : this.tileActions}\n ?useLocalTime=${this.useLocalTime}\n >\n </tile-list>`;\n default:\n return nothing;\n }\n }\n\n static get styles() {\n return [\n srOnlyStyle,\n tileActionStyles,\n css`\n :host {\n display: block;\n height: 100%;\n }\n\n collection-tile {\n --tileBorderColor: #555555;\n --tileBackgroundColor: #666666;\n --imageBlockBackgroundColor: #666666;\n }\n\n account-tile {\n --tileBorderColor: #dddddd;\n --imageBlockBackgroundColor: #fcf5e6;\n }\n\n item-tile {\n --tileBorderColor: #dddddd;\n --imageBlockBackgroundColor: #f1f1f4;\n }\n\n search-tile {\n --tileBorderColor: #555555;\n --tileBackgroundColor: #666666;\n --imageBlockBackgroundColor: #666666;\n --iconFillColor: #2c2c2c;\n }\n\n #container {\n position: relative;\n height: 100%;\n border-radius: 4px;\n }\n\n /*\n * When tile actions are present, the container takes on the role of\n * the tile's visual card so the tile content and action row appear\n * as a single unified element. The inner tile's own shadow/radius\n * are disabled via CSS variable overrides to avoid visual\n * duplication, and the action row sits as a footer inside the same\n * card.\n */\n #container.has-tile-actions {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-shadow: var(--tileShadow, 1px 1px 2px 0);\n --tileBoxShadow: none;\n --tileCornerRadius: 0;\n }\n\n #container.has-tile-actions .tile-link {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n border-radius: 0;\n }\n\n /* Normal hover shadow lives on the inner anchor for plain tiles */\n #container.hoverable:not(.has-tile-actions) a:focus,\n #container.hoverable:not(.has-tile-actions) a:hover {\n box-shadow: var(\n --tileHoverBoxShadow,\n 0 0 6px 2px rgba(8, 8, 32, 0.8)\n );\n transition: box-shadow 0.1s ease;\n }\n\n /*\n * When the container owns the card visuals, the hover shadow needs\n * to move up to the container so it wraps the action row too.\n */\n #container.hoverable.has-tile-actions:hover {\n box-shadow: var(\n --tileHoverBoxShadow,\n 0 0 6px 2px rgba(8, 8, 32, 0.8)\n );\n transition: box-shadow 0.1s ease;\n }\n\n a {\n display: block;\n height: 100%;\n color: unset;\n text-decoration: none;\n transition: transform 0.05s ease;\n border-radius: 4px;\n outline: none;\n }\n\n a :first-child {\n display: block;\n height: 100%;\n }\n\n .manage-check {\n position: absolute;\n right: 0;\n top: 0;\n border: 5px solid #2c2c2c;\n border-radius: 3px;\n background-color: #2c2c2c;\n z-index: 1;\n }\n\n .manage-check > input[type='checkbox'] {\n display: block;\n margin: 0;\n }\n\n #touch-backdrop {\n position: fixed;\n width: 100vw;\n height: 100vh;\n top: 0;\n left: 0;\n z-index: 2;\n background: transparent;\n }\n\n tile-hover-pane {\n position: absolute;\n top: 0;\n left: -9999px;\n z-index: 2;\n }\n\n /*\n * Grid-mode action row sits flush against the bottom of the card —\n * the buttons' own borders form the visible bottom edge. The outer\n * buttons get rounded bottom corners to match the container so the\n * red border traces cleanly around the card's bottom corners.\n */\n .grid-tile-actions .tile-action-btn:first-child {\n border-bottom-left-radius: 4px;\n }\n\n .grid-tile-actions .tile-action-btn:last-child {\n border-bottom-right-radius: 4px;\n }\n `,\n ];\n }\n}\n"]}
@@ -0,0 +1,69 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ /** What the controller needs from the element that owns the grid. */
6
+ export interface GridZoomHost<Anchor = unknown> {
7
+ /** The element the gestures are listened on and the preview transform is applied to. */
8
+ getSurface(): HTMLElement | undefined;
9
+ /** How many columns the grid is showing right now. */
10
+ getColumns(): number;
11
+ /** The zoom level that is currently in effect. */
12
+ getLevel(): number;
13
+ /** Notes what is under `focal` (viewport coordinates) before anything is scaled. */
14
+ captureAnchor(focal: Point): Anchor | undefined;
15
+ /**
16
+ * Switches the layout to `level`, keeping the captured anchor under `focal`.
17
+ * Resolves once the new layout is in place.
18
+ */
19
+ commitLevel(level: number, focal: Point, anchor?: Anchor): Promise<void>;
20
+ }
21
+ /**
22
+ * Turns pinch gestures, ctrl+wheel (trackpad pinch in Chrome/Firefox), Safari
23
+ * gesture events and ctrl/cmd +/- into zoom level changes on the tile grid.
24
+ *
25
+ * While a gesture is in progress the grid is scaled with a CSS transform
26
+ * around the pinch point. When it ends, the transform eases to the size of the
27
+ * level it snapped to, then the real layout is swapped in.
28
+ */
29
+ export declare class GridZoomController<Anchor = unknown> {
30
+ private host;
31
+ private surface?;
32
+ private source?;
33
+ private startColumns;
34
+ private scale;
35
+ private focal;
36
+ private anchor?;
37
+ private settling;
38
+ /** The latest level asked for while a zoom was still settling. */
39
+ private pendingLevel?;
40
+ private pointers;
41
+ private startDistance;
42
+ private wheelTimer;
43
+ private hovering;
44
+ private lastPointer?;
45
+ constructor(host: GridZoomHost<Anchor>);
46
+ /** Starts listening. Safe to call again after the surface element has changed. */
47
+ connect(): void;
48
+ disconnect(): void;
49
+ /** Moves to `level` with the same animated settle a pinch gets. */
50
+ zoomToLevel(level: number, focal?: Point): Promise<void>;
51
+ private begin;
52
+ private update;
53
+ /**
54
+ * Ends the gesture, snapping to the level its scale is closest to. When
55
+ * `forcedRatio` is given (zooming to a specific level), that level is used.
56
+ */
57
+ private finish;
58
+ private clearTransform;
59
+ private viewportCenter;
60
+ private pointerDown;
61
+ private pointerMove;
62
+ private pointerEnd;
63
+ private pointerLeave;
64
+ private wheel;
65
+ private safariStart;
66
+ private safariChange;
67
+ private safariEnd;
68
+ private keyDown;
69
+ }
@@ -0,0 +1,261 @@
1
+ import { clampGestureScale, GRID_ZOOM_COLUMNS, zoomLevelForScale, } from './grid-zoom';
2
+ const SETTLE_MS = 180;
3
+ const WHEEL_IDLE_MS = 160;
4
+ const WHEEL_SENSITIVITY = 0.01;
5
+ const MAX_WHEEL_STEP = 30;
6
+ /**
7
+ * Turns pinch gestures, ctrl+wheel (trackpad pinch in Chrome/Firefox), Safari
8
+ * gesture events and ctrl/cmd +/- into zoom level changes on the tile grid.
9
+ *
10
+ * While a gesture is in progress the grid is scaled with a CSS transform
11
+ * around the pinch point. When it ends, the transform eases to the size of the
12
+ * level it snapped to, then the real layout is swapped in.
13
+ */
14
+ export class GridZoomController {
15
+ constructor(host) {
16
+ this.host = host;
17
+ this.startColumns = 1;
18
+ this.scale = 1;
19
+ this.focal = { x: 0, y: 0 };
20
+ this.settling = false;
21
+ this.pointers = new Map();
22
+ this.startDistance = 0;
23
+ this.wheelTimer = 0;
24
+ this.hovering = false;
25
+ // Touch pinch: two pointers
26
+ this.pointerDown = (e) => {
27
+ if (e.pointerType !== 'touch')
28
+ return;
29
+ this.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
30
+ if (this.pointers.size === 2 && !this.source) {
31
+ const [a, b] = [...this.pointers.values()];
32
+ this.startDistance = Math.hypot(a.x - b.x, a.y - b.y);
33
+ if (this.startDistance > 0) {
34
+ this.begin('pointer', { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });
35
+ }
36
+ }
37
+ };
38
+ this.pointerMove = (e) => {
39
+ this.lastPointer = { x: e.clientX, y: e.clientY };
40
+ this.hovering = true;
41
+ if (!this.pointers.has(e.pointerId))
42
+ return;
43
+ this.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
44
+ if (this.source === 'pointer' && this.pointers.size >= 2) {
45
+ const [a, b] = [...this.pointers.values()];
46
+ this.update(Math.hypot(a.x - b.x, a.y - b.y) / this.startDistance);
47
+ }
48
+ };
49
+ this.pointerEnd = (e) => {
50
+ if (!this.pointers.delete(e.pointerId))
51
+ return;
52
+ if (this.source === 'pointer' && this.pointers.size < 2)
53
+ this.finish();
54
+ };
55
+ this.pointerLeave = () => {
56
+ this.hovering = false;
57
+ };
58
+ // Trackpad pinch in Chrome and Firefox arrives as ctrl+wheel
59
+ this.wheel = (e) => {
60
+ if (!e.ctrlKey)
61
+ return;
62
+ e.preventDefault();
63
+ if (this.settling)
64
+ return;
65
+ if (!this.source)
66
+ this.begin('wheel', { x: e.clientX, y: e.clientY });
67
+ if (this.source !== 'wheel')
68
+ return;
69
+ const step = Math.max(-MAX_WHEEL_STEP, Math.min(MAX_WHEEL_STEP, e.deltaY));
70
+ this.update(this.scale * Math.exp(-step * WHEEL_SENSITIVITY));
71
+ window.clearTimeout(this.wheelTimer);
72
+ this.wheelTimer = window.setTimeout(() => {
73
+ if (this.source === 'wheel')
74
+ this.finish();
75
+ }, WHEEL_IDLE_MS);
76
+ };
77
+ // Safari reports pinches (trackpad and touch) as gesture events
78
+ this.safariStart = (e) => {
79
+ const { clientX, clientY } = e;
80
+ e.preventDefault();
81
+ // Touch pinches also produce pointer events; the gesture events win.
82
+ if (this.source === 'pointer')
83
+ this.source = undefined;
84
+ this.begin('safari', { x: clientX, y: clientY });
85
+ };
86
+ this.safariChange = (e) => {
87
+ e.preventDefault();
88
+ if (this.source === 'safari')
89
+ this.update(e.scale);
90
+ };
91
+ this.safariEnd = (e) => {
92
+ e.preventDefault();
93
+ if (this.source === 'safari')
94
+ this.finish();
95
+ };
96
+ // ctrl/cmd + and - while the pointer or focus is on the grid
97
+ this.keyDown = (e) => {
98
+ if (!(e.ctrlKey || e.metaKey) || e.altKey)
99
+ return;
100
+ const inGrid = this.hovering ||
101
+ (this.surface && e.composedPath().includes(this.surface));
102
+ if (!inGrid)
103
+ return;
104
+ const level = this.host.getLevel();
105
+ let target;
106
+ if (e.key === '=' || e.key === '+')
107
+ target = level - 1;
108
+ else if (e.key === '-' || e.key === '_')
109
+ target = level + 1;
110
+ else
111
+ return;
112
+ if (target < 0 || target >= GRID_ZOOM_COLUMNS.length)
113
+ return;
114
+ e.preventDefault();
115
+ this.zoomToLevel(target, this.hovering ? this.lastPointer : undefined);
116
+ };
117
+ }
118
+ /** Starts listening. Safe to call again after the surface element has changed. */
119
+ connect() {
120
+ const surface = this.host.getSurface();
121
+ if (!surface || surface === this.surface)
122
+ return;
123
+ this.disconnect();
124
+ this.surface = surface;
125
+ surface.style.touchAction = 'pan-x pan-y';
126
+ surface.addEventListener('pointerdown', this.pointerDown);
127
+ surface.addEventListener('pointermove', this.pointerMove);
128
+ surface.addEventListener('pointerup', this.pointerEnd);
129
+ surface.addEventListener('pointercancel', this.pointerEnd);
130
+ surface.addEventListener('pointerleave', this.pointerLeave);
131
+ surface.addEventListener('wheel', this.wheel, { passive: false });
132
+ surface.addEventListener('gesturestart', this.safariStart);
133
+ surface.addEventListener('gesturechange', this.safariChange);
134
+ surface.addEventListener('gestureend', this.safariEnd);
135
+ window.addEventListener('keydown', this.keyDown);
136
+ }
137
+ disconnect() {
138
+ const { surface } = this;
139
+ if (surface) {
140
+ surface.style.touchAction = '';
141
+ surface.removeEventListener('pointerdown', this.pointerDown);
142
+ surface.removeEventListener('pointermove', this.pointerMove);
143
+ surface.removeEventListener('pointerup', this.pointerEnd);
144
+ surface.removeEventListener('pointercancel', this.pointerEnd);
145
+ surface.removeEventListener('pointerleave', this.pointerLeave);
146
+ surface.removeEventListener('wheel', this.wheel);
147
+ surface.removeEventListener('gesturestart', this.safariStart);
148
+ surface.removeEventListener('gesturechange', this.safariChange);
149
+ surface.removeEventListener('gestureend', this.safariEnd);
150
+ this.clearTransform();
151
+ surface.style.transition = '';
152
+ surface.style.opacity = '';
153
+ }
154
+ window.removeEventListener('keydown', this.keyDown);
155
+ window.clearTimeout(this.wheelTimer);
156
+ this.pointers.clear();
157
+ this.source = undefined;
158
+ this.settling = false;
159
+ this.pendingLevel = undefined;
160
+ this.hovering = false;
161
+ this.lastPointer = undefined;
162
+ this.surface = undefined;
163
+ }
164
+ /** Moves to `level` with the same animated settle a pinch gets. */
165
+ async zoomToLevel(level, focal) {
166
+ if (this.source || this.settling) {
167
+ this.pendingLevel = level;
168
+ return;
169
+ }
170
+ const columns = this.host.getColumns();
171
+ const targetColumns = GRID_ZOOM_COLUMNS[level];
172
+ if (targetColumns === undefined || level === this.host.getLevel())
173
+ return;
174
+ this.begin('programmatic', focal ?? this.viewportCenter());
175
+ await this.finish(targetColumns / columns);
176
+ }
177
+ begin(source, focal) {
178
+ const { surface } = this;
179
+ if (!surface || this.source || this.settling)
180
+ return;
181
+ this.source = source;
182
+ this.focal = focal;
183
+ this.scale = 1;
184
+ this.startColumns = this.host.getColumns();
185
+ this.anchor = this.host.captureAnchor(focal);
186
+ const rect = surface.getBoundingClientRect();
187
+ surface.style.transformOrigin = `${focal.x - rect.left}px ${focal.y - rect.top}px`;
188
+ surface.style.willChange = 'transform';
189
+ surface.style.transition = 'none';
190
+ }
191
+ update(scale) {
192
+ if (!this.source || !this.surface)
193
+ return;
194
+ this.scale = clampGestureScale(this.startColumns, scale);
195
+ this.surface.style.transform = `scale(${this.scale})`;
196
+ }
197
+ /**
198
+ * Ends the gesture, snapping to the level its scale is closest to. When
199
+ * `forcedRatio` is given (zooming to a specific level), that level is used.
200
+ */
201
+ async finish(forcedRatio) {
202
+ const { surface } = this;
203
+ if (!surface || !this.source)
204
+ return;
205
+ const { focal, anchor } = this;
206
+ const targetLevel = forcedRatio !== undefined
207
+ ? zoomLevelForScale(this.startColumns, 1 / forcedRatio)
208
+ : zoomLevelForScale(this.startColumns, this.scale);
209
+ const targetColumns = GRID_ZOOM_COLUMNS[targetLevel];
210
+ this.source = undefined;
211
+ this.settling = true;
212
+ // The scale at which the current tiles are the size the target level's tiles will be.
213
+ const settledScale = this.startColumns / targetColumns;
214
+ surface.style.transition = `transform ${SETTLE_MS}ms ease-out`;
215
+ surface.style.transform = `scale(${settledScale})`;
216
+ await new Promise(resolve => {
217
+ window.setTimeout(resolve, SETTLE_MS + 20);
218
+ });
219
+ if (!this.surface)
220
+ return;
221
+ if (targetLevel !== this.host.getLevel()) {
222
+ // Swap layouts behind a brief fade so the reflow isn't seen mid-way.
223
+ surface.style.transition = 'none';
224
+ surface.style.opacity = '0';
225
+ this.clearTransform();
226
+ try {
227
+ await this.host.commitLevel(targetLevel, focal, anchor);
228
+ }
229
+ finally {
230
+ surface.style.transition = 'opacity 120ms ease-in';
231
+ surface.style.opacity = '';
232
+ }
233
+ }
234
+ else {
235
+ this.clearTransform();
236
+ }
237
+ this.settling = false;
238
+ const { pendingLevel } = this;
239
+ this.pendingLevel = undefined;
240
+ if (pendingLevel !== undefined)
241
+ this.zoomToLevel(pendingLevel, focal);
242
+ }
243
+ clearTransform() {
244
+ const { surface } = this;
245
+ if (!surface)
246
+ return;
247
+ surface.style.transform = '';
248
+ surface.style.transformOrigin = '';
249
+ surface.style.willChange = '';
250
+ }
251
+ viewportCenter() {
252
+ const rect = this.surface?.getBoundingClientRect();
253
+ const top = Math.max(0, rect?.top ?? 0);
254
+ const bottom = Math.min(window.innerHeight, rect?.bottom ?? 0);
255
+ return {
256
+ x: (rect?.left ?? 0) + (rect?.width ?? window.innerWidth) / 2,
257
+ y: top + Math.max(0, bottom - top) / 2,
258
+ };
259
+ }
260
+ }
261
+ //# sourceMappingURL=grid-zoom-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid-zoom-controller.js","sourceRoot":"","sources":["../../../src/utils/grid-zoom-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n clampGestureScale,\n GRID_ZOOM_COLUMNS,\n zoomLevelForScale,\n} from './grid-zoom';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\n/** What the controller needs from the element that owns the grid. */\nexport interface GridZoomHost<Anchor = unknown> {\n /** The element the gestures are listened on and the preview transform is applied to. */\n getSurface(): HTMLElement | undefined;\n /** How many columns the grid is showing right now. */\n getColumns(): number;\n /** The zoom level that is currently in effect. */\n getLevel(): number;\n /** Notes what is under `focal` (viewport coordinates) before anything is scaled. */\n captureAnchor(focal: Point): Anchor | undefined;\n /**\n * Switches the layout to `level`, keeping the captured anchor under `focal`.\n * Resolves once the new layout is in place.\n */\n commitLevel(level: number, focal: Point, anchor?: Anchor): Promise<void>;\n}\n\nconst SETTLE_MS = 180;\nconst WHEEL_IDLE_MS = 160;\nconst WHEEL_SENSITIVITY = 0.01;\nconst MAX_WHEEL_STEP = 30;\n\ntype GestureSource = 'pointer' | 'wheel' | 'safari' | 'programmatic';\n\ninterface SafariGestureEvent extends UIEvent {\n scale: number;\n clientX: number;\n clientY: number;\n}\n\n/**\n * Turns pinch gestures, ctrl+wheel (trackpad pinch in Chrome/Firefox), Safari\n * gesture events and ctrl/cmd +/- into zoom level changes on the tile grid.\n *\n * While a gesture is in progress the grid is scaled with a CSS transform\n * around the pinch point. When it ends, the transform eases to the size of the\n * level it snapped to, then the real layout is swapped in.\n */\nexport class GridZoomController<Anchor = unknown> {\n private surface?: HTMLElement;\n\n private source?: GestureSource;\n\n private startColumns = 1;\n\n private scale = 1;\n\n private focal: Point = { x: 0, y: 0 };\n\n private anchor?: Anchor;\n\n private settling = false;\n\n /** The latest level asked for while a zoom was still settling. */\n private pendingLevel?: number;\n\n private pointers = new Map<number, Point>();\n\n private startDistance = 0;\n\n private wheelTimer = 0;\n\n private hovering = false;\n\n private lastPointer?: Point;\n\n constructor(private host: GridZoomHost<Anchor>) {}\n\n /** Starts listening. Safe to call again after the surface element has changed. */\n connect(): void {\n const surface = this.host.getSurface();\n if (!surface || surface === this.surface) return;\n this.disconnect();\n this.surface = surface;\n\n surface.style.touchAction = 'pan-x pan-y';\n surface.addEventListener('pointerdown', this.pointerDown);\n surface.addEventListener('pointermove', this.pointerMove);\n surface.addEventListener('pointerup', this.pointerEnd);\n surface.addEventListener('pointercancel', this.pointerEnd);\n surface.addEventListener('pointerleave', this.pointerLeave);\n surface.addEventListener('wheel', this.wheel, { passive: false });\n surface.addEventListener('gesturestart', this.safariStart);\n surface.addEventListener('gesturechange', this.safariChange);\n surface.addEventListener('gestureend', this.safariEnd);\n window.addEventListener('keydown', this.keyDown);\n }\n\n disconnect(): void {\n const { surface } = this;\n if (surface) {\n surface.style.touchAction = '';\n surface.removeEventListener('pointerdown', this.pointerDown);\n surface.removeEventListener('pointermove', this.pointerMove);\n surface.removeEventListener('pointerup', this.pointerEnd);\n surface.removeEventListener('pointercancel', this.pointerEnd);\n surface.removeEventListener('pointerleave', this.pointerLeave);\n surface.removeEventListener('wheel', this.wheel);\n surface.removeEventListener('gesturestart', this.safariStart);\n surface.removeEventListener('gesturechange', this.safariChange);\n surface.removeEventListener('gestureend', this.safariEnd);\n this.clearTransform();\n surface.style.transition = '';\n surface.style.opacity = '';\n }\n window.removeEventListener('keydown', this.keyDown);\n window.clearTimeout(this.wheelTimer);\n this.pointers.clear();\n this.source = undefined;\n this.settling = false;\n this.pendingLevel = undefined;\n this.hovering = false;\n this.lastPointer = undefined;\n this.surface = undefined;\n }\n\n /** Moves to `level` with the same animated settle a pinch gets. */\n async zoomToLevel(level: number, focal?: Point): Promise<void> {\n if (this.source || this.settling) {\n this.pendingLevel = level;\n return;\n }\n const columns = this.host.getColumns();\n const targetColumns = GRID_ZOOM_COLUMNS[level];\n if (targetColumns === undefined || level === this.host.getLevel()) return;\n this.begin('programmatic', focal ?? this.viewportCenter());\n await this.finish(targetColumns / columns);\n }\n\n private begin(source: GestureSource, focal: Point): void {\n const { surface } = this;\n if (!surface || this.source || this.settling) return;\n this.source = source;\n this.focal = focal;\n this.scale = 1;\n this.startColumns = this.host.getColumns();\n this.anchor = this.host.captureAnchor(focal);\n\n const rect = surface.getBoundingClientRect();\n surface.style.transformOrigin = `${focal.x - rect.left}px ${focal.y - rect.top}px`;\n surface.style.willChange = 'transform';\n surface.style.transition = 'none';\n }\n\n private update(scale: number): void {\n if (!this.source || !this.surface) return;\n this.scale = clampGestureScale(this.startColumns, scale);\n this.surface.style.transform = `scale(${this.scale})`;\n }\n\n /**\n * Ends the gesture, snapping to the level its scale is closest to. When\n * `forcedRatio` is given (zooming to a specific level), that level is used.\n */\n private async finish(forcedRatio?: number): Promise<void> {\n const { surface } = this;\n if (!surface || !this.source) return;\n const { focal, anchor } = this;\n const targetLevel =\n forcedRatio !== undefined\n ? zoomLevelForScale(this.startColumns, 1 / forcedRatio)\n : zoomLevelForScale(this.startColumns, this.scale);\n const targetColumns = GRID_ZOOM_COLUMNS[targetLevel];\n this.source = undefined;\n this.settling = true;\n\n // The scale at which the current tiles are the size the target level's tiles will be.\n const settledScale = this.startColumns / targetColumns;\n surface.style.transition = `transform ${SETTLE_MS}ms ease-out`;\n surface.style.transform = `scale(${settledScale})`;\n await new Promise<void>(resolve => {\n window.setTimeout(resolve, SETTLE_MS + 20);\n });\n\n if (!this.surface) return;\n if (targetLevel !== this.host.getLevel()) {\n // Swap layouts behind a brief fade so the reflow isn't seen mid-way.\n surface.style.transition = 'none';\n surface.style.opacity = '0';\n this.clearTransform();\n try {\n await this.host.commitLevel(targetLevel, focal, anchor);\n } finally {\n surface.style.transition = 'opacity 120ms ease-in';\n surface.style.opacity = '';\n }\n } else {\n this.clearTransform();\n }\n this.settling = false;\n\n const { pendingLevel } = this;\n this.pendingLevel = undefined;\n if (pendingLevel !== undefined) this.zoomToLevel(pendingLevel, focal);\n }\n\n private clearTransform(): void {\n const { surface } = this;\n if (!surface) return;\n surface.style.transform = '';\n surface.style.transformOrigin = '';\n surface.style.willChange = '';\n }\n\n private viewportCenter(): Point {\n const rect = this.surface?.getBoundingClientRect();\n const top = Math.max(0, rect?.top ?? 0);\n const bottom = Math.min(window.innerHeight, rect?.bottom ?? 0);\n return {\n x: (rect?.left ?? 0) + (rect?.width ?? window.innerWidth) / 2,\n y: top + Math.max(0, bottom - top) / 2,\n };\n }\n\n // Touch pinch: two pointers\n\n private pointerDown = (e: PointerEvent): void => {\n if (e.pointerType !== 'touch') return;\n this.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (this.pointers.size === 2 && !this.source) {\n const [a, b] = [...this.pointers.values()];\n this.startDistance = Math.hypot(a.x - b.x, a.y - b.y);\n if (this.startDistance > 0) {\n this.begin('pointer', { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });\n }\n }\n };\n\n private pointerMove = (e: PointerEvent): void => {\n this.lastPointer = { x: e.clientX, y: e.clientY };\n this.hovering = true;\n if (!this.pointers.has(e.pointerId)) return;\n this.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (this.source === 'pointer' && this.pointers.size >= 2) {\n const [a, b] = [...this.pointers.values()];\n this.update(Math.hypot(a.x - b.x, a.y - b.y) / this.startDistance);\n }\n };\n\n private pointerEnd = (e: PointerEvent): void => {\n if (!this.pointers.delete(e.pointerId)) return;\n if (this.source === 'pointer' && this.pointers.size < 2) this.finish();\n };\n\n private pointerLeave = (): void => {\n this.hovering = false;\n };\n\n // Trackpad pinch in Chrome and Firefox arrives as ctrl+wheel\n\n private wheel = (e: WheelEvent): void => {\n if (!e.ctrlKey) return;\n e.preventDefault();\n if (this.settling) return;\n if (!this.source) this.begin('wheel', { x: e.clientX, y: e.clientY });\n if (this.source !== 'wheel') return;\n\n const step = Math.max(-MAX_WHEEL_STEP, Math.min(MAX_WHEEL_STEP, e.deltaY));\n this.update(this.scale * Math.exp(-step * WHEEL_SENSITIVITY));\n window.clearTimeout(this.wheelTimer);\n this.wheelTimer = window.setTimeout(() => {\n if (this.source === 'wheel') this.finish();\n }, WHEEL_IDLE_MS);\n };\n\n // Safari reports pinches (trackpad and touch) as gesture events\n\n private safariStart = (e: Event): void => {\n const { clientX, clientY } = e as SafariGestureEvent;\n e.preventDefault();\n // Touch pinches also produce pointer events; the gesture events win.\n if (this.source === 'pointer') this.source = undefined;\n this.begin('safari', { x: clientX, y: clientY });\n };\n\n private safariChange = (e: Event): void => {\n e.preventDefault();\n if (this.source === 'safari') this.update((e as SafariGestureEvent).scale);\n };\n\n private safariEnd = (e: Event): void => {\n e.preventDefault();\n if (this.source === 'safari') this.finish();\n };\n\n // ctrl/cmd + and - while the pointer or focus is on the grid\n\n private keyDown = (e: KeyboardEvent): void => {\n if (!(e.ctrlKey || e.metaKey) || e.altKey) return;\n const inGrid =\n this.hovering ||\n (this.surface && e.composedPath().includes(this.surface));\n if (!inGrid) return;\n\n const level = this.host.getLevel();\n let target: number;\n if (e.key === '=' || e.key === '+') target = level - 1;\n else if (e.key === '-' || e.key === '_') target = level + 1;\n else return;\n\n if (target < 0 || target >= GRID_ZOOM_COLUMNS.length) return;\n e.preventDefault();\n this.zoomToLevel(target, this.hovering ? this.lastPointer : undefined);\n };\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { LayoutType } from '../tiles/models';
2
+ /**
3
+ * The column counts the tile grid can snap to when pinch-zoom is enabled,
4
+ * from the biggest tiles to the densest thumbnail mosaic.
5
+ */
6
+ export declare const GRID_ZOOM_COLUMNS: readonly [1, 2, 3, 4, 6, 8, 12];
7
+ export declare const GRID_ZOOM_STORAGE_KEY = "ia-collection-browser-grid-zoom";
8
+ export declare const MIN_ZOOM_LEVEL = 0;
9
+ export declare const MAX_ZOOM_LEVEL: number;
10
+ /** Returns the index of the zoom level whose column count is closest to `columns`. */
11
+ export declare function nearestZoomLevel(columns: number): number;
12
+ export declare function clampZoomLevel(level: number): number;
13
+ /**
14
+ * Clamps a gesture scale so the tiles can't be stretched past the biggest or
15
+ * smallest level (with a little give so the limit feels soft).
16
+ * A scale above 1 means bigger tiles, so fewer columns.
17
+ */
18
+ export declare function clampGestureScale(startColumns: number, scale: number): number;
19
+ /** The level a gesture of the given scale should settle on. */
20
+ export declare function zoomLevelForScale(startColumns: number, scale: number): number;
21
+ /** Reads a persisted zoom level (stored as a column count), if there is a valid one. */
22
+ export declare function loadStoredZoomLevel(): number | undefined;
23
+ export declare function storeZoomLevel(level: number | undefined): void;
24
+ /**
25
+ * The inline CSS variables that give the infinite scroller `level`'s layout.
26
+ * `gridWidth` is the scroller's width in px, which sets the thumbnail size
27
+ * at the levels that show only thumbnails.
28
+ */
29
+ export declare function zoomLevelStyles(level: number, gridWidth: number): Record<string, string>;
30
+ /** The tile layout to use at `level`: dense levels drop details, the densest show only thumbnails. */
31
+ export declare function zoomLayoutType(level: number): LayoutType | undefined;