@flytedan/flytebot-design-system 0.49.2 → 0.50.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": "@flytedan/flytebot-design-system",
3
- "version": "0.49.2",
3
+ "version": "0.50.0",
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",
@@ -2052,6 +2052,14 @@
2052
2052
  color: var(--text);
2053
2053
  vertical-align: middle;
2054
2054
  }
2055
+ /* SMS-50: a link-variant Button in a cell (a name that acts on its row) is text, not a control:
2056
+ it wraps like the text around it and reads left-aligned. A Button on its own never wraps
2057
+ (`white-space: nowrap`), so a long name kept its row on one line and stretched the table
2058
+ past its card. */
2059
+ .fd-table td .fd-btn-link {
2060
+ white-space: normal;
2061
+ text-align: left;
2062
+ }
2055
2063
  .fd-table td.is-num {
2056
2064
  text-align: right;
2057
2065
  font-variant-numeric: tabular-nums lining-nums;
@@ -2965,22 +2973,67 @@
2965
2973
  transform: rotate(180deg);
2966
2974
  }
2967
2975
 
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. */
2976
+ /* ============ FilterBar (MP-243, SMS-50) ============ */
2977
+ /* The bar is two columns on one wrapping row: the MAIN column, which is the "Filter" trigger
2978
+ (its own column, always first) beside the chips (a wrapping area of their own, taking the room
2979
+ left over), and an optional `actions` slot pinned to the right edge (`margin-left: auto`).
2980
+ The columns top-align, so however many lines the chips wrap onto, the trigger and the actions
2981
+ never move. The main column never wraps its two children under each other, at any width; when
2982
+ the row is too narrow for main and actions together (main keeps a floor of
2983
+ --filterbar-main-min), the actions wrap onto a row of their own and stay right-aligned.
2984
+
2985
+ It is a block-level flex box that fills the row it is put in, and `min-width: 0` lets it
2986
+ shrink inside a flex parent instead of forcing that parent wider. (Until SMS-50 it was
2987
+ inline-flex with `max-width: 100%`: an inline-flex box's shrink-to-fit width comes from its
2988
+ UNWRAPPED content, so wrapping only worked once something capped it. A block-level flex box
2989
+ takes its width from its container, which is what makes wrapping, and the chips column's
2990
+ own width, well defined.) */
2973
2991
  .fd-filterbar {
2974
- display: inline-flex;
2992
+ --filterbar-main-min: 20rem;
2993
+ /* The most a chip may take. A longer value is shortened in the middle (MiddleEllipsis). */
2994
+ --filterbar-chip-max: 15rem;
2995
+ display: flex;
2996
+ flex: 1 1 auto;
2997
+ flex-wrap: wrap;
2998
+ align-items: flex-start;
2999
+ gap: var(--s-sm) var(--s-md);
3000
+ min-width: 0;
3001
+ }
3002
+ .fd-filterbar-main {
3003
+ flex: 1 1 var(--filterbar-main-min);
3004
+ min-width: 0;
3005
+ display: flex;
3006
+ align-items: flex-start;
3007
+ gap: var(--s-sm) var(--s-md);
3008
+ }
3009
+ /* A bar with nothing to show in it (no Filter button, no chips: the options have not loaded yet)
3010
+ gives up its main column entirely, so the actions are not pushed onto a row of their own by a
3011
+ column that holds nothing. */
3012
+ .fd-filterbar-main:not(:has(.fd-filterbar-trigger, .fd-tag)) {
3013
+ display: none;
3014
+ }
3015
+ .fd-filterbar-trigger {
3016
+ flex: none;
3017
+ }
3018
+ .fd-filterbar-chips {
3019
+ flex: 1 1 0;
3020
+ min-width: 0;
3021
+ display: flex;
2975
3022
  flex-wrap: wrap;
2976
3023
  align-items: center;
3024
+ align-content: center;
2977
3025
  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). */
3026
+ /* One chip row centres on the Filter button (a control-h-md button) beside it. */
3027
+ min-height: var(--control-h-md);
3028
+ }
3029
+ .fd-filterbar-actions {
3030
+ flex: none;
3031
+ margin-left: auto;
3032
+ display: flex;
3033
+ flex-wrap: wrap;
3034
+ align-items: flex-start;
3035
+ justify-content: flex-end;
3036
+ gap: var(--s-sm) var(--s-md);
2984
3037
  max-width: 100%;
2985
3038
  }
