@jsenv/navi 0.29.103 → 0.29.104

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.
@@ -202,7 +202,8 @@ consistency across the app, not from any single call site.
202
202
  `Text`, `maxLines` as the one way to truncate (and why `lineClamp={1}` is
203
203
  not it), what makes truncation actually happen in a flex row, and the two
204
204
  opposite shapes of "icon | text | icon" — the end icon kept outside the
205
- truncating text, or `attachLastChild` so it never lands alone on a line.
205
+ truncating text, or `attachLastChild` so it never lands alone on a line
206
+ and why an emoji belongs only in free text, rendered with `emojiAsIcon`.
206
207
  Read it before writing an overflowing label or a row with a trailing icon.
207
208
  - `docs/i18n.md` — where the texts an app displays live: `interpolateText` /
208
209
  `<Interpolate>` for one sentence, `createI18n` for the app's registry,
@@ -217,7 +218,11 @@ consistency across the app, not from any single call site.
217
218
  - `docs/interactions.md` — the `interactions` prop: making a component answer a
218
219
  swipe, a held press, a shortcut, and registering a gesture navi does not have.
219
220
  It also holds `ownTarget`, for an affordance an application draws inside a
220
- zone that belongs to another control — a chip's cross, an eye, a diskette.
221
+ zone that belongs to another control — a chip's cross, an eye, a diskette:
222
+ the three modes and the question that picks one (does it write to the control
223
+ it sits in?), the `data-own-target` attribute for an element you draw
224
+ yourself, and what `ownTarget` does NOT stop (a plain `onClick` on an
225
+ ancestor).
221
226
  Read it before reading the pointer by hand — who owns a press between nested
222
227
  boxes, and what a touch may do, are decided before the first pixel moves and
223
228
  cannot be got right from outside navi — and before stopping the propagation of
@@ -531,15 +531,11 @@ sideways), while a `move` goes wherever it is put, a `land` wherever the board h
531
531
  places and a `toss` wherever it was thrown (`xy`). `data-drag-delay`,
532
532
  `data-drag-slop`, `data-drag-threshold` tune when the press becomes a grab.
533
533
 
534
- ### A control inside something draggable
534
+ ### An affordance inside somebody else's box: `ownTarget`
535
535
 
536
- `ownTarget` on the control says it: a chip's cross inside a carried piece, an
537
- eye on a row that travels, a diskette on a picker's façade. The press belongs to
538
- that control alone — no gesture starts under it, and nothing above it answers
539
- the mousedown or the click. It also follows the interactivity of the zone around
540
- it: read-only, disabled or busy, the affordance goes rather than greys (pass
541
- `ownTarget="refuse"` for one whose presence is information in itself), and its
542
- `onClick` waits for its own gate instead of firing from the DOM.
536
+ A chip's cross inside a carried piece, an eye on a row that travels, a diskette
537
+ on a picker's façade. It is aimed AT, not merely inside, and the press belongs
538
+ to it alone:
543
539
 
544
540
  ```jsx
545
541
  <Badge.Button ownTarget onClick={() => remove(id)}>
@@ -547,9 +543,50 @@ it: read-only, disabled or busy, the affordance goes rather than greys (pass
547
543
  </Badge.Button>
548
544
  ```
549
545
 
550
- `data-drag-ignore` says the same thing to the gesture alone, for something that
551
- is not a control: the press there is none of the gesture's business, and the
552
- element keeps both its cursor and its text selection.
546
+ No gesture starts under it, no navi control above it answers the mousedown or
547
+ the click, and its `onClick` waits for its own interaction gate instead of
548
+ firing from the DOM.
549
+
550
+ #### Does it write to the control it sits in?
551
+
552
+ That question, and nothing else, picks the mode:
553
+
554
+ | what it does | mode | on a read-only / disabled / busy zone |
555
+ | ------------------------------------------------------------ | -------------------- | ----------------------------------------- |
556
+ | writes to it (a cross that removes, a stepper) | `ownTarget` | it goes |
557
+ | writes to it, and its presence says there is something there | `ownTarget="refuse"` | it stays and refuses with a callout |
558
+ | never touches it (a diskette saving into MY address book) | `ownTarget="always"` | nothing changes: still lit, still pressed |
559
+
560
+ A greyed cross that still removes is worse than no cross — hence the default.
561
+ `"always"` is the other extreme and the caller owns it: the zone's read-only is
562
+ about a value the affordance does not write, so answering "read-only" to a
563
+ gesture that was never going to write anything says nothing true. Use it only
564
+ when that is really the case.
565
+
566
+ #### On an element you draw yourself
567
+
568
+ The claim is one attribute, and nothing is asked of the element carrying it —
569
+ `ownTarget` is only the prop that writes it on a navi control:
570
+
571
+ ```jsx
572
+ <button class="court_side" data-own-target="always" onClick={explain}>
573
+ ```
574
+
575
+ That is what the controls above read, and what the gesture readers read
576
+ (`data-drag-handle`, `data-drag-ignore` and friends are the same vocabulary). An
577
+ application keeps its own drawing and gets the press ownership all the same.
578
+
579
+ #### navi steps back; a plain `onClick` does not
580
+
581
+ What `ownTarget` stops is navi answering: the controls above it, and the
582
+ gestures. It does **not** stop the event — the propagation is left whole, so
583
+ that everything which is not a navi interaction still sees the press it always
584
+ saw. A raw `onClick` on an ancestor is one of those, and still fires; stop it
585
+ there yourself if it must not.
586
+
587
+ `data-drag-ignore` says a narrower thing, to the gesture alone: the press there
588
+ is none of the gesture's business, and the element keeps both its cursor and its
589
+ text selection.
553
590
 
