@k2b/ui 0.41.0 → 0.42.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 (59) hide show
  1. package/dist/base.css +22 -0
  2. package/dist/browser/actions/Dropdown.js +220 -110
  3. package/dist/browser/actions/Dropdown.js.map +1 -1
  4. package/dist/browser/actions/index.js.map +1 -1
  5. package/dist/browser/actions/menu-search.js +166 -0
  6. package/dist/browser/actions/menu-search.js.map +1 -0
  7. package/dist/browser/actions/menu-sheet.js +107 -13
  8. package/dist/browser/actions/menu-sheet.js.map +1 -1
  9. package/dist/browser/content/CalendarWeekView.js +13 -1
  10. package/dist/browser/content/CalendarWeekView.js.map +1 -1
  11. package/dist/browser/inputs/IconChoices.js +121 -0
  12. package/dist/browser/inputs/IconChoices.js.map +1 -0
  13. package/dist/browser/inputs/index.js +1 -0
  14. package/dist/browser/inputs/index.js.map +1 -1
  15. package/dist/browser/intl/messages.js +6 -0
  16. package/dist/browser/intl/messages.js.map +1 -1
  17. package/dist/browser/layout/AppWorkspace.js +118 -93
  18. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  19. package/dist/browser/text-editor/format.js +11 -7
  20. package/dist/browser/text-editor/format.js.map +1 -1
  21. package/dist/browser/text-editor/table.js +39 -11
  22. package/dist/browser/text-editor/table.js.map +1 -1
  23. package/dist/browser/text-editor.js +1 -1
  24. package/dist/browser/text-editor.js.map +1 -1
  25. package/dist/ssr/actions/Dropdown.js +118 -40
  26. package/dist/ssr/actions/Dropdown.js.map +1 -1
  27. package/dist/ssr/actions/index.js.map +1 -1
  28. package/dist/ssr/actions/menu-search.js +113 -0
  29. package/dist/ssr/actions/menu-search.js.map +1 -0
  30. package/dist/ssr/actions/menu-sheet.js +82 -13
  31. package/dist/ssr/actions/menu-sheet.js.map +1 -1
  32. package/dist/ssr/content/CalendarWeekView.js +13 -1
  33. package/dist/ssr/content/CalendarWeekView.js.map +1 -1
  34. package/dist/ssr/inputs/IconChoices.js +59 -0
  35. package/dist/ssr/inputs/IconChoices.js.map +1 -0
  36. package/dist/ssr/inputs/index.js +1 -0
  37. package/dist/ssr/inputs/index.js.map +1 -1
  38. package/dist/ssr/intl/messages.js +6 -0
  39. package/dist/ssr/intl/messages.js.map +1 -1
  40. package/dist/ssr/layout/AppWorkspace.js +40 -20
  41. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  42. package/dist/ssr/text-editor/format.js +11 -7
  43. package/dist/ssr/text-editor/format.js.map +1 -1
  44. package/dist/ssr/text-editor/table.js +39 -11
  45. package/dist/ssr/text-editor/table.js.map +1 -1
  46. package/dist/ssr/text-editor.js +1 -1
  47. package/dist/ssr/text-editor.js.map +1 -1
  48. package/dist/styles.css +1 -1
  49. package/dist/types/actions/Dropdown.d.ts +34 -3
  50. package/dist/types/actions/index.d.ts +1 -1
  51. package/dist/types/actions/menu-search.d.ts +27 -0
  52. package/dist/types/actions/menu-sheet.d.ts +5 -1
  53. package/dist/types/inputs/IconChoices.d.ts +19 -0
  54. package/dist/types/inputs/index.d.ts +2 -0
  55. package/dist/types/intl/messages.d.ts +3 -0
  56. package/dist/types/layout/AppWorkspace.d.ts +6 -1
  57. package/dist/types/text-editor/table.d.ts +2 -0
  58. package/dist/types/text-editor.d.ts +1 -1
  59. package/package.json +3 -1
@@ -8,12 +8,15 @@ import { runHydrationEvents as _$runHydrationEvents } from "solid-js/web";
8
8
  import { insert as _$insert } from "solid-js/web";
9
9
  import { createComponent as _$createComponent } from "solid-js/web";
10
10
  import { use as _$use } from "solid-js/web";
