@estiva-app/ui 0.73.0-inline-text.1 → 0.73.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estiva-app/ui",
3
- "version": "0.73.0-inline-text.1",
3
+ "version": "0.73.0",
4
4
  "description": "Estiva's design tokens (the contract) and a small set of primitives (a convenience) for every Estiva app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/registry.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "kind": "package",
5
5
  "repo": "estiva-ui",
6
6
  "package": "@estiva-app/ui",
7
- "packageVersion": "0.73.0-inline-text.1",
7
+ "packageVersion": "0.73.0",
8
8
  "exports": 178,
9
9
  "typeExports": 164,
10
10
  "files": null
@@ -168,6 +168,27 @@ describe('InlineText: a refused save, and a change from elsewhere', () => {
168
168
  expect(field().textContent).toBe('Item one two')
169
169
  })
170
170
 
171
+ it('Enter and the leave that follows it save once while the save is still in flight', async () => {
172
+ let answer: (saved: boolean) => void = () => {}
173
+ const onCommit = vi.fn(() => new Promise<boolean>((resolve) => (answer = resolve)))
174
+ draw({ onCommit, onEnter: () => field().blur() })
175
+ await enter()
176
+ await type(' two')
177
+ await userEvent.keyboard('{Enter}')
178
+ await act(async () => answer(true))
179
+ expect(onCommit).toHaveBeenCalledTimes(1)
180
+ })
181
+
182
+ it('a change from elsewhere made while you are in it, and you change nothing, is loaded when you leave', async () => {
183
+ const { rerender, onCommit } = draw()
184
+ await enter()
185
+ rerender({ value: 'Item three' })
186
+ expect(field().textContent).toBe('Item one')
187
+ await act(async () => field().blur())
188
+ expect(field().textContent).toBe('Item three')
189
+ expect(onCommit).not.toHaveBeenCalled()
190
+ })
191
+
171
192
  it('a change from elsewhere is loaded while you are not in it', async () => {
172
193
  const { rerender } = draw()
173
194
  rerender({ value: 'Item three' })
@@ -98,14 +98,22 @@ export function InlineText({ value, placeholder, label, onCommit, onText, onEnte
98
98
  const [empty, setEmpty] = useState(!value)
99
99
  // Read at key time: the editor's handlers are built once, and a prop they
100
100
  // closed over would be the first render's.
101
- const latest = useRef({ onEnter, onText, onCommit, clearable })
102
- latest.current = { onEnter, onText, onCommit, clearable }
101
+ const latest = useRef({ onEnter, onText, onCommit, clearable, value })
102
+ latest.current = { onEnter, onText, onCommit, clearable, value }
103
+ // The text being saved right now: Enter moves on, and the leave that follows
104
+ // must not send the same save a second time while the first is in flight.
105
+ const pending = useRef<string | null>(null)
103
106
 
104
107
  const save = useCallback(async (text: string) => {
105
108
  const next = oneLine(text)
106
- if (next === saved.current) return
109
+ if (next === saved.current || next === pending.current) return
107
110
  if (!next && !latest.current.clearable) return
108
- if (await latest.current.onCommit(next)) saved.current = next
111
+ pending.current = next
112
+ try {
113
+ if (await latest.current.onCommit(next)) saved.current = next
114
+ } finally {
115
+ if (pending.current === next) pending.current = null
116
+ }
109
117
  }, [])
110
118
 
111
119
  const editor = useEditor({
@@ -156,6 +164,15 @@ export function InlineText({ value, placeholder, label, onCommit, onText, onEnte
156
164
  setEmpty(false)
157
165
  return
158
166
  }
167
+ // Left unchanged while someone else changed it: their text comes in now,
168
+ // as it would have had you not been in it.
169
+ const { value: current } = latest.current
170
+ if (oneLine(e.getText()) === saved.current && current !== saved.current && pending.current === null) {
171
+ e.commands.setContent(doc(current), { emitUpdate: false })
172
+ saved.current = current
173
+ setEmpty(!current)
174
+ return
175
+ }
159
176
  void save(e.getText())
160
177
  },
161
178
  }, [readOnly])