@estiva-app/ui 0.69.0 → 0.69.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estiva-app/ui",
3
- "version": "0.69.0",
3
+ "version": "0.69.2",
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.69.0",
7
+ "packageVersion": "0.69.2",
8
8
  "exports": 162,
9
9
  "typeExports": 152,
10
10
  "files": null
@@ -429,9 +429,15 @@ export function AttachmentCard({
429
429
  return (
430
430
  // `role="status"`: a plain box may not carry a name (axe, aria-prohibited-attr), and a
431
431
  // status says what it is doing — loading — to a reader that cannot see the pulse.
432
+ // The ready picture's shell — its 112px area and the name row under it — so nothing below moves as it
433
+ // arrives (Peek OPT-10: a loading box 34px shorter than the card it became pushed the conversation).
432
434
  // @estiva-escape(no-restyled-part): the loading pulse is AttachmentCard's own state; Card has no loading state (UIG-9, Katerina 17 September)
433
- <Card role="status" fill="inset" aria-busy="true" aria-label={`Loading ${name}`} className={cn('w-[180px] h-28 animate-pulse', className)} {...props}>
434
- {null}
435
+ <Card role="status" fill="inset" aria-busy="true" aria-label={`Loading ${name}`} clip className={cn('flex flex-col w-[180px] animate-pulse', className)} {...props}>
436
+ <div className="w-full h-28" />
437
+ <div className="flex items-center gap-1 pl-2 pr-1 py-1 min-w-0">
438
+ <Truncating text={name} className="flex-1 text-caption text-text-primary truncate" wrapperClassName="flex-1" />
439
+ {downloadControl}
440
+ </div>
435
441
  </Card>
436
442
  )
437
443
  }
package/src/LinkChips.mdx CHANGED
@@ -48,7 +48,8 @@ LinkChips.configure({
48
48
  is asked about and becomes its chip at once, with the caret at its end, so
49
49
  **ReferenceMenu** can open its menu.
50
50
  - A URL in code or in a code block stays text, and so does a link on words.
51
- - Each URL is asked about once, and one editor asks about at most `limit`
51
+ - Each URL is asked about once while the document holds it, and one editor
52
+ asks about at most `limit` of the URLs it holds unanswered at a time
52
53
  (24 by default), the first ones: an answer can cost the app a query.
53
54
  - A pasted URL's chip is its own undo step: undo gives the URL back, and a
54
55
  second undo takes the paste away. A typed URL's chip is not a step: undo
@@ -83,6 +83,23 @@ describe('LinkChips', () => {
83
83
  expect(asked).toEqual([['https://a.example/1', 'https://a.example/2']])
84
84
  })
85
85
 
86
+ it('counts its limit over the URLs the document holds: a message box keeps asking after the last message is sent', async () => {
87
+ const asked: string[][] = []
88
+ editor = new Editor({
89
+ extensions: [
90
+ StarterKit.configure({ link: false }),
91
+ BlockId,
92
+ ReferenceNode,
93
+ LinkChips.configure({ referenceOf: () => undefined, ask: (urls) => void asked.push(urls), limit: 2 }),
94
+ ],
95
+ content: para({ type: 'text', text: 'https://a.example/1 https://a.example/2 ' }),
96
+ })
97
+ await tick()
98
+ // Sent: the box is emptied, and the next message's links are asked about.
99
+ editor.commands.setContent(para({ type: 'text', text: 'https://a.example/3 ' }))
100
+ expect(asked).toEqual([['https://a.example/1', 'https://a.example/2'], ['https://a.example/3']])
101
+ })
102
+
86
103
  it('turns the URL into its chip once the answer comes back', async () => {
87
104
  const known = new Map<string, string>()
88
105
  const ed = make(para({ type: 'text', text: `See ${URL_} now` }), known)
package/src/LinkChips.ts CHANGED
@@ -215,6 +215,10 @@ export const LinkChips = Extension.create<LinkChipsOptions>({
215
215
  const look = (state: EditorState) => {
216
216
  if (!options.ask) return
217
217
  const { open } = findLinkChips(state.doc, state.selection, options.referenceOf, key.getState(state)?.pasted)
218
+ // The limit counts the URLs the document still holds unanswered: a message box lives across many messages,
219
+ // and one that counted every URL it was ever asked would stop making chips after a day's links (OPT-19).
220
+ const waiting = new Set(open)
221
+ for (const url of asked) if (!waiting.has(url)) asked.delete(url)
218
222
  const fresh = open.filter((url) => !asked.has(url)).slice(0, Math.max(0, options.limit - asked.size))
219
223
  if (!fresh.length) return
220
224
  for (const url of fresh) asked.add(url)