11
- var _tmpl$ = /*#__PURE__*/_$template(`<div class=k2b-gesture-menu__sheet-menu role=menu>`);
12
- import { onCleanup, onMount } from "solid-js";
11
+ var _tmpl$ = /*#__PURE__*/_$template(`<div class=k2b-gesture-menu__sheet-menu role=menu>`),
12
+ _tmpl$2 = /*#__PURE__*/_$template(`<div class=k2b-gesture-menu__sheet-menu role=dialog data-search>`),
13
+ _tmpl$3 = /*#__PURE__*/_$template(`<p class=k2b-dropdown__note>`);
14
+ import { createSignal, onCleanup, onMount, Show } from "solid-js";
13
15
  import { dialogCore } from "../feedback/dialog-core";
14
16
  import { LocaleProvider } from "../intl/locale";
15
17
  import BottomSheet, { bottomSheetOptions } from "../layout/BottomSheet";
16
18
  import { focusMenuRow, focusRowSegment } from "./menu-rows";
19
+ import { filterDropdownItems, MenuSearch } from "./menu-search";
17
20
 
18
21
  /** Phones, where a menu opens as a bottom sheet instead of beside its trigger. */
19
22
  export const PHONE_SHEET_QUERY = "(max-width: 47.999rem)";
@@ -95,11 +98,61 @@ function SheetMenu(props) {
95
98
  },
96
99
  close: () => props.close()
97
100
  }));
98
- _$effect(() => _$setAttribute(_el$, "aria-label", props.label));
101
+ _$effect(_p$ => {
102
+ var _v$ = props.label,
103
+ _v$2 = props.icons === "tile" ? "tile" : undefined;
104
+ _v$ !== _p$.e && _$setAttribute(_el$, "aria-label", _p$.e = _v$);
105
+ _v$2 !== _p$.t && _$setAttribute(_el$, "data-icons", _p$.t = _v$2);
106
+ return _p$;
107
+ }, {
108
+ e: undefined,
109
+ t: undefined
110
+ });
99
111
  _$runHydrationEvents();
100
112
  return _el$;
101
113
  })();
102
114
  }
115
+ /** A searchable menu in the sheet: the sheet's first field, so it takes the focus as the sheet opens, as in the menu. */
116
+ function SheetSearch(props) {
117
+ const [query, setQuery] = createSignal("");
118
+ const filter = props.search.filter;
119
+ return (() => {
120
+ var _el$2 = _$getNextElement(_tmpl$2);
121
+ _$insert(_el$2, _$createComponent(MenuSearch, {
122
+ get Items() {
123
+ return props.Items;
124
+ },
125
+ get items() {
126
+ return props.items;
127
+ },
128
+ get search() {
129
+ return {
130
+ ...props.search,
131
+ // The sheet's own items are wrapped already; a host's filter returns its unwrapped entries.
132
+ filter: value => filter ? props.wrap(filter(value)) : filterDropdownItems(props.items, value, props.locale)
133
+ };
134
+ },
135
+ get label() {
136
+ return props.label;
137
+ },
138
+ query: query,
139
+ setQuery: setQuery,
140
+ close: () => props.close()
141
+ }));
142
+ _$effect(_p$ => {
143
+ var _v$3 = props.label,
144
+ _v$4 = props.icons === "tile" ? "tile" : undefined;
145
+ _v$3 !== _p$.e && _$setAttribute(_el$2, "aria-label", _p$.e = _v$3);
146
+ _v$4 !== _p$.t && _$setAttribute(_el$2, "data-icons", _p$.t = _v$4);
147
+ return _p$;
148
+ }, {
149
+ e: undefined,
150
+ t: undefined
151
+ });
152
+ return _el$2;
153
+ })();
154
+ }
155
+
103
156
  /** Opens menu items as a bottom sheet; resolves after the sheet closed and the chosen action ran. */
