react-native-gtkx 0.2.0-alpha.1 → 0.2.0-alpha.2

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 (144) hide show
  1. package/README.md +11 -2
  2. package/dist/adw/widgets.generated.d.ts +4 -4
  3. package/dist/adw/widgets.generated.js +51 -51
  4. package/dist/adw/widgets.generated.js.map +1 -1
  5. package/dist/animated/create-animated.d.ts +2 -0
  6. package/dist/animated/create-animated.js +2 -0
  7. package/dist/animated/create-animated.js.map +1 -1
  8. package/dist/animated/index.d.ts +2 -0
  9. package/dist/animated/index.js +1 -0
  10. package/dist/animated/index.js.map +1 -1
  11. package/dist/animated/value-xy.d.ts +41 -0
  12. package/dist/animated/value-xy.js +90 -0
  13. package/dist/animated/value-xy.js.map +1 -0
  14. package/dist/animated/value.d.ts +9 -0
  15. package/dist/animated/value.js +30 -2
  16. package/dist/animated/value.js.map +1 -1
  17. package/dist/common/icon.d.ts +23 -0
  18. package/dist/common/icon.js +14 -0
  19. package/dist/common/icon.js.map +1 -0
  20. package/dist/common/index.d.ts +2 -0
  21. package/dist/common/index.js +2 -0
  22. package/dist/common/index.js.map +1 -1
  23. package/dist/common/list.d.ts +82 -0
  24. package/dist/common/list.js +166 -0
  25. package/dist/common/list.js.map +1 -0
  26. package/dist/common/widget.d.ts +1 -22
  27. package/dist/common/widget.js +75 -8
  28. package/dist/common/widget.js.map +1 -1
  29. package/dist/components/animated.d.ts +4 -2
  30. package/dist/components/animated.js +3 -1
  31. package/dist/components/animated.js.map +1 -1
  32. package/dist/components/app-registry.d.ts +26 -0
  33. package/dist/components/app-registry.js.map +1 -1
  34. package/dist/components/host-node.d.ts +7 -0
  35. package/dist/components/host-node.js +26 -1
  36. package/dist/components/host-node.js.map +1 -1
  37. package/dist/components/index.d.ts +6 -2
  38. package/dist/components/index.js +2 -1
  39. package/dist/components/index.js.map +1 -1
  40. package/dist/components/measure.d.ts +13 -0
  41. package/dist/components/measure.js +51 -0
  42. package/dist/components/measure.js.map +1 -0
  43. package/dist/components/modal.d.ts +1 -1
  44. package/dist/components/press-event.d.ts +28 -0
  45. package/dist/components/press-event.js +46 -0
  46. package/dist/components/press-event.js.map +1 -0
  47. package/dist/components/pressable.d.ts +2 -6
  48. package/dist/components/pressable.js +14 -8
  49. package/dist/components/pressable.js.map +1 -1
  50. package/dist/components/scroll-view.d.ts +4 -0
  51. package/dist/components/scroll-view.js +8 -2
  52. package/dist/components/scroll-view.js.map +1 -1
  53. package/dist/components/text-input.js +1 -1
  54. package/dist/components/text-input.js.map +1 -1
  55. package/dist/components/text.js +25 -1
  56. package/dist/components/text.js.map +1 -1
  57. package/dist/components/use-layout-child.js +17 -14
  58. package/dist/components/use-layout-child.js.map +1 -1
  59. package/dist/components/view.d.ts +9 -4
  60. package/dist/components/view.js +8 -2
  61. package/dist/components/view.js.map +1 -1
  62. package/dist/contracts.d.ts +19 -3
  63. package/dist/contracts.js.map +1 -1
  64. package/dist/gtk/index.d.ts +2 -1
  65. package/dist/gtk/index.js +8 -1
  66. package/dist/gtk/index.js.map +1 -1
  67. package/dist/gtk/widgets.generated.d.ts +14 -14
  68. package/dist/gtk/widgets.generated.js +101 -102
  69. package/dist/gtk/widgets.generated.js.map +1 -1
  70. package/dist/gtk/window-actions.d.ts +77 -0
  71. package/dist/gtk/window-actions.js +193 -0
  72. package/dist/gtk/window-actions.js.map +1 -0
  73. package/dist/gtkx/bridge/geometry.d.ts +15 -0
  74. package/dist/gtkx/bridge/geometry.js +38 -0
  75. package/dist/gtkx/bridge/geometry.js.map +1 -0
  76. package/dist/gtkx/bridge/index.d.ts +2 -0
  77. package/dist/gtkx/bridge/index.js +7 -2
  78. package/dist/gtkx/bridge/index.js.map +1 -1
  79. package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
  80. package/dist/gtkx/bridge/slot-portal.js +17 -0
  81. package/dist/gtkx/bridge/slot-portal.js.map +1 -0
  82. package/dist/gtkx/bridge/svg-node.js +10 -54
  83. package/dist/gtkx/bridge/svg-node.js.map +1 -1
  84. package/dist/gtkx/bridge/use-signal.js +2 -2
  85. package/dist/gtkx/bridge/use-signal.js.map +1 -1
  86. package/dist/index.d.ts +2 -2
  87. package/dist/index.js +1 -1
  88. package/dist/index.js.map +1 -1
  89. package/dist/mcp/data/generated.d.ts +99 -77
  90. package/dist/mcp/data/generated.js +107 -76
  91. package/dist/mcp/data/generated.js.map +1 -1
  92. package/dist/mcp/server.js +1 -1
  93. package/dist/mcp/server.js.map +1 -1
  94. package/dist/navigation/sidebar.d.ts +31 -1
  95. package/dist/navigation/sidebar.js +12 -2
  96. package/dist/navigation/sidebar.js.map +1 -1
  97. package/dist/responder/system.d.ts +27 -0
  98. package/dist/responder/system.js +184 -0
  99. package/dist/responder/system.js.map +1 -0
  100. package/dist/responder/touch-history.d.ts +10 -0
  101. package/dist/responder/touch-history.js +78 -0
  102. package/dist/responder/touch-history.js.map +1 -0
  103. package/dist/responder/types.d.ts +63 -0
  104. package/dist/responder/types.js +29 -0
  105. package/dist/responder/types.js.map +1 -0
  106. package/dist/responder/use-responder.d.ts +12 -0
  107. package/dist/responder/use-responder.js +98 -0
  108. package/dist/responder/use-responder.js.map +1 -0
  109. package/dist/runner/host-dev.js +3 -1
  110. package/dist/runner/host-dev.js.map +1 -1
  111. package/dist/runner/host.js +5 -1
  112. package/dist/runner/host.js.map +1 -1
  113. package/dist/runner/index.js +1 -1
  114. package/dist/runner/index.js.map +1 -1
  115. package/dist/sea/gtkx-config-module.js +1 -1
  116. package/dist/sea/gtkx-config-module.js.map +1 -1
  117. package/dist/style/box-shadow.d.ts +7 -0
  118. package/dist/style/box-shadow.js +197 -0
  119. package/dist/style/box-shadow.js.map +1 -0
  120. package/dist/style/index.d.ts +2 -0
  121. package/dist/style/index.js +2 -0
  122. package/dist/style/index.js.map +1 -1
  123. package/dist/style/split-style.d.ts +5 -2
  124. package/dist/style/split-style.js +31 -3
  125. package/dist/style/split-style.js.map +1 -1
  126. package/dist/style/text-decoration.d.ts +15 -0
  127. package/dist/style/text-decoration.js +26 -0
  128. package/dist/style/text-decoration.js.map +1 -0
  129. package/dist/style/visual-css.js +37 -1
  130. package/dist/style/visual-css.js.map +1 -1
  131. package/dist/svg/gradient-geometry.js +2 -5
  132. package/dist/svg/gradient-geometry.js.map +1 -1
  133. package/dist/testing/index.js.map +1 -1
  134. package/dist/vendor/react-native/pan-responder.d.ts +102 -0
  135. package/dist/vendor/react-native/pan-responder.js +196 -0
  136. package/dist/vendor/react-native/pan-responder.js.map +1 -0
  137. package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
  138. package/dist/vendor/react-native/touch-history-math.js +102 -0
  139. package/dist/vendor/react-native/touch-history-math.js.map +1 -0
  140. package/dist/vite/index.js +1 -1
  141. package/dist/vite/index.js.map +1 -1
  142. package/dist/vitest/index.d.ts +1 -1
  143. package/dist/vitest/index.js.map +1 -1
  144. package/package.json +9 -9
@@ -5,12 +5,19 @@
5
5
  // GTK4 CSS is not web CSS. What this generator relies on:
6
6
  // - background-color, border-*, border-radius (incl. per-corner), opacity,
7
7
  // color, font-*, letter-spacing: supported by GTK4;
