List.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import Element, { emit, on, s } from '@dom111/element';
  2. import Collection from '../lib/Collection';
  3. import DAV from '../lib/DAV';
  4. import Entry from '../lib/Entry';
  5. import Item from './Item';
  6. import State from '../lib/State';
  7. import previewItems from '../lib/previewItems';
  8. import supportsFocusWithin from '../lib/supportsFocusWithin';
  9. export class List extends Element<HTMLUListElement> {
  10. #dav: DAV;
  11. #state: State;
  12. constructor(dav: DAV, state: State) {
  13. super(s('<ul class="loading"></ul>'));
  14. this.#dav = dav;
  15. this.#state = state;
  16. this.load();
  17. this.bindEvents();
  18. }
  19. private bindEvents(): void {
  20. this.#state.on('updated', (bypassCache: boolean): void => {
  21. if (this.#state.isDirectory()) {
  22. this.load(bypassCache);
  23. return;
  24. }
  25. const item = this.query<HTMLLIElement>(
  26. `[data-full-path="${this.#state.getPath()}"]`
  27. );
  28. if (!item) {
  29. return;
  30. }
  31. emit(item, new MouseEvent('click'));
  32. });
  33. this.#state.on('collection-updated', (): void => this.render());
  34. const arrowHandler = (event: KeyboardEvent): void => {
  35. if (!['ArrowUp', 'ArrowDown'].includes(event.key)) {
  36. return;
  37. }
  38. event.preventDefault();
  39. event.stopPropagation();
  40. const current = this.query(
  41. `li:focus${supportsFocusWithin ? ', li:focus-within' : ''}`
  42. ) as HTMLElement,
  43. [previous, next] = current
  44. ? previewItems(current)
  45. : [
  46. this.element().firstElementChild as HTMLElement,
  47. this.element().firstElementChild as HTMLElement,
  48. ];
  49. if (event.key === 'ArrowUp' && previous) {
  50. previous.focus();
  51. }
  52. if (event.key === 'ArrowDown' && next) {
  53. next.focus();
  54. }
  55. };
  56. on(document, 'keydown', arrowHandler);
  57. }
  58. async load(bypassCache: boolean = false): Promise<void> {
  59. const collection = await this.#dav.list(this.#state.getPath(), bypassCache);
  60. if (!collection) {
  61. return;
  62. }
  63. this.update(collection);
  64. }
  65. private render(): void {
  66. this.addClass('loading');
  67. this.empty();
  68. this.#state
  69. .getCollection()
  70. .forEach((entry: Entry): void =>
  71. this.append(new Item(entry, this.#dav, this.#state))
  72. );
  73. this.removeClass('loading');
  74. }
  75. update(collection: Collection): void {
  76. this.#state.setCollection(collection);
  77. this.render();
  78. }
  79. }
  80. export default List;