104
157
  export const openMenuSheet = options => {
105
158
  let chosen;
@@ -118,19 +171,60 @@ export const openMenuSheet = options => {
118
171
  get children() {
119
172
  return _$createComponent(BottomSheet.Body, {
120
173
  get children() {
121
- return [_$memo(() => options.top?.(() => close())), _$createComponent(SheetMenu, {
122
- get Items() {
123
- return options.Items;
174
+ return [_$memo(() => options.top?.(() => close())), _$createComponent(Show, {
175
+ get when() {
176
+ return options.search;
124
177
  },
125
- get items() {
126
- return afterClose(options.items, choose);
178
+ get fallback() {
179
+ return _$createComponent(SheetMenu, {
180
+ get Items() {
181
+ return options.Items;
182
+ },
183
+ get items() {
184
+ return afterClose(options.items, choose);
185
+ },
186
+ get label() {
187
+ return options.label;
188
+ },
189
+ close: () => close(),
190
+ get dialog() {
191
+ return context.dialog;
192
+ },
193
+ get icons() {
194
+ return options.icons;
195
+ }
196
+ });
127
197
  },
128
- get label() {
129
- return options.label;
198
+ children: search => _$createComponent(SheetSearch, {
199
+ get Items() {
200
+ return options.Items;
201
+ },
202
+ get items() {
203
+ return afterClose(options.items, choose);
204
+ },
205
+ get search() {
206
+ return search();
207
+ },
208
+ wrap: items => afterClose(items, choose),
209
+ get label() {
210
+ return options.label;
211
+ },
212
+ get locale() {
213
+ return options.locale;
214
+ },
215
+ close: () => close(),
216
+ get icons() {
217
+ return options.icons;
218
+ }
219
+ })
220
+ }), _$createComponent(Show, {
221
+ get when() {
222
+ return options.note;
130
223
  },
131
- close: () => close(),
132
- get dialog() {
133
- return context.dialog;
224
+ get children() {
225
+ var _el$3 = _$getNextElement(_tmpl$3);
226
+ _$insert(_el$3, () => options.note);
227
+ return _el$3;
134
228
  }
135
229
  })];
136
230
  }
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["actions/menu-sheet.tsx"],"sourcesContent":["import { type JSX, onCleanup, onMount } from \"solid-js\";\nimport { dialogCore } from \"../feedback/dialog-core\";\nimport { LocaleProvider } from \"../intl/locale\";\nimport BottomSheet, { bottomSheetOptions } from \"../layout/BottomSheet\";\nimport type { DropdownAction, DropdownChoice, DropdownItem } from \"./Dropdown\";\nimport { focusMenuRow, focusRowSegment } from \"./menu-rows\";\n\n/** Phones, where a menu opens as a bottom sheet instead of beside its trigger. */\nexport const PHONE_SHEET_QUERY = \"(max-width: 47.999rem)\";\n\nexport const prefersMenuSheet = (): boolean => typeof matchMedia === \"function\" && matchMedia(PHONE_SHEET_QUERY).matches;\n\ntype SheetEntry = DropdownAction | DropdownChoice;\ntype SheetItem = SheetEntry | { items: readonly SheetEntry[] };\n\n/**\n * Follows a link the way a click on it would, so a page's own link handling, such as progressive navigation, still\n * applies; it runs only after the sheet has left the history.\n */\nconst followLink = (href: string) => {\n const link = document.createElement(\"a\");\n link.href = href;\n link.hidden = true;\n document.body.append(link);\n link.click();\n link.remove();\n};\n\n/**\n * The sheet's items hand their action to `run` instead of running it while the sheet closes: `dialogCore` settles the\n * sheet after it left the history, and only then may an action navigate or open the next dialog.\n * A choice or action that keeps the menu open, or an `immediate` action, runs at once.\n */\nconst afterClose = <T extends DropdownItem>(items: readonly T[], run: (action: () => void) => void): T[] => {\n const entry = (item: SheetEntry): SheetEntry => {\n // A plain link would navigate while the sheet's history entry is still being removed, and that Back could\n // undo the navigation; a plain click instead follows it once the sheet is gone. Modified clicks and external\n // links keep the browser's own behavior.\n if (!item.action && \"href\" in item && item.href && !(\"external\" in item && item.external)) {\n const href = item.href;\n return { ...item, action: () => run(() => followLink(href)) } as SheetEntry;\n }\n const action = item.action;\n if (!action || (\"closeOnSelect\" in item && item.closeOnSelect === false) || (\"immediate\" in item && item.immediate)) return item;\n return { ...item, action: () => run(action) } as SheetEntry;\n };\n return items.map((item) => {\n const sheetItem = item as SheetItem;\n return (\"items\" in sheetItem ? { ...sheetItem, items: sheetItem.items.map(entry) } : entry(sheetItem)) as T;\n });\n};\n\nconst menuItems = (menu: HTMLElement): HTMLElement[] =>\n Array.from(menu.querySelectorAll<HTMLElement>(\"[role^='menuitem']:not([aria-disabled='true'])\"));\n\n/**\n * The menu inside the sheet: the same items, at touch size, with arrow keys for a connected keyboard. Focus starts on\n * the sheet itself, which shows no ring on a phone; the first arrow key moves it into the menu.\n */\nfunction SheetMenu(props: {\n Items: MenuSheetOptions[\"Items\"];\n items: readonly DropdownItem[];\n label: string;\n close: () => void;\n dialog: HTMLDialogElement;\n}): JSX.Element {\n let menu!: HTMLDivElement;\n const onKeyDown = (event: KeyboardEvent) => {\n const items = menuItems(menu);\n if (!focusMenuRow(items, event.key) && !focusRowSegment(items, event.key)) return;\n event.preventDefault();\n };\n // Keys pressed on the sheet itself, while it is the top level of the shared dialog.\n const onSheetKeyDown = (event: KeyboardEvent) => {\n if (event.target === props.dialog && props.dialog.lastElementChild?.contains(menu)) onKeyDown(event);\n };\n onMount(() => {\n props.dialog.addEventListener(\"keydown\", onSheetKeyDown);\n onCleanup(() => props.dialog.removeEventListener(\"keydown\", onSheetKeyDown));\n });\n return (\n <div ref={menu} class=\"k2b-gesture-menu__sheet-menu\" role=\"menu\" aria-label={props.label} onKeyDown={onKeyDown}>\n <props.Items items={props.items} close={() => props.close()} />\n </div>\n );\n}\n\nexport type MenuSheetOptions = {\n /** Renders the rows: the dropdown's own `DropdownItems`, passed in so this module does not import it back. */\n Items: (props: { items: readonly DropdownItem[]; close: (restoreFocus?: boolean) => void }) => JSX.Element;\n items: readonly DropdownItem[];\n /** Names the sheet and its menu. */\n label: string;\n /** The render locale of the opener; the sheet renders outside its tree. */\n locale: string;\n /** Shown above the menu, for example a row of quick reactions. `close` closes the sheet. */\n top?: (close: () => void) => JSX.Element;\n signal?: AbortSignal;\n};\n\n/** Opens menu items as a bottom sheet; resolves after the sheet closed and the chosen action ran. */\nexport const openMenuSheet = (options: MenuSheetOptions): Promise<void> => {\n let chosen: (() => void) | undefined;\n const choose = (action: () => void) => {\n chosen = action;\n };\n return dialogCore\n .open<void>(\n (close, context) => (\n <LocaleProvider locale={options.locale}>\n <BottomSheet onDismiss={context.requestDismiss}>\n <BottomSheet.Body>\n {options.top?.(() => close())}\n <SheetMenu\n Items={options.Items}\n items={afterClose(options.items, choose)}\n label={options.label}\n close={() => close()}\n dialog={context.dialog}\n />\n </BottomSheet.Body>\n </BottomSheet>\n </LocaleProvider>\n ),\n { ...bottomSheetOptions, ariaLabel: options.label, history: true, signal: options.signal },\n )\n .then(() => chosen?.());\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["actions/menu-sheet.tsx"],"sourcesContent":["import { createSignal, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { dialogCore } from \"../feedback/dialog-core\";\nimport { LocaleProvider } from \"../intl/locale\";\nimport BottomSheet, { bottomSheetOptions } from \"../layout/BottomSheet\";\nimport type { DropdownAction, DropdownChoice, DropdownItem, DropdownSearch } from \"./Dropdown\";\nimport { focusMenuRow, focusRowSegment } from \"./menu-rows\";\nimport { filterDropdownItems, MenuSearch } from \"./menu-search\";\n\n/** Phones, where a menu opens as a bottom sheet instead of beside its trigger. */\nexport const PHONE_SHEET_QUERY = \"(max-width: 47.999rem)\";\n\nexport const prefersMenuSheet = (): boolean => typeof matchMedia === \"function\" && matchMedia(PHONE_SHEET_QUERY).matches;\n\ntype SheetEntry = DropdownAction | DropdownChoice;\ntype SheetItem = SheetEntry | { items: readonly SheetEntry[] };\n\n/**\n * Follows a link the way a click on it would, so a page's own link handling, such as progressive navigation, still\n * applies; it runs only after the sheet has left the history.\n */\nconst followLink = (href: string) => {\n const link = document.createElement(\"a\");\n link.href = href;\n link.hidden = true;\n document.body.append(link);\n link.click();\n link.remove();\n};\n\n/**\n * The sheet's items hand their action to `run` instead of running it while the sheet closes: `dialogCore` settles the\n * sheet after it left the history, and only then may an action navigate or open the next dialog.\n * A choice or action that keeps the menu open, or an `immediate` action, runs at once.\n */\nconst afterClose = <T extends DropdownItem>(items: readonly T[], run: (action: () => void) => void): T[] => {\n const entry = (item: SheetEntry): SheetEntry => {\n // A plain link would navigate while the sheet's history entry is still being removed, and that Back could\n // undo the navigation; a plain click instead follows it once the sheet is gone. Modified clicks and external\n // links keep the browser's own behavior.\n if (!item.action && \"href\" in item && item.href && !(\"external\" in item && item.external)) {\n const href = item.href;\n return { ...item, action: () => run(() => followLink(href)) } as SheetEntry;\n }\n const action = item.action;\n if (!action || (\"closeOnSelect\" in item && item.closeOnSelect === false) || (\"immediate\" in item && item.immediate)) return item;\n return { ...item, action: () => run(action) } as SheetEntry;\n };\n return items.map((item) => {\n const sheetItem = item as SheetItem;\n return (\"items\" in sheetItem ? { ...sheetItem, items: sheetItem.items.map(entry) } : entry(sheetItem)) as T;\n });\n};\n\nconst menuItems = (menu: HTMLElement): HTMLElement[] =>\n Array.from(menu.querySelectorAll<HTMLElement>(\"[role^='menuitem']:not([aria-disabled='true'])\"));\n\n/**\n * The menu inside the sheet: the same items, at touch size, with arrow keys for a connected keyboard. Focus starts on\n * the sheet itself, which shows no ring on a phone; the first arrow key moves it into the menu.\n */\nfunction SheetMenu(props: {\n Items: MenuSheetOptions[\"Items\"];\n items: readonly DropdownItem[];\n label: string;\n close: () => void;\n dialog: HTMLDialogElement;\n icons?: \"plain\" | \"tile\";\n}): JSX.Element {\n let menu!: HTMLDivElement;\n const onKeyDown = (event: KeyboardEvent) => {\n const items = menuItems(menu);\n if (!focusMenuRow(items, event.key) && !focusRowSegment(items, event.key)) return;\n event.preventDefault();\n };\n // Keys pressed on the sheet itself, while it is the top level of the shared dialog.\n const onSheetKeyDown = (event: KeyboardEvent) => {\n if (event.target === props.dialog && props.dialog.lastElementChild?.contains(menu)) onKeyDown(event);\n };\n onMount(() => {\n props.dialog.addEventListener(\"keydown\", onSheetKeyDown);\n onCleanup(() => props.dialog.removeEventListener(\"keydown\", onSheetKeyDown));\n });\n return (\n <div\n ref={menu}\n class=\"k2b-gesture-menu__sheet-menu\"\n role=\"menu\"\n aria-label={props.label}\n data-icons={props.icons === \"tile\" ? \"tile\" : undefined}\n onKeyDown={onKeyDown}\n >\n <props.Items items={props.items} close={() => props.close()} />\n </div>\n );\n}\n\nexport type MenuSheetOptions = {\n /** Renders the rows: the dropdown's own `DropdownItems`, passed in so this module does not import it back. */\n Items: (props: { items: readonly DropdownItem[]; close: (restoreFocus?: boolean) => void }) => JSX.Element;\n items: readonly DropdownItem[];\n /** Names the sheet and its menu. */\n label: string;\n /** The render locale of the opener; the sheet renders outside its tree. */\n locale: string;\n /** Shown above the menu, for example a row of quick reactions. `close` closes the sheet. */\n top?: (close: () => void) => JSX.Element;\n search?: DropdownSearch;\n icons?: \"plain\" | \"tile\";\n /** A quiet sentence below the entries. */\n note?: string;\n signal?: AbortSignal;\n};\n\n/** A searchable menu in the sheet: the sheet's first field, so it takes the focus as the sheet opens, as in the menu. */\nfunction SheetSearch(props: {\n Items: MenuSheetOptions[\"Items\"];\n items: readonly DropdownItem[];\n search: DropdownSearch;\n wrap: <T extends DropdownItem>(items: readonly T[]) => T[];\n label: string;\n locale: string;\n close: () => void;\n icons?: \"plain\" | \"tile\";\n}): JSX.Element {\n const [query, setQuery] = createSignal(\"\");\n const filter = props.search.filter;\n return (\n <div\n class=\"k2b-gesture-menu__sheet-menu\"\n role=\"dialog\"\n aria-label={props.label}\n data-search=\"\"\n data-icons={props.icons === \"tile\" ? \"tile\" : undefined}\n >\n <MenuSearch\n Items={props.Items}\n items={props.items}\n search={{\n ...props.search,\n // The sheet's own items are wrapped already; a host's filter returns its unwrapped entries.\n filter: (value) => (filter ? props.wrap(filter(value)) : filterDropdownItems(props.items, value, props.locale)),\n }}\n label={props.label}\n query={query}\n setQuery={setQuery}\n close={() => props.close()}\n />\n </div>\n );\n}\n\n/** Opens menu items as a bottom sheet; resolves after the sheet closed and the chosen action ran. */\nexport const openMenuSheet = (options: MenuSheetOptions): Promise<void> => {\n let chosen: (() => void) | undefined;\n const choose = (action: () => void) => {\n chosen = action;\n };\n return dialogCore\n .open<void>(\n (close, context) => (\n <LocaleProvider locale={options.locale}>\n <BottomSheet onDismiss={context.requestDismiss}>\n <BottomSheet.Body>\n {options.top?.(() => close())}\n <Show\n when={options.search}\n fallback={\n <SheetMenu\n Items={options.Items}\n items={afterClose(options.items, choose)}\n label={options.label}\n close={() => close()}\n dialog={context.dialog}\n icons={options.icons}\n />\n }\n >\n {(search) => (\n <SheetSearch\n Items={options.Items}\n items={afterClose(options.items, choose)}\n search={search()}\n wrap={(items) => afterClose(items, choose)}\n label={options.label}\n locale={options.locale}\n close={() => close()}\n icons={options.icons}\n />\n )}\n </Show>\n <Show when={options.note}>\n <p class=\"k2b-dropdown__note\">{options.note}</p>\n </Show>\n </BottomSheet.Body>\n </BottomSheet>\n </LocaleProvider>\n ),\n { ...bottomSheetOptions, ariaLabel: options.label, history: true, signal: options.signal },\n )\n .then(() => chosen?.());\n};\n"],"mappings":"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","ignoreList":[]}
@@ -188,6 +188,7 @@ export const CalendarWeekView = props => {
188
188
  let lastWheel = 0;
189
189
  let fingers = 0;
190
190
  let restTimer = 0;
191
+ let restFrame = 0;
191
192
  let ready = false;
192
193
  /** Whether a wheel or a finger moved the strip since it last settled, and the way it last moved. */
193
194
  let gesture = false;
@@ -211,6 +212,15 @@ export const CalendarWeekView = props => {
211
212
  settling = false;
212
213
  }, 450);
213
214
  };
215
+ /**
216
+ * A busy page runs the rest timer before the scroll event of a reveal still on its way, and a settle then would stop
217
+ * that reveal short of what it brings into view. Two frames tell: a scroll still moving reports before them and starts over.
218
+ */
219
+ const settleOnceStill = () => {
220
+ restFrame = requestAnimationFrame(() => {
221
+ restFrame = requestAnimationFrame(settle);
222
+ });
223
+ };
214
224
  const onWheel = event => {
215
225
  if (!scroller || event.ctrlKey || view.dragging()) return;
216
226
  // A gesture that goes on takes over from a glide onto a day start; the strip settles again once it rests.
@@ -269,7 +279,8 @@ export const CalendarWeekView = props => {
269
279
  rememberAnchor();
270
280
  });
271
281
  window.clearTimeout(restTimer);
272
- restTimer = window.setTimeout(settle, fingers > 0 ? 160 : 140);
282
+ cancelAnimationFrame(restFrame);
283
+ restTimer = window.setTimeout(settleOnceStill, fingers > 0 ? 160 : 140);
273
284
  };
274
285
 
275
286
  // Days loaded before the strip's first day shift it; the view keeps its days in place.
@@ -346,6 +357,7 @@ export const CalendarWeekView = props => {
346
357
  onCleanup(() => {
347
358
  observer?.disconnect();
348
359
  window.clearTimeout(restTimer);
360
+ cancelAnimationFrame(restFrame);
349
361
  window.clearTimeout(reportTimer);
350
362
  window.clearTimeout(glideTimer);
351
363
  if (frame) cancelAnimationFrame(frame);