8
+ // - box-shadow and outline-*: supported by GTK4, and used by Adwaita itself
9
+ // for the `.card`/`.boxed-list` frame and for every focus ring in the
10
+ // theme — which is why an app cannot reproduce the platform's own look
11
+ // without them. box-shadow is compiled by ./box-shadow.ts;
8
12
  // - line-height: supported since GTK 4.6 (we target 4.20+);
9
13
  // - text-align does NOT exist in GTK CSS — textAlign is applied by the Text
10
14
  // component via widget properties (justify/xalign), so it is skipped here;
15
+ // - text-decoration does not exist for GTK widgets either: Pango carries it,
16
+ // and the Text component applies textDecorationLine as label attributes;
11
17
  // - transform does NOT exist as a widget property in GTK4 CSS either: it is
12
18
  // composed into a matrix by ./transform.ts and applied as the GskTransform
13
19
  // of the child's allocation, so it is skipped here as well.
20
+ import { boxShadowToCss } from "./box-shadow";
14
21
  import { parseColor } from "./colors";
15
22
  import { warnOnce } from "./dev-warning";
16
23
  const px = (value) => `${value}px`;
@@ -44,6 +51,12 @@ export const visualStyleToCss = (visual) => {
44
51
  if (visual.opacity !== undefined) {
45
52
  decls.push(`opacity: ${clamp01(visual.opacity)};`);
46
53
  }
54
+ if (visual.boxShadow !== undefined) {
55
+ const shadow = boxShadowToCss(visual.boxShadow);
56
+ if (shadow !== null) {
57
+ decls.push(`box-shadow: ${shadow};`);
58
+ }
59
+ }
47
60
  // GTK defaults border-style to none — width alone renders nothing. An
48
61
  // explicit borderStyle wins (GTK4 CSS supports solid/dotted/dashed);
49
62
  // otherwise any positive border width turns the border on with "solid".
@@ -107,6 +120,28 @@ export const visualStyleToCss = (visual) => {
107
120
  if (visual.borderBottomLeftRadius !== undefined) {
108
121
  decls.push(`border-bottom-left-radius: ${px(visual.borderBottomLeftRadius)};`);
109
122
  }
123
+ // outline is a ring around the border box that takes no layout space, so
124
+ // unlike `border` it needs no Yoga involvement at all — GTK4 draws it from
125
+ // CSS exactly as the web does, which is how Adwaita itself paints every
126
+ // focus ring in the theme. GTK, like the web, defaults outline-style to
127
+ // `none`, so a width on its own would draw nothing; the same auto-solid
128
+ // rule the borders above use applies.
129
+ const hasVisibleOutlineWidth = visual.outlineWidth !== undefined && visual.outlineWidth > 0;
130
+ if (visual.outlineStyle !== undefined) {
131
+ decls.push(`outline-style: ${visual.outlineStyle};`);
132
+ }
133
+ else if (hasVisibleOutlineWidth) {
134
+ decls.push("outline-style: solid;");
135
+ }
136
+ if (visual.outlineWidth !== undefined) {
137
+ decls.push(`outline-width: ${px(visual.outlineWidth)};`);
138
+ }
139
+ if (visual.outlineColor !== undefined) {
140
+ pushColor(decls, "outline-color", visual.outlineColor, "outlineColor");
141
+ }
142
+ if (visual.outlineOffset !== undefined) {
143
+ decls.push(`outline-offset: ${px(visual.outlineOffset)};`);
144
+ }
110
145
  if (visual.color !== undefined) {
111
146
  pushColor(decls, "color", visual.color, "color");
112
147
  }
@@ -128,7 +163,8 @@ export const visualStyleToCss = (visual) => {
128
163
  if (visual.lineHeight !== undefined) {
129
164
  decls.push(`line-height: ${px(visual.lineHeight)};`);
130
165
  }
131
- // visual.textAlign, visual.transform: intentionally not CSS (see header).
166
+ // visual.textAlign, visual.textDecorationLine, visual.transform:
167
+ // intentionally not CSS (see header).
132
168
  return decls.join("\n");
133
169
  };
134
170
  //# sourceMappingURL=visual-css.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"visual-css.js","sourceRoot":"","sources":["../../src/style/visual-css.ts"],"names":[],"mappings":"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","sourcesContent":["// Compiles a VisualStyle into a GTK4 CSS declaration block (no selector).\n// Pure module — the resulting string is registered as a class through an\n// injected `css` function (see registry.ts / registry.gtkx.ts).\n//\n// GTK4 CSS is not web CSS. What this generator relies on:\n// - background-color, border-*, border-radius (incl. per-corner), opacity,\n// color, font-*, letter-spacing: supported by GTK4;\n// - line-height: supported since GTK 4.6 (we target 4.20+);\n// - text-align does NOT exist in GTK CSS — textAlign is applied by the Text\n// component via widget properties (justify/xalign), so it is skipped here;\n// - transform does NOT exist as a widget property in GTK4 CSS either: it is\n// composed into a matrix by ./transform.ts and applied as the GskTransform\n// of the child's allocation, so it is skipped here as well.\n\nimport type { VisualStyle } from \"../contracts\"\nimport { parseColor } from \"./colors\"\nimport { warnOnce } from \"./dev-warning\"\n\nconst px = (value: number): string => `${value}px`\n\nconst clamp01 = (value: number): number => Math.min(1, Math.max(0, value))\n\nconst pushColor = (\n declarations: string[],\n cssProperty: string,\n value: string,\n styleProperty: string,\n): void => {\n const parsed = parseColor(value)\n if (parsed === null) {\n warnOnce(\n `invalid-color:${styleProperty}:${value}`,\n `[react-native-gtkx] Invalid color ${JSON.stringify(value)} in \"${styleProperty}\" — declaration dropped`,\n )\n return\n }\n declarations.push(`${cssProperty}: ${parsed};`)\n}\n\nconst formatFontFamily = (family: string): string => {\n const trimmed = family.trim()\n if (/^(\".*\"|'.*')$/.test(trimmed)) {\n return trimmed\n }\n return /^[A-Za-z][\\w-]*$/.test(trimmed)\n ? trimmed\n : `\"${trimmed.replace(/\"/g, '\\\\\"')}\"`\n}\n\n/**\n * Renders a VisualStyle as GTK CSS declarations (one per line, stable\n * order). Returns \"\" when the style produces no CSS (e.g. transform-only).\n */\nexport const visualStyleToCss = (visual: VisualStyle): string => {\n const decls: string[] = []\n\n if (visual.backgroundColor !== undefined) {\n pushColor(\n decls,\n \"background-color\",\n visual.backgroundColor,\n \"backgroundColor\",\n )\n }\n if (visual.opacity !== undefined) {\n decls.push(`opacity: ${clamp01(visual.opacity)};`)\n }\n\n // GTK defaults border-style to none — width alone renders nothing. An\n // explicit borderStyle wins (GTK4 CSS supports solid/dotted/dashed);\n // otherwise any positive border width turns the border on with \"solid\".\n const hasVisibleBorderWidth = [\n visual.borderWidth,\n visual.borderTopWidth,\n visual.borderRightWidth,\n visual.borderBottomWidth,\n visual.borderLeftWidth,\n ].some((width) => width !== undefined && width > 0)\n if (visual.borderStyle !== undefined) {\n decls.push(`border-style: ${visual.borderStyle};`)\n } else if (hasVisibleBorderWidth) {\n decls.push(\"border-style: solid;\")\n }\n // Shorthands first, then per-side overrides so the sides win.\n if (visual.borderWidth !== undefined) {\n decls.push(`border-width: ${px(visual.borderWidth)};`)\n }\n if (visual.borderTopWidth !== undefined) {\n decls.push(`border-top-width: ${px(visual.borderTopWidth)};`)\n }\n if (visual.borderRightWidth !== undefined) {\n decls.push(`border-right-width: ${px(visual.borderRightWidth)};`)\n }\n if (visual.borderBottomWidth !== undefined) {\n decls.push(`border-bottom-width: ${px(visual.borderBottomWidth)};`)\n }\n if (visual.borderLeftWidth !== undefined) {\n decls.push(`border-left-width: ${px(visual.borderLeftWidth)};`)\n }\n if (visual.borderColor !== undefined) {\n pushColor(decls, \"border-color\", visual.borderColor, \"borderColor\")\n }\n if (visual.borderTopColor !== undefined) {\n pushColor(\n decls,\n \"border-top-color\",\n visual.borderTopColor,\n \"borderTopColor\",\n )\n }\n if (visual.borderRightColor !== undefined) {\n pushColor(\n decls,\n \"border-right-color\",\n visual.borderRightColor,\n \"borderRightColor\",\n )\n }\n if (visual.borderBottomColor !== undefined) {\n pushColor(\n decls,\n \"border-bottom-color\",\n visual.borderBottomColor,\n \"borderBottomColor\",\n )\n }\n if (visual.borderLeftColor !== undefined) {\n pushColor(\n decls,\n \"border-left-color\",\n visual.borderLeftColor,\n \"borderLeftColor\",\n )\n }\n if (visual.borderRadius !== undefined) {\n decls.push(`border-radius: ${px(visual.borderRadius)};`)\n }\n // Per-corner radii come after the shorthand so they override it.\n if (visual.borderTopLeftRadius !== undefined) {\n decls.push(`border-top-left-radius: ${px(visual.borderTopLeftRadius)};`)\n }\n if (visual.borderTopRightRadius !== undefined) {\n decls.push(`border-top-right-radius: ${px(visual.borderTopRightRadius)};`)\n }\n if (visual.borderBottomRightRadius !== undefined) {\n decls.push(\n `border-bottom-right-radius: ${px(visual.borderBottomRightRadius)};`,\n )\n }\n if (visual.borderBottomLeftRadius !== undefined) {\n decls.push(\n `border-bottom-left-radius: ${px(visual.borderBottomLeftRadius)};`,\n )\n }\n\n if (visual.color !== undefined) {\n pushColor(decls, \"color\", visual.color, \"color\")\n }\n if (visual.fontFamily !== undefined) {\n decls.push(`font-family: ${formatFontFamily(visual.fontFamily)};`)\n }\n if (visual.fontSize !== undefined) {\n decls.push(`font-size: ${px(visual.fontSize)};`)\n }\n if (visual.fontStyle !== undefined) {\n decls.push(`font-style: ${visual.fontStyle};`)\n }\n if (visual.fontWeight !== undefined) {\n decls.push(`font-weight: ${visual.fontWeight};`)\n }\n if (visual.letterSpacing !== undefined) {\n decls.push(`letter-spacing: ${px(visual.letterSpacing)};`)\n }\n if (visual.lineHeight !== undefined) {\n decls.push(`line-height: ${px(visual.lineHeight)};`)\n }\n\n // visual.textAlign, visual.transform: intentionally not CSS (see header).\n\n return decls.join(\"\\n\")\n}\n"]}
1
+ {"version":3,"file":"visual-css.js","sourceRoot":"","sources":["../../src/style/visual-css.ts"],"names":[],"mappings":"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","sourcesContent":["// Compiles a VisualStyle into a GTK4 CSS declaration block (no selector).\n// Pure module — the resulting string is registered as a class through an\n// injected `css` function (see registry.ts / registry.gtkx.ts).\n//\n// GTK4 CSS is not web CSS. What this generator relies on:\n// - background-color, border-*, border-radius (incl. per-corner), opacity,\n// color, font-*, letter-spacing: supported by GTK4;\n// - box-shadow and outline-*: supported by GTK4, and used by Adwaita itself\n// for the `.card`/`.boxed-list` frame and for every focus ring in the\n// theme — which is why an app cannot reproduce the platform's own look\n// without them. box-shadow is compiled by ./box-shadow.ts;\n// - line-height: supported since GTK 4.6 (we target 4.20+);\n// - text-align does NOT exist in GTK CSS — textAlign is applied by the Text\n// component via widget properties (justify/xalign), so it is skipped here;\n// - text-decoration does not exist for GTK widgets either: Pango carries it,\n// and the Text component applies textDecorationLine as label attributes;\n// - transform does NOT exist as a widget property in GTK4 CSS either: it is\n// composed into a matrix by ./transform.ts and applied as the GskTransform\n// of the child's allocation, so it is skipped here as well.\n\nimport type { VisualStyle } from \"../contracts\"\nimport { boxShadowToCss } from \"./box-shadow\"\nimport { parseColor } from \"./colors\"\nimport { warnOnce } from \"./dev-warning\"\n\nconst px = (value: number): string => `${value}px`\n\nconst clamp01 = (value: number): number => Math.min(1, Math.max(0, value))\n\nconst pushColor = (\n declarations: string[],\n cssProperty: string,\n value: string,\n styleProperty: string,\n): void => {\n const parsed = parseColor(value)\n if (parsed === null) {\n warnOnce(\n `invalid-color:${styleProperty}:${value}`,\n `[react-native-gtkx] Invalid color ${JSON.stringify(value)} in \"${styleProperty}\" — declaration dropped`,\n )\n return\n }\n declarations.push(`${cssProperty}: ${parsed};`)\n}\n\nconst formatFontFamily = (family: string): string => {\n const trimmed = family.trim()\n if (/^(\".*\"|'.*')$/.test(trimmed)) {\n return trimmed\n }\n return /^[A-Za-z][\\w-]*$/.test(trimmed)\n ? trimmed\n : `\"${trimmed.replace(/\"/g, '\\\\\"')}\"`\n}\n\n/**\n * Renders a VisualStyle as GTK CSS declarations (one per line, stable\n * order). Returns \"\" when the style produces no CSS (e.g. transform-only).\n */\nexport const visualStyleToCss = (visual: VisualStyle): string => {\n const decls: string[] = []\n\n if (visual.backgroundColor !== undefined) {\n pushColor(\n decls,\n \"background-color\",\n visual.backgroundColor,\n \"backgroundColor\",\n )\n }\n if (visual.opacity !== undefined) {\n decls.push(`opacity: ${clamp01(visual.opacity)};`)\n }\n if (visual.boxShadow !== undefined) {\n const shadow = boxShadowToCss(visual.boxShadow)\n if (shadow !== null) {\n decls.push(`box-shadow: ${shadow};`)\n }\n }\n\n // GTK defaults border-style to none — width alone renders nothing. An\n // explicit borderStyle wins (GTK4 CSS supports solid/dotted/dashed);\n // otherwise any positive border width turns the border on with \"solid\".\n const hasVisibleBorderWidth = [\n visual.borderWidth,\n visual.borderTopWidth,\n visual.borderRightWidth,\n visual.borderBottomWidth,\n visual.borderLeftWidth,\n ].some((width) => width !== undefined && width > 0)\n if (visual.borderStyle !== undefined) {\n decls.push(`border-style: ${visual.borderStyle};`)\n } else if (hasVisibleBorderWidth) {\n decls.push(\"border-style: solid;\")\n }\n // Shorthands first, then per-side overrides so the sides win.\n if (visual.borderWidth !== undefined) {\n decls.push(`border-width: ${px(visual.borderWidth)};`)\n }\n if (visual.borderTopWidth !== undefined) {\n decls.push(`border-top-width: ${px(visual.borderTopWidth)};`)\n }\n if (visual.borderRightWidth !== undefined) {\n decls.push(`border-right-width: ${px(visual.borderRightWidth)};`)\n }\n if (visual.borderBottomWidth !== undefined) {\n decls.push(`border-bottom-width: ${px(visual.borderBottomWidth)};`)\n }\n if (visual.borderLeftWidth !== undefined) {\n decls.push(`border-left-width: ${px(visual.borderLeftWidth)};`)\n }\n if (visual.borderColor !== undefined) {\n pushColor(decls, \"border-color\", visual.borderColor, \"borderColor\")\n }\n if (visual.borderTopColor !== undefined) {\n pushColor(\n decls,\n \"border-top-color\",\n visual.borderTopColor,\n \"borderTopColor\",\n )\n }\n if (visual.borderRightColor !== undefined) {\n pushColor(\n decls,\n \"border-right-color\",\n visual.borderRightColor,\n \"borderRightColor\",\n )\n }\n if (visual.borderBottomColor !== undefined) {\n pushColor(\n decls,\n \"border-bottom-color\",\n visual.borderBottomColor,\n \"borderBottomColor\",\n )\n }\n if (visual.borderLeftColor !== undefined) {\n pushColor(\n decls,\n \"border-left-color\",\n visual.borderLeftColor,\n \"borderLeftColor\",\n )\n }\n if (visual.borderRadius !== undefined) {\n decls.push(`border-radius: ${px(visual.borderRadius)};`)\n }\n // Per-corner radii come after the shorthand so they override it.\n if (visual.borderTopLeftRadius !== undefined) {\n decls.push(`border-top-left-radius: ${px(visual.borderTopLeftRadius)};`)\n }\n if (visual.borderTopRightRadius !== undefined) {\n decls.push(`border-top-right-radius: ${px(visual.borderTopRightRadius)};`)\n }\n if (visual.borderBottomRightRadius !== undefined) {\n decls.push(\n `border-bottom-right-radius: ${px(visual.borderBottomRightRadius)};`,\n )\n }\n if (visual.borderBottomLeftRadius !== undefined) {\n decls.push(\n `border-bottom-left-radius: ${px(visual.borderBottomLeftRadius)};`,\n )\n }\n\n // outline is a ring around the border box that takes no layout space, so\n // unlike `border` it needs no Yoga involvement at all — GTK4 draws it from\n // CSS exactly as the web does, which is how Adwaita itself paints every\n // focus ring in the theme. GTK, like the web, defaults outline-style to\n // `none`, so a width on its own would draw nothing; the same auto-solid\n // rule the borders above use applies.\n const hasVisibleOutlineWidth =\n visual.outlineWidth !== undefined && visual.outlineWidth > 0\n if (visual.outlineStyle !== undefined) {\n decls.push(`outline-style: ${visual.outlineStyle};`)\n } else if (hasVisibleOutlineWidth) {\n decls.push(\"outline-style: solid;\")\n }\n if (visual.outlineWidth !== undefined) {\n decls.push(`outline-width: ${px(visual.outlineWidth)};`)\n }\n if (visual.outlineColor !== undefined) {\n pushColor(decls, \"outline-color\", visual.outlineColor, \"outlineColor\")\n }\n if (visual.outlineOffset !== undefined) {\n decls.push(`outline-offset: ${px(visual.outlineOffset)};`)\n }\n\n if (visual.color !== undefined) {\n pushColor(decls, \"color\", visual.color, \"color\")\n }\n if (visual.fontFamily !== undefined) {\n decls.push(`font-family: ${formatFontFamily(visual.fontFamily)};`)\n }\n if (visual.fontSize !== undefined) {\n decls.push(`font-size: ${px(visual.fontSize)};`)\n }\n if (visual.fontStyle !== undefined) {\n decls.push(`font-style: ${visual.fontStyle};`)\n }\n if (visual.fontWeight !== undefined) {\n decls.push(`font-weight: ${visual.fontWeight};`)\n }\n if (visual.letterSpacing !== undefined) {\n decls.push(`letter-spacing: ${px(visual.letterSpacing)};`)\n }\n if (visual.lineHeight !== undefined) {\n decls.push(`line-height: ${px(visual.lineHeight)};`)\n }\n\n // visual.textAlign, visual.textDecorationLine, visual.transform:\n // intentionally not CSS (see header).\n\n return decls.join(\"\\n\")\n}\n"]}
@@ -1,9 +1,6 @@
1
1
  // Pure objectBoundingBox / userSpaceOnUse coordinate math for gradients — no
2
- // gtkx imports, so it stays testable even though the bridge's actual
3
- // gradient PAINT is currently blocked by an upstream gtkx-rc2 native bug
4
- // (see the WORKAROUND note by makeColorStop in gtkx/bridge/svg-node.ts).
5
- // This is the part of gradient support that already works end to end today
6
- // and will need no changes once that bug is fixed upstream.
2
+ // gtkx imports, so it is unit-testable off Linux; the bridge
3
+ // (gtkx/bridge/svg-node.ts) feeds the results to Gsk's gradient nodes.
7
4
  export const resolveGradientPoint = (bounds, fractionX, fractionY, units) => units === "userSpaceOnUse"
8
5
  ? { x: fractionX, y: fractionY }
9
6
  : {
@@ -1 +1 @@
1
- {"version":3,"file":"gradient-geometry.js","sourceRoot":"","sources":["../../src/svg/gradient-geometry.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,qEAAqE;AACrE,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,4DAA4D;AAU5D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAmB,EACnB,SAAiB,EACjB,SAAiB,EACjB,KAAoB,EACM,EAAE,CAC5B,KAAK,KAAK,gBAAgB;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE;IAChC,CAAC,CAAC;QACE,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,KAAK;QACtC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM;KACxC,CAAA;AAEP,yEAAyE;AACzE,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,MAAmB,EACnB,CAAS,EACT,KAAoB,EACkB,EAAE,CACxC,KAAK,KAAK,gBAAgB;IACxB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;IAC5B,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA","sourcesContent":["// Pure objectBoundingBox / userSpaceOnUse coordinate math for gradients — no\n// gtkx imports, so it stays testable even though the bridge's actual\n// gradient PAINT is currently blocked by an upstream gtkx-rc2 native bug\n// (see the WORKAROUND note by makeColorStop in gtkx/bridge/svg-node.ts).\n// This is the part of gradient support that already works end to end today\n// and will need no changes once that bug is fixed upstream.\n\nexport type BoundingBox = {\n x: number\n y: number\n width: number\n height: number\n}\nexport type GradientUnits = \"objectBoundingBox\" | \"userSpaceOnUse\"\n\nexport const resolveGradientPoint = (\n bounds: BoundingBox,\n fractionX: number,\n fractionY: number,\n units: GradientUnits,\n): { x: number; y: number } =>\n units === \"userSpaceOnUse\"\n ? { x: fractionX, y: fractionY }\n : {\n x: bounds.x + fractionX * bounds.width,\n y: bounds.y + fractionY * bounds.height,\n }\n\n// SVG only has one radial radius; objectBoundingBox stretches it into an\n// ellipse matching the shape's own aspect ratio (what browsers do too).\nexport const resolveGradientRadius = (\n bounds: BoundingBox,\n r: number,\n units: GradientUnits,\n): { hradius: number; vradius: number } =>\n units === \"userSpaceOnUse\"\n ? { hradius: r, vradius: r }\n : { hradius: r * bounds.width, vradius: r * bounds.height }\n"]}
1
+ {"version":3,"file":"gradient-geometry.js","sourceRoot":"","sources":["../../src/svg/gradient-geometry.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6DAA6D;AAC7D,uEAAuE;AAUvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAmB,EACnB,SAAiB,EACjB,SAAiB,EACjB,KAAoB,EACM,EAAE,CAC5B,KAAK,KAAK,gBAAgB;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE;IAChC,CAAC,CAAC;QACE,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,KAAK;QACtC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM;KACxC,CAAA;AAEP,yEAAyE;AACzE,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,MAAmB,EACnB,CAAS,EACT,KAAoB,EACkB,EAAE,CACxC,KAAK,KAAK,gBAAgB;IACxB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;IAC5B,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA","sourcesContent":["// Pure objectBoundingBox / userSpaceOnUse coordinate math for gradients — no\n// gtkx imports, so it is unit-testable off Linux; the bridge\n// (gtkx/bridge/svg-node.ts) feeds the results to Gsk's gradient nodes.\n\nexport type BoundingBox = {\n x: number\n y: number\n width: number\n height: number\n}\nexport type GradientUnits = \"objectBoundingBox\" | \"userSpaceOnUse\"\n\nexport const resolveGradientPoint = (\n bounds: BoundingBox,\n fractionX: number,\n fractionY: number,\n units: GradientUnits,\n): { x: number; y: number } =>\n units === \"userSpaceOnUse\"\n ? { x: fractionX, y: fractionY }\n : {\n x: bounds.x + fractionX * bounds.width,\n y: bounds.y + fractionY * bounds.height,\n }\n\n// SVG only has one radial radius; objectBoundingBox stretches it into an\n// ellipse matching the shape's own aspect ratio (what browsers do too).\nexport const resolveGradientRadius = (\n bounds: BoundingBox,\n r: number,\n units: GradientUnits,\n): { hradius: number; vradius: number } =>\n units === \"userSpaceOnUse\"\n ? { hradius: r, vradius: r }\n : { hradius: r * bounds.width, vradius: r * bounds.height }\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAOlD,2EAA2E;AAC3E,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,iDAAiD;AACjD,cAAc,eAAe,CAAA;AA2B7B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmC,EACnC,OAAkC;IAIlC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,QAAoC,EACpC,OAAmC,CACpC,CAAA;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAA;AAC5B,CAAC","sourcesContent":["import { render, renderHook } from \"@gtkx/testing\"\nimport type {\n RenderHookOptions,\n RenderHookResult,\n RenderResult,\n} from \"@gtkx/testing\"\n\n// react-native-gtkx/testing — @gtkx/testing's queries, render, renderHook,\n// fireEvent and userEvent already operate on the real GTK widget tree our\n// RN components render into, so `getByText` on a `Text` or `userEvent.click`\n// on a `Pressable` already work with no RN-flavoured wrapper: this subpath\n// re-exports that surface as-is, one import source for a consumer, plus the\n// one genuinely repeated workaround (see below).\nexport * from \"@gtkx/testing\"\n\n// RC2-WORKAROUND(renderhook-no-window): see docs/gtkx-rc2-notes.md\n// @gtkx/testing's renderHook() always mounts its test component into a bare\n// Gtk.Box, so a hook that reads the active toplevel (useWindowDimensions,\n// anything through Gtk.Window.getToplevels()) has nothing to read. render()\n// always creates and presents a harness window when no container is given,\n// regardless of what it renders — rendering null is enough to get one.\n\n/**\n * Like `renderHook`, but first creates and presents a harness window (via\n * `render(null)`), so window-dependent hooks have a toplevel to read.\n * Cleanup is automatic — @gtkx/testing tears down every active render\n * (including the harness window) after each test.\n */\nexport function renderHookWithWindow<Result>(\n callback: () => Result,\n options?: RenderHookOptions<undefined>,\n): Promise<\n RenderHookResult<Result, undefined> & { window: RenderResult[\"container\"] }\n>\nexport function renderHookWithWindow<Result, Props>(\n callback: (props: Props) => Result,\n options: RenderHookOptions<Props>,\n): Promise<\n RenderHookResult<Result, Props> & { window: RenderResult[\"container\"] }\n>\nexport async function renderHookWithWindow<Result, Props>(\n callback: (props?: Props) => Result,\n options?: RenderHookOptions<Props>,\n): Promise<\n RenderHookResult<Result, Props> & { window: RenderResult[\"container\"] }\n> {\n const { container: window } = await render(null)\n const hook = await renderHook(\n callback as (props: Props) => Result,\n options as RenderHookOptions<Props>,\n )\n return { ...hook, window }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAOlD,2EAA2E;AAC3E,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,iDAAiD;AACjD,cAAc,eAAe,CAAA;AA2B7B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmC,EACnC,OAAkC;IAIlC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,QAAoC,EACpC,OAAmC,CACpC,CAAA;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAA;AAC5B,CAAC","sourcesContent":["import { render, renderHook } from \"@gtkx/testing\"\nimport type {\n RenderHookOptions,\n RenderHookResult,\n RenderResult,\n} from \"@gtkx/testing\"\n\n// react-native-gtkx/testing — @gtkx/testing's queries, render, renderHook,\n// fireEvent and userEvent already operate on the real GTK widget tree our\n// RN components render into, so `getByText` on a `Text` or `userEvent.click`\n// on a `Pressable` already work with no RN-flavoured wrapper: this subpath\n// re-exports that surface as-is, one import source for a consumer, plus the\n// one genuinely repeated workaround (see below).\nexport * from \"@gtkx/testing\"\n\n// RC3-WORKAROUND(renderhook-no-window): see docs/gtkx-rc3-notes.md\n// @gtkx/testing's renderHook() always mounts its test component into a bare\n// Gtk.Box, so a hook that reads the active toplevel (useWindowDimensions,\n// anything through Gtk.Window.getToplevels()) has nothing to read. render()\n// always creates and presents a harness window when no container is given,\n// regardless of what it renders — rendering null is enough to get one.\n\n/**\n * Like `renderHook`, but first creates and presents a harness window (via\n * `render(null)`), so window-dependent hooks have a toplevel to read.\n * Cleanup is automatic — @gtkx/testing tears down every active render\n * (including the harness window) after each test.\n */\nexport function renderHookWithWindow<Result>(\n callback: () => Result,\n options?: RenderHookOptions<undefined>,\n): Promise<\n RenderHookResult<Result, undefined> & { window: RenderResult[\"container\"] }\n>\nexport function renderHookWithWindow<Result, Props>(\n callback: (props: Props) => Result,\n options: RenderHookOptions<Props>,\n): Promise<\n RenderHookResult<Result, Props> & { window: RenderResult[\"container\"] }\n>\nexport async function renderHookWithWindow<Result, Props>(\n callback: (props?: Props) => Result,\n options?: RenderHookOptions<Props>,\n): Promise<\n RenderHookResult<Result, Props> & { window: RenderResult[\"container\"] }\n> {\n const { container: window } = await render(null)\n const hook = await renderHook(\n callback as (props: Props) => Result,\n options as RenderHookOptions<Props>,\n )\n return { ...hook, window }\n}\n"]}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { GestureResponderEvent } from "../../responder/types";
8
+ export type PanResponderGestureState = {
9
+ /** ID of the gestureState - persisted as long as there's a touch on screen */
10
+ stateID: number;
11
+ /** the latest screen coordinates of the recently-moved touch */
12
+ moveX: number;
13
+ /** the latest screen coordinates of the recently-moved touch */
14
+ moveY: number;
15
+ /** the screen coordinates of the responder grant */
16
+ x0: number;
17
+ /** the screen coordinates of the responder grant */
18
+ y0: number;
19
+ /** accumulated distance of the gesture since the touch started */
20
+ dx: number;
21
+ /** accumulated distance of the gesture since the touch started */
22
+ dy: number;
23
+ /** current velocity of the gesture */
24
+ vx: number;
25
+ /** current velocity of the gesture */
26
+ vy: number;
27
+ /** Number of touches currently on screen */
28
+ numberActiveTouches: number;
29
+ /** All `gestureState` accounts for timeStamps up until this value */
30
+ _accountsForMovesUpTo: number;
31
+ };
32
+ type ActiveCallback = (event: GestureResponderEvent, gestureState: PanResponderGestureState) => boolean;
33
+ type PassiveCallback = (event: GestureResponderEvent, gestureState: PanResponderGestureState) => unknown;
34
+ export type GestureResponderHandlerMethods = {
35
+ onMoveShouldSetResponder: (event: GestureResponderEvent) => boolean;
36
+ onMoveShouldSetResponderCapture: (event: GestureResponderEvent) => boolean;
37
+ onResponderEnd: (event: GestureResponderEvent) => void;
38
+ onResponderGrant: (event: GestureResponderEvent) => boolean;
39
+ onResponderMove: (event: GestureResponderEvent) => void;
40
+ onResponderReject: (event: GestureResponderEvent) => void;
41
+ onResponderRelease: (event: GestureResponderEvent) => void;
42
+ onResponderStart: (event: GestureResponderEvent) => void;
43
+ onResponderTerminate: (event: GestureResponderEvent) => void;
44
+ onResponderTerminationRequest: (event: GestureResponderEvent) => boolean;
45
+ onStartShouldSetResponder: (event: GestureResponderEvent) => boolean;
46
+ onStartShouldSetResponderCapture: (event: GestureResponderEvent) => boolean;
47
+ };
48
+ export type PanResponderCallbacks = Readonly<{
49
+ onMoveShouldSetPanResponder?: ActiveCallback;
50
+ onMoveShouldSetPanResponderCapture?: ActiveCallback;
51
+ onStartShouldSetPanResponder?: ActiveCallback;
52
+ onStartShouldSetPanResponderCapture?: ActiveCallback;
53
+ onPanResponderGrant?: PassiveCallback;
54
+ onPanResponderMove?: PassiveCallback;
55
+ onPanResponderRelease?: PassiveCallback;
56
+ onPanResponderTerminate?: PassiveCallback;
57
+ onPanResponderReject?: PassiveCallback;
58
+ onPanResponderStart?: PassiveCallback;
59
+ onPanResponderEnd?: PassiveCallback;
60
+ onPanResponderTerminationRequest?: ActiveCallback;
61
+ onShouldBlockNativeResponder?: ActiveCallback;
62
+ }>;
63
+ declare const PanResponder: {
64
+ _initializeGestureState(gestureState: PanResponderGestureState): void;
65
+ /**
66
+ * This is nuanced and is necessary. It is incorrect to continuously take all
67
+ * active *and* recently moved touches, find the centroid, and track how that
68
+ * result changes over time. Instead, we must take all recently moved
69
+ * touches, and calculate how the centroid has changed just for those
70
+ * recently moved touches, and append that change to an accumulator. This is
71
+ * to (at least) handle the case where the user is moving three fingers, and
72
+ * then one of the fingers stops but the other two continue.
73
+ *
74
+ * This is very different than taking all of the recently moved touches and
75
+ * storing their centroid as `dx/dy`. For correctness, we must *accumulate
76
+ * changes* in the centroid of recently moved touches.
77
+ *
78
+ * There is also some nuance with how we handle multiple moved touches in a
79
+ * single event. With the way `ReactNativeEventEmitter` dispatches touches as
80
+ * individual events, multiple touches generate two 'move' events, each of
81
+ * them triggering `onResponderMove`. But with the way `PanResponder` works,
82
+ * all of the gesture inference is performed on the first dispatch, since it
83
+ * looks at all of the touches (even the ones for which there hasn't been a
84
+ * native dispatch yet). Therefore, `PanResponder` does not call
85
+ * `onResponderMove` passed the first dispatch. This diverges from the
86
+ * typical responder callback pattern (without using `PanResponder`), but
87
+ * avoids more dispatches than necessary.
88
+ */
89
+ _updateGestureStateOnMove(gestureState: PanResponderGestureState, touchHistory: GestureResponderEvent["touchHistory"]): void;
90
+ /**
91
+ * @param config Enhanced versions of all of the responder callbacks that
92
+ * provide not only the typical `ResponderSyntheticEvent`, but also the
93
+ * `PanResponder` gesture state. Simply replace the word `Responder` with
94
+ * `PanResponder` in each of the typical `onResponder*` callbacks.
95
+ */
96
+ create(config: PanResponderCallbacks): {
97
+ getInteractionHandle: () => number | null;
98
+ panHandlers: GestureResponderHandlerMethods;
99
+ };
100
+ };
101
+ export type PanResponderInstance = ReturnType<(typeof PanResponder)["create"]>;
102
+ export default PanResponder;
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import TouchHistoryMath from "./touch-history-math";
8
+ const currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
9
+ const currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
10
+ const previousCentroidXOfTouchesChangedAfter = TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
11
+ const previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
12
+ const currentCentroidX = TouchHistoryMath.currentCentroidX;
13
+ const currentCentroidY = TouchHistoryMath.currentCentroidY;
14
+ const PanResponder = {
15
+ _initializeGestureState(gestureState) {
16
+ gestureState.moveX = 0;
17
+ gestureState.moveY = 0;
18
+ gestureState.x0 = 0;
19
+ gestureState.y0 = 0;
20
+ gestureState.dx = 0;
21
+ gestureState.dy = 0;
22
+ gestureState.vx = 0;
23
+ gestureState.vy = 0;
24
+ gestureState.numberActiveTouches = 0;
25
+ // All `gestureState` accounts for timeStamps up until:
26
+ gestureState._accountsForMovesUpTo = 0;
27
+ },
28
+ /**
29
+ * This is nuanced and is necessary. It is incorrect to continuously take all
30
+ * active *and* recently moved touches, find the centroid, and track how that
31
+ * result changes over time. Instead, we must take all recently moved
32
+ * touches, and calculate how the centroid has changed just for those
33
+ * recently moved touches, and append that change to an accumulator. This is
34
+ * to (at least) handle the case where the user is moving three fingers, and
35
+ * then one of the fingers stops but the other two continue.
36
+ *
37
+ * This is very different than taking all of the recently moved touches and
38
+ * storing their centroid as `dx/dy`. For correctness, we must *accumulate
39
+ * changes* in the centroid of recently moved touches.
40
+ *
41
+ * There is also some nuance with how we handle multiple moved touches in a
42
+ * single event. With the way `ReactNativeEventEmitter` dispatches touches as
43
+ * individual events, multiple touches generate two 'move' events, each of
44
+ * them triggering `onResponderMove`. But with the way `PanResponder` works,
45
+ * all of the gesture inference is performed on the first dispatch, since it
46
+ * looks at all of the touches (even the ones for which there hasn't been a
47
+ * native dispatch yet). Therefore, `PanResponder` does not call
48
+ * `onResponderMove` passed the first dispatch. This diverges from the
49
+ * typical responder callback pattern (without using `PanResponder`), but
50
+ * avoids more dispatches than necessary.
51
+ */
52
+ _updateGestureStateOnMove(gestureState, touchHistory) {
53
+ gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
54
+ gestureState.moveX = currentCentroidXOfTouchesChangedAfter(touchHistory, gestureState._accountsForMovesUpTo);
55
+ gestureState.moveY = currentCentroidYOfTouchesChangedAfter(touchHistory, gestureState._accountsForMovesUpTo);
56
+ const movedAfter = gestureState._accountsForMovesUpTo;
57
+ const prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
58
+ const x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
59
+ const prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
60
+ const y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
61
+ const nextDX = gestureState.dx + (x - prevX);
62
+ const nextDY = gestureState.dy + (y - prevY);
63
+ // TODO: This must be filtered intelligently.
64
+ const dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
65
+ gestureState.vx = (nextDX - gestureState.dx) / dt;
66
+ gestureState.vy = (nextDY - gestureState.dy) / dt;
67
+ gestureState.dx = nextDX;
68
+ gestureState.dy = nextDY;
69
+ gestureState._accountsForMovesUpTo = touchHistory.mostRecentTimeStamp;
70
+ },
71
+ /**
72
+ * @param config Enhanced versions of all of the responder callbacks that
73
+ * provide not only the typical `ResponderSyntheticEvent`, but also the
74
+ * `PanResponder` gesture state. Simply replace the word `Responder` with
75
+ * `PanResponder` in each of the typical `onResponder*` callbacks.
76
+ */
77
+ create(config) {
78
+ const gestureState = {
79
+ // Useful for debugging
80
+ stateID: Math.random(),
81
+ moveX: 0,
82
+ moveY: 0,
83
+ x0: 0,
84
+ y0: 0,
85
+ dx: 0,
86
+ dy: 0,
87
+ vx: 0,
88
+ vy: 0,
89
+ numberActiveTouches: 0,
90
+ _accountsForMovesUpTo: 0,
91
+ };
92
+ const panHandlers = {
93
+ onStartShouldSetResponder(event) {
94
+ return config.onStartShouldSetPanResponder == null
95
+ ? false
96
+ : config.onStartShouldSetPanResponder(event, gestureState);
97
+ },
98
+ onMoveShouldSetResponder(event) {
99
+ return config.onMoveShouldSetPanResponder == null
100
+ ? false
101
+ : config.onMoveShouldSetPanResponder(event, gestureState);
102
+ },
103
+ onStartShouldSetResponderCapture(event) {
104
+ // TODO: Actually, we should reinitialize the state any time
105
+ // touches.length increases from 0 active to > 0 active.
106
+ if (event.nativeEvent.touches.length === 1) {
107
+ PanResponder._initializeGestureState(gestureState);
108
+ }
109
+ gestureState.numberActiveTouches =
110
+ event.touchHistory.numberActiveTouches;
111
+ return config.onStartShouldSetPanResponderCapture != null
112
+ ? config.onStartShouldSetPanResponderCapture(event, gestureState)
113
+ : false;
114
+ },
115
+ onMoveShouldSetResponderCapture(event) {
116
+ const touchHistory = event.touchHistory;
117
+ // Responder system incorrectly dispatches should* to current responder
118
+ // Filter out any touch moves past the first one - we would have
119
+ // already processed multi-touch geometry during the first event.
120
+ if (gestureState._accountsForMovesUpTo ===
121
+ touchHistory.mostRecentTimeStamp) {
122
+ return false;
123
+ }
124
+ PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
125
+ return config.onMoveShouldSetPanResponderCapture
126
+ ? config.onMoveShouldSetPanResponderCapture(event, gestureState)
127
+ : false;
128
+ },
129
+ onResponderGrant(event) {
130
+ gestureState.x0 = currentCentroidX(event.touchHistory);
131
+ gestureState.y0 = currentCentroidY(event.touchHistory);
132
+ gestureState.dx = 0;
133
+ gestureState.dy = 0;
134
+ if (config.onPanResponderGrant) {
135
+ config.onPanResponderGrant(event, gestureState);
136
+ }
137
+ // TODO: t7467124 investigate if this can be removed
138
+ return config.onShouldBlockNativeResponder == null
139
+ ? true
140
+ : config.onShouldBlockNativeResponder(event, gestureState);
141
+ },
142
+ onResponderReject(event) {
143
+ config.onPanResponderReject?.call(undefined, event, gestureState);
144
+ },
145
+ onResponderRelease(event) {
146
+ config.onPanResponderRelease?.call(undefined, event, gestureState);
147
+ PanResponder._initializeGestureState(gestureState);
148
+ },
149
+ onResponderStart(event) {
150
+ const touchHistory = event.touchHistory;
151
+ gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
152
+ if (config.onPanResponderStart) {
153
+ config.onPanResponderStart(event, gestureState);
154
+ }
155
+ },
156
+ onResponderMove(event) {
157
+ const touchHistory = event.touchHistory;
158
+ // Guard against the dispatch of two touch moves when there are two
159
+ // simultaneously changed touches.
160
+ if (gestureState._accountsForMovesUpTo ===
161
+ touchHistory.mostRecentTimeStamp) {
162
+ return;
163
+ }
164
+ // Filter out any touch moves past the first one - we would have
165
+ // already processed multi-touch geometry during the first event.
166
+ PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
167
+ if (config.onPanResponderMove) {
168
+ config.onPanResponderMove(event, gestureState);
169
+ }
170
+ },
171
+ onResponderEnd(event) {
172
+ const touchHistory = event.touchHistory;
173
+ gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
174
+ config.onPanResponderEnd?.call(undefined, event, gestureState);
175
+ },
176
+ onResponderTerminate(event) {
177
+ config.onPanResponderTerminate?.call(undefined, event, gestureState);
178
+ PanResponder._initializeGestureState(gestureState);
179
+ },
180
+ onResponderTerminationRequest(event) {
181
+ return config.onPanResponderTerminationRequest == null
182
+ ? true
183
+ : config.onPanResponderTerminationRequest(event, gestureState);
184
+ },
185
+ };
186
+ return {
187
+ panHandlers,
188
+ getInteractionHandle() {
189
+ // TODO: Deprecate and delete this method.
190
+ return null;
191
+ },
192
+ };
193
+ },
194
+ };
195
+ export default PanResponder;
196
+ //# sourceMappingURL=pan-responder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pan-responder.js","sourceRoot":"","sources":["../../../src/vendor/react-native/pan-responder.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// VENDORED from react-native (MIT), Libraries/Interaction/PanResponder.js.\n//\n// react-native-web re-exports Meta's file directly and it works untouched,\n// because RNW reproduces the touchHistory structure PanResponder expects. We\n// reproduce the same structure (src/responder/touch-history.ts), so the\n// algorithm below is upstream's, unmodified in substance.\n//\n// We cannot re-export it the way RNW does: upstream ships Flow, and this\n// package is one tsc emit. So it is transliterated, and ONLY transliterated:\n// - Flow annotations to TypeScript (`?number` to `number | null`,\n// inexact object types to plain ones, `Readonly<{}>` kept as-is);\n// - `GestureResponderEvent` imported from our responder types instead of\n// RN's CoreEventTypes;\n// - `require` to `import`.\n//\n// The logic is deliberately untouched, comments included — especially the\n// clustered multi-touch accumulation in _updateGestureStateOnMove, which is\n// the part with no obvious specification and the reason vendoring beats\n// reimplementing.\nimport type { GestureResponderEvent } from \"../../responder/types\"\nimport TouchHistoryMath from \"./touch-history-math\"\n\nconst currentCentroidXOfTouchesChangedAfter =\n TouchHistoryMath.currentCentroidXOfTouchesChangedAfter\nconst currentCentroidYOfTouchesChangedAfter =\n TouchHistoryMath.currentCentroidYOfTouchesChangedAfter\nconst previousCentroidXOfTouchesChangedAfter =\n TouchHistoryMath.previousCentroidXOfTouchesChangedAfter\nconst previousCentroidYOfTouchesChangedAfter =\n TouchHistoryMath.previousCentroidYOfTouchesChangedAfter\nconst currentCentroidX = TouchHistoryMath.currentCentroidX\nconst currentCentroidY = TouchHistoryMath.currentCentroidY\n\nexport type PanResponderGestureState = {\n /** ID of the gestureState - persisted as long as there's a touch on screen */\n stateID: number\n /** the latest screen coordinates of the recently-moved touch */\n moveX: number\n /** the latest screen coordinates of the recently-moved touch */\n moveY: number\n /** the screen coordinates of the responder grant */\n x0: number\n /** the screen coordinates of the responder grant */\n y0: number\n /** accumulated distance of the gesture since the touch started */\n dx: number\n /** accumulated distance of the gesture since the touch started */\n dy: number\n /** current velocity of the gesture */\n vx: number\n /** current velocity of the gesture */\n vy: number\n /** Number of touches currently on screen */\n numberActiveTouches: number\n /** All `gestureState` accounts for timeStamps up until this value */\n _accountsForMovesUpTo: number\n}\n\ntype ActiveCallback = (\n event: GestureResponderEvent,\n gestureState: PanResponderGestureState,\n) => boolean\n\ntype PassiveCallback = (\n event: GestureResponderEvent,\n gestureState: PanResponderGestureState,\n) => unknown\n\nexport type GestureResponderHandlerMethods = {\n onMoveShouldSetResponder: (event: GestureResponderEvent) => boolean\n onMoveShouldSetResponderCapture: (event: GestureResponderEvent) => boolean\n onResponderEnd: (event: GestureResponderEvent) => void\n onResponderGrant: (event: GestureResponderEvent) => boolean\n onResponderMove: (event: GestureResponderEvent) => void\n onResponderReject: (event: GestureResponderEvent) => void\n onResponderRelease: (event: GestureResponderEvent) => void\n onResponderStart: (event: GestureResponderEvent) => void\n onResponderTerminate: (event: GestureResponderEvent) => void\n onResponderTerminationRequest: (event: GestureResponderEvent) => boolean\n onStartShouldSetResponder: (event: GestureResponderEvent) => boolean\n onStartShouldSetResponderCapture: (event: GestureResponderEvent) => boolean\n}\n\nexport type PanResponderCallbacks = Readonly<{\n onMoveShouldSetPanResponder?: ActiveCallback\n onMoveShouldSetPanResponderCapture?: ActiveCallback\n onStartShouldSetPanResponder?: ActiveCallback\n onStartShouldSetPanResponderCapture?: ActiveCallback\n onPanResponderGrant?: PassiveCallback\n onPanResponderMove?: PassiveCallback\n onPanResponderRelease?: PassiveCallback\n onPanResponderTerminate?: PassiveCallback\n onPanResponderReject?: PassiveCallback\n onPanResponderStart?: PassiveCallback\n onPanResponderEnd?: PassiveCallback\n onPanResponderTerminationRequest?: ActiveCallback\n onShouldBlockNativeResponder?: ActiveCallback\n}>\n\nconst PanResponder = {\n _initializeGestureState(gestureState: PanResponderGestureState): void {\n gestureState.moveX = 0\n gestureState.moveY = 0\n gestureState.x0 = 0\n gestureState.y0 = 0\n gestureState.dx = 0\n gestureState.dy = 0\n gestureState.vx = 0\n gestureState.vy = 0\n gestureState.numberActiveTouches = 0\n // All `gestureState` accounts for timeStamps up until:\n gestureState._accountsForMovesUpTo = 0\n },\n\n /**\n * This is nuanced and is necessary. It is incorrect to continuously take all\n * active *and* recently moved touches, find the centroid, and track how that\n * result changes over time. Instead, we must take all recently moved\n * touches, and calculate how the centroid has changed just for those\n * recently moved touches, and append that change to an accumulator. This is\n * to (at least) handle the case where the user is moving three fingers, and\n * then one of the fingers stops but the other two continue.\n *\n * This is very different than taking all of the recently moved touches and\n * storing their centroid as `dx/dy`. For correctness, we must *accumulate\n * changes* in the centroid of recently moved touches.\n *\n * There is also some nuance with how we handle multiple moved touches in a\n * single event. With the way `ReactNativeEventEmitter` dispatches touches as\n * individual events, multiple touches generate two 'move' events, each of\n * them triggering `onResponderMove`. But with the way `PanResponder` works,\n * all of the gesture inference is performed on the first dispatch, since it\n * looks at all of the touches (even the ones for which there hasn't been a\n * native dispatch yet). Therefore, `PanResponder` does not call\n * `onResponderMove` passed the first dispatch. This diverges from the\n * typical responder callback pattern (without using `PanResponder`), but\n * avoids more dispatches than necessary.\n */\n _updateGestureStateOnMove(\n gestureState: PanResponderGestureState,\n touchHistory: GestureResponderEvent[\"touchHistory\"],\n ): void {\n gestureState.numberActiveTouches = touchHistory.numberActiveTouches\n gestureState.moveX = currentCentroidXOfTouchesChangedAfter(\n touchHistory,\n gestureState._accountsForMovesUpTo,\n )\n gestureState.moveY = currentCentroidYOfTouchesChangedAfter(\n touchHistory,\n gestureState._accountsForMovesUpTo,\n )\n const movedAfter = gestureState._accountsForMovesUpTo\n const prevX = previousCentroidXOfTouchesChangedAfter(\n touchHistory,\n movedAfter,\n )\n const x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter)\n const prevY = previousCentroidYOfTouchesChangedAfter(\n touchHistory,\n movedAfter,\n )\n const y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter)\n const nextDX = gestureState.dx + (x - prevX)\n const nextDY = gestureState.dy + (y - prevY)\n\n // TODO: This must be filtered intelligently.\n const dt =\n touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo\n gestureState.vx = (nextDX - gestureState.dx) / dt\n gestureState.vy = (nextDY - gestureState.dy) / dt\n\n gestureState.dx = nextDX\n gestureState.dy = nextDY\n gestureState._accountsForMovesUpTo = touchHistory.mostRecentTimeStamp\n },\n\n /**\n * @param config Enhanced versions of all of the responder callbacks that\n * provide not only the typical `ResponderSyntheticEvent`, but also the\n * `PanResponder` gesture state. Simply replace the word `Responder` with\n * `PanResponder` in each of the typical `onResponder*` callbacks.\n */\n create(config: PanResponderCallbacks): {\n getInteractionHandle: () => number | null\n panHandlers: GestureResponderHandlerMethods\n } {\n const gestureState: PanResponderGestureState = {\n // Useful for debugging\n stateID: Math.random(),\n moveX: 0,\n moveY: 0,\n x0: 0,\n y0: 0,\n dx: 0,\n dy: 0,\n vx: 0,\n vy: 0,\n numberActiveTouches: 0,\n _accountsForMovesUpTo: 0,\n }\n const panHandlers: GestureResponderHandlerMethods = {\n onStartShouldSetResponder(event: GestureResponderEvent): boolean {\n return config.onStartShouldSetPanResponder == null\n ? false\n : config.onStartShouldSetPanResponder(event, gestureState)\n },\n onMoveShouldSetResponder(event: GestureResponderEvent): boolean {\n return config.onMoveShouldSetPanResponder == null\n ? false\n : config.onMoveShouldSetPanResponder(event, gestureState)\n },\n onStartShouldSetResponderCapture(event: GestureResponderEvent): boolean {\n // TODO: Actually, we should reinitialize the state any time\n // touches.length increases from 0 active to > 0 active.\n if (event.nativeEvent.touches.length === 1) {\n PanResponder._initializeGestureState(gestureState)\n }\n gestureState.numberActiveTouches =\n event.touchHistory.numberActiveTouches\n return config.onStartShouldSetPanResponderCapture != null\n ? config.onStartShouldSetPanResponderCapture(event, gestureState)\n : false\n },\n\n onMoveShouldSetResponderCapture(event: GestureResponderEvent): boolean {\n const touchHistory = event.touchHistory\n // Responder system incorrectly dispatches should* to current responder\n // Filter out any touch moves past the first one - we would have\n // already processed multi-touch geometry during the first event.\n if (\n gestureState._accountsForMovesUpTo ===\n touchHistory.mostRecentTimeStamp\n ) {\n return false\n }\n PanResponder._updateGestureStateOnMove(gestureState, touchHistory)\n return config.onMoveShouldSetPanResponderCapture\n ? config.onMoveShouldSetPanResponderCapture(event, gestureState)\n : false\n },\n\n onResponderGrant(event: GestureResponderEvent): boolean {\n gestureState.x0 = currentCentroidX(event.touchHistory)\n gestureState.y0 = currentCentroidY(event.touchHistory)\n gestureState.dx = 0\n gestureState.dy = 0\n if (config.onPanResponderGrant) {\n config.onPanResponderGrant(event, gestureState)\n }\n // TODO: t7467124 investigate if this can be removed\n return config.onShouldBlockNativeResponder == null\n ? true\n : config.onShouldBlockNativeResponder(event, gestureState)\n },\n\n onResponderReject(event: GestureResponderEvent): void {\n config.onPanResponderReject?.call(undefined, event, gestureState)\n },\n\n onResponderRelease(event: GestureResponderEvent): void {\n config.onPanResponderRelease?.call(undefined, event, gestureState)\n PanResponder._initializeGestureState(gestureState)\n },\n\n onResponderStart(event: GestureResponderEvent): void {\n const touchHistory = event.touchHistory\n gestureState.numberActiveTouches = touchHistory.numberActiveTouches\n if (config.onPanResponderStart) {\n config.onPanResponderStart(event, gestureState)\n }\n },\n\n onResponderMove(event: GestureResponderEvent): void {\n const touchHistory = event.touchHistory\n // Guard against the dispatch of two touch moves when there are two\n // simultaneously changed touches.\n if (\n gestureState._accountsForMovesUpTo ===\n touchHistory.mostRecentTimeStamp\n ) {\n return\n }\n // Filter out any touch moves past the first one - we would have\n // already processed multi-touch geometry during the first event.\n PanResponder._updateGestureStateOnMove(gestureState, touchHistory)\n if (config.onPanResponderMove) {\n config.onPanResponderMove(event, gestureState)\n }\n },\n\n onResponderEnd(event: GestureResponderEvent): void {\n const touchHistory = event.touchHistory\n gestureState.numberActiveTouches = touchHistory.numberActiveTouches\n config.onPanResponderEnd?.call(undefined, event, gestureState)\n },\n\n onResponderTerminate(event: GestureResponderEvent): void {\n config.onPanResponderTerminate?.call(undefined, event, gestureState)\n PanResponder._initializeGestureState(gestureState)\n },\n\n onResponderTerminationRequest(event: GestureResponderEvent): boolean {\n return config.onPanResponderTerminationRequest == null\n ? true\n : config.onPanResponderTerminationRequest(event, gestureState)\n },\n }\n return {\n panHandlers,\n getInteractionHandle(): number | null {\n // TODO: Deprecate and delete this method.\n return null\n },\n }\n },\n}\n\nexport type PanResponderInstance = ReturnType<(typeof PanResponder)[\"create\"]>\n\nexport default PanResponder\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { TouchHistory } from "../../responder/types";
8
+ declare const TouchHistoryMath: {
9
+ centroidDimension: (touchHistory: TouchHistory, touchesChangedAfter: number, isXAxis: boolean, ofCurrent: boolean) => number;
10
+ currentCentroidXOfTouchesChangedAfter(touchHistory: TouchHistory, touchesChangedAfter: number): number;
11
+ currentCentroidYOfTouchesChangedAfter(touchHistory: TouchHistory, touchesChangedAfter: number): number;
12
+ previousCentroidXOfTouchesChangedAfter(touchHistory: TouchHistory, touchesChangedAfter: number): number;
13
+ previousCentroidYOfTouchesChangedAfter(touchHistory: TouchHistory, touchesChangedAfter: number): number;
14
+ currentCentroidX(touchHistory: TouchHistory): number;
15
+ currentCentroidY(touchHistory: TouchHistory): number;
16
+ noCentroid: number;
17
+ };
18
+ export default TouchHistoryMath;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ const noCentroid = -1;
8
+ /**
9
+ * This code is optimized and not intended to look beautiful. This allows
10
+ * computing of touch centroids that have moved after `touchesChangedAfter`
11
+ * timeStamp. You can compute the current centroid involving all touches
12
+ * moves after `touchesChangedAfter`, or you can compute the previous
13
+ * centroid of all touches that were moved after `touchesChangedAfter`.
14
+ *
15
+ * @param touchHistory Standard Responder touch track data.
16
+ * @param touchesChangedAfter timeStamp after which moved touches are
17
+ * considered "actively moving" - not just "active".
18
+ * @param isXAxis Consider `x` dimension vs. `y` dimension.
19
+ * @param ofCurrent Compute current centroid for actively moving touches vs.
20
+ * previous centroid of now actively moving touches.
21
+ * @return value of centroid in specified dimension.
22
+ */
23
+ const centroidDimension = (touchHistory, touchesChangedAfter, isXAxis, ofCurrent) => {
24
+ const touchBank = touchHistory.touchBank;
25
+ let total = 0;
26
+ let count = 0;
27
+ const oneTouchData = touchHistory.numberActiveTouches === 1
28
+ ? touchHistory.touchBank[touchHistory.indexOfSingleActiveTouch]
29
+ : null;
30
+ if (oneTouchData !== null && oneTouchData !== undefined) {
31
+ if (oneTouchData.touchActive &&
32
+ oneTouchData.currentTimeStamp > touchesChangedAfter) {
33
+ total +=
34
+ ofCurrent && isXAxis
35
+ ? oneTouchData.currentPageX
36
+ : ofCurrent && !isXAxis
37
+ ? oneTouchData.currentPageY
38
+ : !ofCurrent && isXAxis
39
+ ? oneTouchData.previousPageX
40
+ : oneTouchData.previousPageY;
41
+ count = 1;
42
+ }
43
+ }
44
+ else {
45
+ for (let i = 0; i < touchBank.length; i++) {
46
+ const touchTrack = touchBank[i];
47
+ if (touchTrack !== null &&
48
+ touchTrack !== undefined &&
49
+ touchTrack.touchActive &&
50
+ touchTrack.currentTimeStamp >= touchesChangedAfter) {
51
+ let toAdd; // Yuck, program temporarily in invalid state.
52
+ if (ofCurrent && isXAxis) {
53
+ toAdd = touchTrack.currentPageX;
54
+ }
55
+ else if (ofCurrent && !isXAxis) {
56
+ toAdd = touchTrack.currentPageY;
57
+ }
58
+ else if (!ofCurrent && isXAxis) {
59
+ toAdd = touchTrack.previousPageX;
60
+ }
61
+ else {
62
+ toAdd = touchTrack.previousPageY;
63
+ }
64
+ total += toAdd;
65
+ count++;
66
+ }
67
+ }
68
+ }
69
+ return count > 0 ? total / count : noCentroid;
70
+ };
71
+ const TouchHistoryMath = {
72
+ centroidDimension,
73
+ currentCentroidXOfTouchesChangedAfter(touchHistory, touchesChangedAfter) {
74
+ return centroidDimension(touchHistory, touchesChangedAfter, true, // isXAxis
75
+ true);
76
+ },
77
+ currentCentroidYOfTouchesChangedAfter(touchHistory, touchesChangedAfter) {
78
+ return centroidDimension(touchHistory, touchesChangedAfter, false, // isXAxis
79
+ true);
80
+ },
81
+ previousCentroidXOfTouchesChangedAfter(touchHistory, touchesChangedAfter) {
82
+ return centroidDimension(touchHistory, touchesChangedAfter, true, // isXAxis
83
+ false);
84
+ },
85
+ previousCentroidYOfTouchesChangedAfter(touchHistory, touchesChangedAfter) {
86
+ return centroidDimension(touchHistory, touchesChangedAfter, false, // isXAxis
87
+ false);
88
+ },
89
+ currentCentroidX(touchHistory) {
90
+ return centroidDimension(touchHistory, 0, // touchesChangedAfter
91
+ true, // isXAxis
92
+ true);
93
+ },
94
+ currentCentroidY(touchHistory) {
95
+ return centroidDimension(touchHistory, 0, // touchesChangedAfter
96
+ false, // isXAxis
97
+ true);
98
+ },
99
+ noCentroid,
100
+ };
101
+ export default TouchHistoryMath;
102
+ //# sourceMappingURL=touch-history-math.js.map