2986
3039
  /* MP-282: seen live at 375px — a chip whose value is long (a full campaign name) painted
@@ -2994,26 +3047,19 @@
2994
3047
  short, already-bounded-by-their-own-layout content where this bug was never observed and
2995
3048
  a forced max-width + ellipsis would only truncate text that fits today for no reason.
2996
3049
 
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. */
3050
+ The bound goes on .fd-filterbar-chip, the actual flex item of the chips column (the extra
3051
+ <span> FilterBar.tsx wraps `.fd-tag` in for select/multiSelect/range/date's chip, so React
3052
+ keeps one stable DOM node across the editing/non-editing transition). A percentage
3053
+ `max-width` on the button ALONE does nothing: this item's own width is shrink-to-fit
3054
+ (`auto`) with a default `min-width: auto`, which for non-wrapping text equals the label's
3055
+ full intrinsic width, so the percentage would resolve against a size derived from the very
3056
+ content it is meant to clamp (CSS2.1 §10.2: undefined; real browsers ignore it).
3057
+ `min-width: 0` on the flex item breaks that cycle, and `max-width` here is a plain length
3058
+ bounded by the row, so the chip is never wider than --filterbar-chip-max nor than the row
3059
+ it wraps in (SMS-50: was "the row" only, so a long value ran the whole row wide). */
3015
3060
  .fd-filterbar-chip {
3016
3061
  min-width: 0;
3062
+ max-width: min(100%, var(--filterbar-chip-max));
3017
3063
  }
3018
3064
  .fd-filterbar .fd-tag {
3019
3065
  max-width: 100%;
@@ -3021,21 +3067,32 @@
3021
3067
  }
3022
3068
  /* Tag.tsx always wraps its children in this span (see Tag.tsx's own MP-282 comment) so
3023
3069
  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)
3070
+ hang the ellipsis off. `min-width: 0` is needed because this label is itself a flex item
3071
+ of .fd-tag. The full text stays as the label's actual text content regardless of what is
3072
+ painted — that's what keeps the button's accessible name (computed from content, not from
3073
+ what's painted) the whole value; Tag's `title` prop (FilterBar passes the same full value)
3032
3074
  covers the hover-tooltip half of that same requirement. */
3033
3075
  .fd-filterbar .fd-tag-label {
3034
3076
  overflow: hidden;
3035
- text-overflow: ellipsis;
3036
3077
  white-space: nowrap;
3037
3078
  min-width: 0;
3038
3079
  }
3080
+ /* MiddleEllipsis (SMS-50): the head shrinks and ends in "…"; the tail never shrinks, so the end
3081
+ of a long value stays visible ("Flytedesk Spr…ement (na)"). `white-space: pre` keeps a space
3082
+ at the seam between the two spans, which a flex item would otherwise strip. */
3083
+ .fd-mid-ellipsis {
3084
+ display: flex;
3085
+ min-width: 0;
3086
+ white-space: pre;
3087
+ }
3088
+ .fd-mid-ellipsis-head {
3089
+ min-width: 0;
3090
+ overflow: hidden;
3091
+ text-overflow: ellipsis;
3092
+ }
3093
+ .fd-mid-ellipsis-tail {
3094
+ flex: none;
3095
+ }
3039
3096
  /* The text field's own inline "chip that is also an input" — its editing state reuses
3040
3097
  .fd-tag's box but swaps in the selected/brand treatment .fd-tag[aria-pressed] already
3041
3098
  defines elsewhere, and the raw <input> inside is stripped of its own chrome so it reads
@@ -3897,9 +3954,11 @@
3897
3954
  stroke: var(--border-strong);
3898
3955
  stroke-width: 1;
3899
3956
  }
3957
+ /* SMS-50: 10px, and it stays 10px — TrendChart draws its SVG in real pixels at the container's
3958
+ width (viewBox === rendered size), so this is what paints, never multiplied by the card. */
3900
3959
  .fd-trend-axis-label {
3901
3960
  fill: var(--text-muted);
3902
- font-size: 10.5px;
3961
+ font-size: 10px;
3903
3962
  font-family: var(--font-sans);
3904
3963
  }
3905
3964
  .fd-trend-line {