554
591
  ### What says a thing can be picked up
555
592
 
@@ -28,7 +28,7 @@ same names — `size="l"` is a font size, `padding="l"` is a gap.
28
28
 
29
29
  Live examples: `src/text/demos/*_demo.html` — one page per concern
30
30
  (`text_overflow_demo.html`, `text_spacing_demo.html`, `text_loading_demo.html`,
31
- `text_attach_last_child_demo.html`, `icon_demo.html`).
31
+ `text_attach_last_child_demo.html`, `text_emoji_demo.html`, `icon_demo.html`).
32
32
 
33
33
  ## Truncating: `maxLines`, and nothing else
34
34
 
@@ -61,7 +61,9 @@ width must say it too**, or the whole chain grows instead of truncating:
61
61
 
62
62
  ```jsx
63
63
  <Box flex width="300" spacing="s">
64
- <Box flex expandX minWidth="0"> {/* without minWidth the row just grows */}
64
+ <Box flex expandX minWidth="0">
65
+ {" "}
66
+ {/* without minWidth the row just grows */}
65
67
  <Text maxLines={1}>…</Text>
66
68
  </Box>
67
69
  </Box>
@@ -153,6 +155,45 @@ Two things opt out of that flow:
153
155
  the space characters too. This replaces them with padding-based spaces, so the
154
156
  underline stops at the text. `Link` sets it.
155
157
 
158
+ ## Emoji
159
+
160
+ **An emoji is not a character the layout can absorb.** The system emoji fonts
161
+ (Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji) have a taller
162
+ ascent/descent than any text font, so the moment an emoji sits in a line, that
163
+ line is taller than the ones around it: a row shifts down next to its
164
+ neighbours, a paragraph's lines are unevenly spaced, a truncated label no
165
+ longer lines up with its siblings.
166
+
167
+ So, first: **an emoji is only expected in free text a user typed** — a message,
168
+ a comment, a description. It has no business in a name, a title, an identifier,
169
+ a label. Validate those fields so it never gets in, rather than teaching every
170
+ row of the app to survive it.
171
+
172
+ Where it is expected, `emojiAsIcon`:
173
+
174
+ ```jsx
175
+ <Text emojiAsIcon>Salut 👋 on se retrouve au parc 🌳 ?</Text>
176
+ ```
177
+
178
+ Every emoji found in the string children is rendered inside an `Icon`, which
179
+ caps it at `1em` and centers it on the line like any glyph icon — the line keeps
180
+ the height of its text, on one line or several. This is what chat apps do (an
181
+ emoji is a 1em box, never a glyph with its own metrics). `Button` and
182
+ `MessageBox` have it on by default — a label is one line whose height
183
+ everything around it relies on, a message is free text; `Badge` forwards it,
184
+ opt-in.
185
+
186
+ What it does not do, and that is accepted: under `maxLines` the `Text` clips at
187
+ its own box — that is what truncation is — and an emoji drawn a little beyond
188
+ its 1em box can lose a sliver at the top. The line stays aligned, which is the
189
+ part that matters.
190
+
191
+ **Do not raise `lineHeight` because of emoji.** The default line height keeps
192
+ text compact and, with `emojiAsIcon`, holds up even when nearly every word is
193
+ an emoji (see the dense cases in `text_emoji_demo.html`). A `lineHeight` is a
194
+ typographic choice for the text itself — a paragraph that wants air — never a
195
+ workaround for a glyph that grew too tall; that glyph gets `emojiAsIcon`.
196
+
156
197
  ## Text that must not move when its style changes
157
198
 
158
199
  A label that becomes bold when its row is selected reflows everything around it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.103",
3
+ "version": "0.29.104",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "prepublishOnly": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@jsenv/dom": "0.17.26",
32
+ "@jsenv/dom": "0.17.27",
33
33
  "@jsenv/humanize": "1.7.8",
34
34
  "@jsenv/validity": "0.4.2"
35
35
  },