@flytedan/flytebot-design-system 0.49.2 → 0.50.1

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": "@flytedan/flytebot-design-system",
3
- "version": "0.49.2",
3
+ "version": "0.50.1",
4
4
  "description": "flytedesk Design System — shared component library, tokens, and business-logic kits for flytedesk apps.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -1882,13 +1882,23 @@
1882
1882
  min-width: 0;
1883
1883
  overflow: hidden;
1884
1884
  }
1885
+ /* The figure and its delta share a line while both fit; when the tile is too narrow for
1886
+ * both, the delta wraps onto its own line under the figure. Neither is ever clipped or
1887
+ * shrunk: each is one unbreakable unit (nowrap) and only the gap between them yields. */
1885
1888
  .fd-stat-top {
1886
1889
  display: flex;
1890
+ flex-wrap: wrap;
1887
1891
  align-items: baseline;
1888
- gap: 8px;
1889
- white-space: nowrap;
1892
+ column-gap: 8px;
1893
+ row-gap: 2px;
1890
1894
  min-width: 0;
1891
1895
  }
1896
+ .fd-stat-delta {
1897
+ flex: none;
1898
+ font-weight: 600;
1899
+ font-size: var(--body-sm-size);
1900
+ white-space: nowrap;
1901
+ }
1892
1902
  .fd-stat-sub {
1893
1903
  /* MP-141: this is explanatory copy ("on every funded campus, at minimum"),
1894
1904
  * never a user-data identifier — per the kit's truncation rule (see this
@@ -2052,6 +2062,14 @@
2052
2062
  color: var(--text);
2053
2063
  vertical-align: middle;
2054
2064
  }
2065
+ /* SMS-50: a link-variant Button in a cell (a name that acts on its row) is text, not a control:
2066
+ it wraps like the text around it and reads left-aligned. A Button on its own never wraps
2067
+ (`white-space: nowrap`), so a long name kept its row on one line and stretched the table
2068
+ past its card. */
2069
+ .fd-table td .fd-btn-link {
2070
+ white-space: normal;
2071
+ text-align: left;
2072
+ }
2055
2073
  .fd-table td.is-num {
2056
2074
  text-align: right;
2057
2075
  font-variant-numeric: tabular-nums lining-nums;
@@ -2965,22 +2983,67 @@
2965
2983
  transform: rotate(180deg);
2966
2984
  }
2967
2985
 
2968
- /* ============ FilterBar (MP-243) ============ */
2969
- /* The bar itself: a wrapping row of chips plus the trailing "Filter" trigger. Kept as its
2970
- own class (rather than composing .fd-row/.fd-wrap utilities) because inline-flex, not
2971
- flex, is load-bearing here — a consumer drops FilterBar into a toolbar row alongside
2972
- other inline elements, and a flex (block-level) root would claim the full row width. */
2986
+ /* ============ FilterBar (MP-243, SMS-50) ============ */
2987
+ /* The bar is two columns on one wrapping row: the MAIN column, which is the "Filter" trigger
2988
+ (its own column, always first) beside the chips (a wrapping area of their own, taking the room
2989
+ left over), and an optional `actions` slot pinned to the right edge (`margin-left: auto`).
2990
+ The columns top-align, so however many lines the chips wrap onto, the trigger and the actions
2991
+ never move. The main column never wraps its two children under each other, at any width; when
2992
+ the row is too narrow for main and actions together (main keeps a floor of
2993
+ --filterbar-main-min), the actions wrap onto a row of their own and stay right-aligned.
2994
+
2995
+ It is a block-level flex box that fills the row it is put in, and `min-width: 0` lets it
2996
+ shrink inside a flex parent instead of forcing that parent wider. (Until SMS-50 it was
2997
+ inline-flex with `max-width: 100%`: an inline-flex box's shrink-to-fit width comes from its
2998
+ UNWRAPPED content, so wrapping only worked once something capped it. A block-level flex box
2999
+ takes its width from its container, which is what makes wrapping, and the chips column's
3000
+ own width, well defined.) */
2973
3001
  .fd-filterbar {
2974
- display: inline-flex;
3002
+ --filterbar-main-min: 20rem;
3003
+ /* The most a chip may take. A longer value is shortened in the middle (MiddleEllipsis). */
3004
+ --filterbar-chip-max: 15rem;
3005
+ display: flex;
3006
+ flex: 1 1 auto;
3007
+ flex-wrap: wrap;
3008
+ align-items: flex-start;
3009
+ gap: var(--s-sm) var(--s-md);
3010
+ min-width: 0;
3011
+ }
3012
+ .fd-filterbar-main {
3013
+ flex: 1 1 var(--filterbar-main-min);
3014
+ min-width: 0;
3015
+ display: flex;
3016
+ align-items: flex-start;
3017
+ gap: var(--s-sm) var(--s-md);
3018
+ }
3019
+ /* A bar with nothing to show in it (no Filter button, no chips: the options have not loaded yet)
3020
+ gives up its main column entirely, so the actions are not pushed onto a row of their own by a
3021
+ column that holds nothing. */
3022
+ .fd-filterbar-main:not(:has(.fd-filterbar-trigger, .fd-tag)) {
3023
+ display: none;
3024
+ }
3025
+ .fd-filterbar-trigger {
3026
+ flex: none;
3027
+ }
3028
+ .fd-filterbar-chips {
3029
+ flex: 1 1 0;
3030
+ min-width: 0;
3031
+ display: flex;
2975
3032
  flex-wrap: wrap;
2976
3033
  align-items: center;
3034
+ align-content: center;
2977
3035
  gap: var(--s-sm);
2978
- /* MP-243: an inline-flex box's shrink-to-fit width is computed from its UNWRAPPED
2979
- content first — flex-wrap only kicks in once something already caps the box's own
2980
- width, which an inline-flex element does not get for free from its containing block
2981
- the way a block-level flex does. Without this, enough chips run the bar past the
2982
- viewport edge instead of wrapping onto a second line, at any width narrow enough to
2983
- matter (found opening three fields' chips on a 375px viewport). */
3036
+ /* One chip row centres on the Filter button (a control-h-md button) beside it. */
3037
+ min-height: var(--control-h-md);
3038
+ }
3039
+ .fd-filterbar-actions {
3040
+ flex: none;
3041
+ margin-left: auto;
3042
+ display: flex;
3043
+ flex-wrap: wrap;
3044
+ align-items: flex-start;
3045
+ justify-content: flex-end;
3046
+ gap: var(--s-sm) var(--s-md);
2984
3047
  max-width: 100%;
2985
3048
  }
2986
3049
  /* MP-282: seen live at 375px — a chip whose value is long (a full campaign name) painted
@@ -2994,26 +3057,19 @@
2994
3057
  short, already-bounded-by-their-own-layout content where this bug was never observed and
2995
3058
  a forced max-width + ellipsis would only truncate text that fits today for no reason.
2996
3059
 
2997
- A `max-width: 100%` on `.fd-tag` ALONE does nothing here — tried first, and it left the
2998
- chip at its full 633px (proven by this card's own failing-first browser test): the actual
2999
- flex item .fd-filterbar-chip wraps `.fd-tag` in for select/multiSelect/range/date's chip
3000
- (the extra <span> FilterBar.tsx keeps around so React preserves one stable DOM node across
3001
- the editing/non-editing transition — see FilterBar.tsx's own comment on it) has `width:
3002
- auto` (shrink-to-fit) with a default `min-width: auto`, which for non-wrapping text (`.fd-
3003
- tag`'s inherited `white-space: nowrap`) equals the label's full intrinsic width — so the
3004
- flex algorithm's shrink never actually engages, `.fd-filterbar-chip` sizes itself to fit
3005
- the (unclamped) content, and a percentage `max-width` on the button THEN resolves against
3006
- that same content-derived size: a circular dependency (CSS2.1 §10.2's "if the containing
3007
- block's width depends on this element's width... the resulting layout is undefined"),
3008
- which real browsers resolve by ignoring the percentage rather than clamping anything.
3009
- `min-width: 0` on the actual flex item is what breaks the cycle: it lets the shrink
3010
- algorithm bring `.fd-filterbar-chip` down to whatever room is actually left in the row —
3011
- a value the flex layout itself computes from `.fd-filterbar`'s own (non-circular, genuinely
3012
- definite once its own ancestor has a real width) size — and ONLY once that ancestor box has
3013
- a real, non-content-derived width does `.fd-tag`'s `max-width: 100%` below have anything
3014
- sound to resolve against. */
3060
+ The bound goes on .fd-filterbar-chip, the actual flex item of the chips column (the extra
3061
+ <span> FilterBar.tsx wraps `.fd-tag` in for select/multiSelect/range/date's chip, so React
3062
+ keeps one stable DOM node across the editing/non-editing transition). A percentage
3063
+ `max-width` on the button ALONE does nothing: this item's own width is shrink-to-fit
3064
+ (`auto`) with a default `min-width: auto`, which for non-wrapping text equals the label's
3065
+ full intrinsic width, so the percentage would resolve against a size derived from the very
3066
+ content it is meant to clamp (CSS2.1 §10.2: undefined; real browsers ignore it).
3067
+ `min-width: 0` on the flex item breaks that cycle, and `max-width` here is a plain length
3068
+ bounded by the row, so the chip is never wider than --filterbar-chip-max nor than the row
3069
+ it wraps in (SMS-50: was "the row" only, so a long value ran the whole row wide). */
3015
3070
  .fd-filterbar-chip {
3016
3071
  min-width: 0;
3072
+ max-width: min(100%, var(--filterbar-chip-max));
3017
3073
  }
3018
3074
  .fd-filterbar .fd-tag {
3019
3075
  max-width: 100%;
@@ -3021,21 +3077,32 @@
3021
3077
  }
3022
3078
  /* Tag.tsx always wraps its children in this span (see Tag.tsx's own MP-282 comment) so
3023
3079
  there is something block-level (flex items are blockified, .fd-tag is inline-flex) to
3024
- hang `text-overflow: ellipsis` off. Unscoped, this rule would do nothing: no width is
3025
- ever constrained on a bare .fd-tag-label outside FilterBar, so there is nothing for
3026
- `white-space: nowrap` (inherited from .fd-tag either way) to need to ellipsise — a Tag
3027
- used anywhere else keeps exactly its current look. `min-width: 0` is the same override
3028
- as above, needed a second time because this label is itself a flex item of .fd-tag. The
3029
- full text stays as this span's actual text content regardless of the visual ellipsis —
3030
- that's what keeps the button's accessible name (computed from content, not from what's
3031
- painted) the whole value; Tag's `title` prop (FilterBar passes the same full value)
3080
+ hang the ellipsis off. `min-width: 0` is needed because this label is itself a flex item
3081
+ of .fd-tag. The full text stays as the label's actual text content regardless of what is
3082
+ painted — that's what keeps the button's accessible name (computed from content, not from
3083
+ what's painted) the whole value; Tag's `title` prop (FilterBar passes the same full value)
3032
3084
  covers the hover-tooltip half of that same requirement. */
3033
3085
  .fd-filterbar .fd-tag-label {
3034
3086
  overflow: hidden;
3035
- text-overflow: ellipsis;
3036
3087
  white-space: nowrap;
3037
3088
  min-width: 0;
3038
3089
  }
3090
+ /* MiddleEllipsis (SMS-50): the head shrinks and ends in "…"; the tail never shrinks, so the end
3091
+ of a long value stays visible ("Flytedesk Spr…ement (na)"). `white-space: pre` keeps a space
3092
+ at the seam between the two spans, which a flex item would otherwise strip. */
3093
+ .fd-mid-ellipsis {
3094
+ display: flex;
3095
+ min-width: 0;
3096
+ white-space: pre;
3097
+ }
3098
+ .fd-mid-ellipsis-head {
3099
+ min-width: 0;
3100
+ overflow: hidden;
3101
+ text-overflow: ellipsis;
3102
+ }
3103
+ .fd-mid-ellipsis-tail {
3104
+ flex: none;
3105
+ }
3039
3106
  /* The text field's own inline "chip that is also an input" — its editing state reuses
3040
3107
  .fd-tag's box but swaps in the selected/brand treatment .fd-tag[aria-pressed] already
3041
3108
  defines elsewhere, and the raw <input> inside is stripped of its own chrome so it reads
@@ -3897,9 +3964,11 @@
3897
3964
  stroke: var(--border-strong);
3898
3965
  stroke-width: 1;
3899
3966
  }
3967
+ /* SMS-50: 10px, and it stays 10px — TrendChart draws its SVG in real pixels at the container's
3968
+ width (viewBox === rendered size), so this is what paints, never multiplied by the card. */
3900
3969
  .fd-trend-axis-label {
3901
3970
  fill: var(--text-muted);
3902
- font-size: 10.5px;
3971
+ font-size: 10px;
3903
3972
  font-family: var(--font-sans);
3904
3973
  }
3905
3974
  .fd-trend-line {
@@ -3977,28 +4046,31 @@
3977
4046
  }
3978
4047
 
3979
4048
  /* BarList */
4049
+ /* One shared column set for the whole list, so every row's label, bar, value and percent
4050
+ line up. The label column is as wide as the widest label (capped at 40% of the list, past
4051
+ which a long label ellipsizes and exposes its full text via `title`), so a short label such
4052
+ as a date is always shown whole instead of being cut to a fixed share of the row. Each level
4053
+ between the list and the cells (item, Tooltip anchor, button) is a subgrid over those same
4054
+ columns. */
3980
4055
  .fd-barlist-list {
3981
4056
  list-style: none;
3982
4057
  margin: 0;
3983
4058
  padding: 0;
3984
- display: flex;
3985
- flex-direction: column;
3986
- gap: 2px;
3987
- }
3988
- /* Tooltip's own trigger wrapper is an inline-flex span sized to its content (so an icon
3989
- trigger elsewhere in the kit hugs the icon) — here the trigger IS the whole row, so it
3990
- has to fill the row's width instead of shrink-wrapping it, or every column past the
3991
- label collapses to min-content. */
3992
- .fd-barlist-row .fd-tooltip-anchor {
3993
- display: block;
3994
- width: 100%;
4059
+ display: grid;
4060
+ grid-template-columns: fit-content(40%) minmax(48px, 1fr) max-content max-content;
4061
+ column-gap: 10px;
4062
+ row-gap: 2px;
3995
4063
  }
4064
+ .fd-barlist-row,
4065
+ .fd-barlist-row .fd-tooltip-anchor,
3996
4066
  .fd-barlist-btn {
3997
4067
  display: grid;
3998
- grid-template-columns: minmax(64px, 0.9fr) 3fr auto auto;
4068
+ grid-column: 1 / -1;
4069
+ grid-template-columns: subgrid;
4070
+ min-width: 0;
4071
+ }
4072
+ .fd-barlist-btn {
3999
4073
  align-items: center;
4000
- gap: 10px;
4001
- width: 100%;
4002
4074
  border: none;
4003
4075
  background: none;
4004
4076
  padding: 6px 4px;
@@ -4050,9 +4122,9 @@
4050
4122
  text-align: right;
4051
4123
  }
4052
4124
  @media (max-width: 480px) {
4053
- .fd-barlist-btn {
4054
- grid-template-columns: minmax(52px, 0.8fr) 2fr auto auto;
4055
- gap: 6px;
4125
+ .fd-barlist-list {
4126
+ grid-template-columns: fit-content(40%) minmax(40px, 1fr) max-content;
4127
+ column-gap: 6px;
4056
4128
  }
4057
4129
  .fd-barlist-pct {
4058
4130
  display: none;