@hjmds/react 1.19.0 → 1.21.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/dist/actions.d.ts +10 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +9 -3
- package/dist/actions.js.map +1 -1
- package/dist/authoring-tools.d.ts +38 -0
- package/dist/authoring-tools.d.ts.map +1 -0
- package/dist/authoring-tools.js +65 -0
- package/dist/authoring-tools.js.map +1 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +1 -1
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +1 -0
- package/dist/calendar.js.map +1 -1
- package/dist/chart.d.ts +157 -0
- package/dist/chart.d.ts.map +1 -0
- package/dist/chart.js +467 -0
- package/dist/chart.js.map +1 -0
- package/dist/collection-editing.d.ts +66 -0
- package/dist/collection-editing.d.ts.map +1 -0
- package/dist/collection-editing.js +126 -0
- package/dist/collection-editing.js.map +1 -0
- package/dist/communication.d.ts +51 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +71 -0
- package/dist/communication.js.map +1 -0
- package/dist/data-table.d.ts +21 -1
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +105 -10
- package/dist/data-table.js.map +1 -1
- package/dist/data-workflows.d.ts +67 -0
- package/dist/data-workflows.d.ts.map +1 -0
- package/dist/data-workflows.js +60 -0
- package/dist/data-workflows.js.map +1 -0
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.js +1 -1
- package/dist/evidence.js.map +1 -1
- package/dist/internal/use-composition-action.d.ts +17 -0
- package/dist/internal/use-composition-action.d.ts.map +1 -0
- package/dist/internal/use-composition-action.js +18 -0
- package/dist/internal/use-composition-action.js.map +1 -0
- package/dist/media-transport.d.ts +22 -0
- package/dist/media-transport.d.ts.map +1 -0
- package/dist/media-transport.js +50 -0
- package/dist/media-transport.js.map +1 -0
- package/dist/navigation.d.ts +3 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +29 -5
- package/dist/navigation.js.map +1 -1
- package/dist/screen-flows.d.ts +1 -1
- package/dist/screen-flows.d.ts.map +1 -1
- package/dist/screen-flows.js +1 -1
- package/dist/screen-flows.js.map +1 -1
- package/dist/screens.d.ts +8 -2
- package/dist/screens.d.ts.map +1 -1
- package/dist/screens.js +11 -5
- package/dist/screens.js.map +1 -1
- package/dist/social-compositions.d.ts +52 -0
- package/dist/social-compositions.d.ts.map +1 -0
- package/dist/social-compositions.js +41 -0
- package/dist/social-compositions.js.map +1 -0
- package/dist/streaming-answer.d.ts +24 -0
- package/dist/streaming-answer.d.ts.map +1 -0
- package/dist/streaming-answer.js +57 -0
- package/dist/streaming-answer.js.map +1 -0
- package/dist/styles.css +149 -1
- package/dist/styles.layered.css +149 -1
- package/dist/table-family.d.ts +66 -0
- package/dist/table-family.d.ts.map +1 -0
- package/dist/table-family.js +75 -0
- package/dist/table-family.js.map +1 -0
- package/dist/task-controls.d.ts +47 -0
- package/dist/task-controls.d.ts.map +1 -0
- package/dist/task-controls.js +46 -0
- package/dist/task-controls.js.map +1 -0
- package/dist/toggle-feedback.d.ts +26 -0
- package/dist/toggle-feedback.d.ts.map +1 -0
- package/dist/toggle-feedback.js +47 -0
- package/dist/toggle-feedback.js.map +1 -0
- package/dist/transfer-task.d.ts +18 -0
- package/dist/transfer-task.d.ts.map +1 -0
- package/dist/transfer-task.js +56 -0
- package/dist/transfer-task.js.map +1 -0
- package/package.json +58 -3
package/dist/styles.layered.css
CHANGED
|
@@ -1219,6 +1219,149 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
|
|
|
1219
1219
|
.hjm-data-table__table tr[data-disabled] { color: var(--hjm-color-text-weak); }
|
|
1220
1220
|
.hjm-data-table__state { margin: 0; color: var(--hjm-color-text-muted); }
|
|
1221
1221
|
.hjm-data-table__footer { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--hjm-space-sm); }
|
|
1222
|
+
/* Pinned start columns (2026-10-14): the table scrolls inside its own box so sticky cells have a
|
|
1223
|
+
scroll container; pinned cells paint the canvas so scrolled text never shows through. */
|
|
1224
|
+
.hjm-data-table__scroll { overflow-x: auto; min-inline-size: 0; }
|
|
1225
|
+
.hjm-data-table__scroll > .hjm-data-table__table { inline-size: max-content; min-inline-size: 100%; }
|
|
1226
|
+
.hjm-data-table__table .hjm-data-table__pinned { position: sticky; z-index: 1; background: var(--hjm-color-bg); }
|
|
1227
|
+
.hjm-data-table__table .hjm-data-table__pinned-edge { box-shadow: inset -1px 0 0 var(--hjm-color-border); }
|
|
1228
|
+
.hjm-data-table__table .hjm-data-table__pinned-edge:dir(rtl) { box-shadow: inset 1px 0 0 var(--hjm-color-border); }
|
|
1229
|
+
.hjm-data-table__table tfoot td { border-block-start: 1px solid var(--hjm-color-border); border-block-end: 0; font-weight: var(--hjm-font-weight-semibold); }
|
|
1230
|
+
|
|
1231
|
+
/* DataTable family (2026-10-14): header bands, row groups, sub-row indent, tone/heat, bounded
|
|
1232
|
+
height. Colors mirror dataTableFamilyRecipe; heat is a background-image so pinned cells keep
|
|
1233
|
+
their opaque canvas under the tint. */
|
|
1234
|
+
.hjm-data-table__bands > th,
|
|
1235
|
+
.hjm-data-table__bands > td { padding-block-end: 0; border-block-end: 0; }
|
|
1236
|
+
.hjm-data-table__band { text-align: center; color: var(--hjm-color-text-muted); font-size: var(--hjm-type-label-size); }
|
|
1237
|
+
.hjm-data-table__band > span { display: block; padding-block-end: var(--hjm-space-xxs); border-block-end: 1px solid color-mix(in srgb, var(--hjm-color-border) 70%, transparent); }
|
|
1238
|
+
.hjm-data-table__group > th {
|
|
1239
|
+
padding-block: var(--hjm-space-xs);
|
|
1240
|
+
background: var(--hjm-color-surface-alt);
|
|
1241
|
+
color: var(--hjm-color-text-muted);
|
|
1242
|
+
font-size: var(--hjm-type-label-size);
|
|
1243
|
+
font-weight: var(--hjm-font-weight-bold);
|
|
1244
|
+
text-align: start;
|
|
1245
|
+
}
|
|
1246
|
+
/* The label stays in view while a wide table scrolls sideways. */
|
|
1247
|
+
.hjm-data-table__group > th > span { position: sticky; inset-inline-start: var(--hjm-space-md); }
|
|
1248
|
+
.hjm-data-table__table td[data-depth] { padding-inline-start: calc(var(--hjm-space-md) + var(--hjm-data-table-indent, 0px)); }
|
|
1249
|
+
.hjm-data-table[data-density="compact"] .hjm-data-table__table td[data-depth] { padding-inline-start: calc(var(--hjm-space-sm) + var(--hjm-data-table-indent, 0px)); }
|
|
1250
|
+
.hjm-table-family__heat { background-image: linear-gradient(var(--hjm-data-table-heat), var(--hjm-data-table-heat)); }
|
|
1251
|
+
.hjm-data-table__table td[data-tone], .hjm-data-table__table td[data-heat] { font-variant-numeric: tabular-nums; }
|
|
1252
|
+
.hjm-data-table__scroll[data-bounded] { overflow: auto; }
|
|
1253
|
+
/* Sticky thead (not each th) keeps band and leaf rows stacked without measuring the band height. */
|
|
1254
|
+
.hjm-data-table__scroll[data-bounded] thead { position: sticky; inset-block-start: 0; z-index: 2; background: var(--hjm-color-bg); box-shadow: inset 0 -1px 0 var(--hjm-color-border); }
|
|
1255
|
+
|
|
1256
|
+
/* LineScore / MatrixTable / ComparisonTable ---------------------------------- */
|
|
1257
|
+
.hjm-table-family { min-inline-size: 0; overflow-x: auto; border-radius: var(--hjm-radius-md); background: var(--hjm-color-surface); }
|
|
1258
|
+
.hjm-line-score__table, .hjm-matrix-table__table { inline-size: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
|
|
1259
|
+
.hjm-line-score__table th, .hjm-line-score__table td { padding: var(--hjm-space-xs) var(--hjm-space-xxs); text-align: center; white-space: nowrap; }
|
|
1260
|
+
.hjm-line-score__table thead th { color: var(--hjm-color-text-muted); font-size: var(--hjm-type-label-size); font-weight: var(--hjm-font-weight-bold); }
|
|
1261
|
+
.hjm-line-score__table th.hjm-line-score__team { padding-inline: var(--hjm-space-sm); text-align: start; color: var(--hjm-color-text-muted); font-weight: var(--hjm-font-weight-semibold); }
|
|
1262
|
+
.hjm-line-score__team-content { display: inline-flex; align-items: center; gap: var(--hjm-space-xs); }
|
|
1263
|
+
.hjm-line-score__table tr[data-leader] th.hjm-line-score__team { color: var(--hjm-color-text); font-weight: var(--hjm-font-weight-bold); }
|
|
1264
|
+
.hjm-line-score__table td.hjm-line-score__period { min-inline-size: 26px; color: var(--hjm-color-text-sub); }
|
|
1265
|
+
.hjm-line-score__table td[data-scored] { color: var(--hjm-color-text); font-weight: var(--hjm-font-weight-bold); }
|
|
1266
|
+
.hjm-line-score__table [data-current] { background: var(--hjm-table-family-selected); }
|
|
1267
|
+
.hjm-line-score__table th[data-current] { color: var(--hjm-color-content-brand); }
|
|
1268
|
+
.hjm-line-score__total { min-inline-size: 30px; color: var(--hjm-color-text-muted); font-weight: var(--hjm-font-weight-semibold); }
|
|
1269
|
+
.hjm-line-score__total-edge { border-inline-start: 1px solid var(--hjm-color-border); }
|
|
1270
|
+
.hjm-line-score__table tbody .hjm-line-score__total-edge { color: var(--hjm-color-text); font-weight: var(--hjm-font-weight-bold); }
|
|
1271
|
+
.hjm-matrix-table__table th, .hjm-matrix-table__table td { padding: var(--hjm-space-xs); text-align: center; white-space: nowrap; border-block-start: 1px solid var(--hjm-color-border); }
|
|
1272
|
+
.hjm-matrix-table__table thead th { border-block-start: 0; color: var(--hjm-color-text-muted); font-size: var(--hjm-type-label-size); font-weight: var(--hjm-font-weight-bold); }
|
|
1273
|
+
.hjm-matrix-table__table tbody th { text-align: start; padding-inline: var(--hjm-space-sm); color: var(--hjm-color-text); font-weight: var(--hjm-font-weight-bold); border-inline-end: 1px solid var(--hjm-color-border); }
|
|
1274
|
+
.hjm-matrix-table__table td { color: var(--hjm-color-text-muted); }
|
|
1275
|
+
.hjm-matrix-table__table thead td { border-block-start: 0; }
|
|
1276
|
+
.hjm-matrix-table__self { background: var(--hjm-color-surface-alt); }
|
|
1277
|
+
.hjm-matrix-table__table tr[data-highlighted] > * { background-color: var(--hjm-table-family-selected); }
|
|
1278
|
+
.hjm-comparison-table { padding-block: var(--hjm-space-xs); }
|
|
1279
|
+
.hjm-comparison-table__sides { display: flex; justify-content: space-between; padding: var(--hjm-space-xs) var(--hjm-space-md); font-weight: var(--hjm-font-weight-bold); }
|
|
1280
|
+
.hjm-comparison-table__rows { margin: 0; padding: 0; list-style: none; }
|
|
1281
|
+
.hjm-comparison-table__row { display: grid; gap: var(--hjm-space-xxs); padding: var(--hjm-space-xs) var(--hjm-space-md); border-block-start: 1px solid var(--hjm-color-border); }
|
|
1282
|
+
.hjm-comparison-table__values { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--hjm-space-sm); font-variant-numeric: tabular-nums; color: var(--hjm-color-text-sub); }
|
|
1283
|
+
.hjm-comparison-table__values > :last-child { text-align: end; }
|
|
1284
|
+
.hjm-comparison-table__values > [data-win] { color: var(--hjm-color-text); font-weight: var(--hjm-font-weight-bold); }
|
|
1285
|
+
.hjm-comparison-table__label { color: var(--hjm-color-text-muted); font-size: var(--hjm-type-label-size); }
|
|
1286
|
+
.hjm-comparison-table__bars { display: grid; grid-template-columns: 1fr 1fr; gap: var(--hjm-space-xs); block-size: 4px; }
|
|
1287
|
+
.hjm-comparison-table__track { display: flex; justify-content: flex-end; border-radius: 2px; background: var(--hjm-color-surface-alt); overflow: hidden; }
|
|
1288
|
+
.hjm-comparison-table__track[data-side="1"] { justify-content: flex-start; }
|
|
1289
|
+
.hjm-comparison-table__bar { display: block; block-size: 100%; border-radius: 2px; }
|
|
1290
|
+
|
|
1291
|
+
/* Chart ------------------------------------------------------------------- */
|
|
1292
|
+
/* Colors come from chartRecipe.chrome via --hjm-chart-* set inline by the renderer (2026-10-14). */
|
|
1293
|
+
.hjm-chart { position: relative; display: flex; flex-direction: column; gap: var(--hjm-space-xs); inline-size: 100%; min-inline-size: 0; }
|
|
1294
|
+
.hjm-chart__plot {
|
|
1295
|
+
position: relative;
|
|
1296
|
+
/* Horizontal drags scrub; vertical drags keep scrolling the page. */
|
|
1297
|
+
touch-action: pan-y;
|
|
1298
|
+
user-select: none;
|
|
1299
|
+
-webkit-user-select: none;
|
|
1300
|
+
border-radius: var(--hjm-radius-sm);
|
|
1301
|
+
outline: none;
|
|
1302
|
+
}
|
|
1303
|
+
.hjm-chart__plot:focus-visible { outline: var(--hjm-focus-width) solid var(--hjm-color-focus); outline-offset: var(--hjm-focus-offset); }
|
|
1304
|
+
.hjm-chart svg, .hjm-sparkline { display: block; overflow: visible; }
|
|
1305
|
+
.hjm-chart__grid { stroke: var(--hjm-chart-grid-line); stroke-width: var(--hjm-chart-grid-width); }
|
|
1306
|
+
.hjm-chart__reference { stroke: var(--hjm-chart-axis-label); stroke-width: 1; }
|
|
1307
|
+
.hjm-chart__cursor { stroke: var(--hjm-chart-axis-label); }
|
|
1308
|
+
.hjm-chart__dot { stroke: var(--hjm-color-bg); }
|
|
1309
|
+
.hjm-chart__label { fill: var(--hjm-chart-axis-label); font-family: inherit; font-size: var(--hjm-type-caption-size); }
|
|
1310
|
+
.hjm-chart__label[data-focused], .hjm-chart__axis-name, .hjm-chart__value { fill: var(--hjm-chart-legend-label); font-weight: var(--hjm-font-weight-semibold); }
|
|
1311
|
+
.hjm-chart__extreme { font-weight: var(--hjm-font-weight-semibold); }
|
|
1312
|
+
.hjm-chart__bubble {
|
|
1313
|
+
position: absolute;
|
|
1314
|
+
inset-block-start: 0;
|
|
1315
|
+
transform: translateX(-50%);
|
|
1316
|
+
padding: 2px var(--hjm-space-xs);
|
|
1317
|
+
border: 1px solid var(--hjm-chart-tooltip-border);
|
|
1318
|
+
border-radius: var(--hjm-radius-sm);
|
|
1319
|
+
background: var(--hjm-chart-tooltip-surface);
|
|
1320
|
+
color: var(--hjm-chart-tooltip-content);
|
|
1321
|
+
font-size: var(--hjm-type-caption-size);
|
|
1322
|
+
line-height: var(--hjm-type-caption-line-height);
|
|
1323
|
+
font-weight: var(--hjm-font-weight-bold);
|
|
1324
|
+
white-space: nowrap;
|
|
1325
|
+
pointer-events: none;
|
|
1326
|
+
}
|
|
1327
|
+
.hjm-chart__pie, .hjm-chart__radar { position: relative; align-self: center; }
|
|
1328
|
+
.hjm-chart__pie-center { position: absolute; inset: 0; display: grid; place-items: center; text-align: center; pointer-events: none; }
|
|
1329
|
+
.hjm-chart__slice[role="button"], .hjm-chart--pie .hjm-chart__slice[data-id] { cursor: pointer; }
|
|
1330
|
+
.hjm-chart__slice:focus-visible { outline: none; stroke: var(--hjm-color-focus); stroke-width: var(--hjm-focus-width); }
|
|
1331
|
+
.hjm-chart__slice-label { fill: #ffffff; font-family: inherit; font-size: var(--hjm-type-caption-size); font-weight: var(--hjm-font-weight-bold); }
|
|
1332
|
+
.hjm-chart__axis-titles { display: flex; justify-content: space-between; color: var(--hjm-chart-axis-label); font-size: var(--hjm-type-caption-size); line-height: var(--hjm-type-caption-line-height); }
|
|
1333
|
+
.hjm-chart-legend {
|
|
1334
|
+
display: flex;
|
|
1335
|
+
flex-wrap: wrap;
|
|
1336
|
+
gap: var(--hjm-space-xxs) var(--hjm-space-md);
|
|
1337
|
+
margin: 0;
|
|
1338
|
+
padding: 0;
|
|
1339
|
+
list-style: none;
|
|
1340
|
+
color: var(--hjm-chart-legend-label);
|
|
1341
|
+
font-size: var(--hjm-type-label-size);
|
|
1342
|
+
line-height: var(--hjm-type-label-line-height);
|
|
1343
|
+
}
|
|
1344
|
+
.hjm-chart-legend[data-interactive] { flex-direction: column; flex-wrap: nowrap; }
|
|
1345
|
+
.hjm-chart-legend__item, .hjm-chart-legend__button { display: flex; align-items: center; gap: var(--hjm-space-xs); }
|
|
1346
|
+
.hjm-chart-legend__item[data-dimmed] { opacity: 0.5; }
|
|
1347
|
+
.hjm-chart-legend__button {
|
|
1348
|
+
position: relative;
|
|
1349
|
+
flex: 1;
|
|
1350
|
+
min-block-size: var(--hjm-control-min-touch-target);
|
|
1351
|
+
padding: 0 var(--hjm-space-xs);
|
|
1352
|
+
border: 0;
|
|
1353
|
+
border-radius: var(--hjm-radius-sm);
|
|
1354
|
+
background: transparent;
|
|
1355
|
+
color: inherit;
|
|
1356
|
+
font: inherit;
|
|
1357
|
+
text-align: start;
|
|
1358
|
+
cursor: pointer;
|
|
1359
|
+
}
|
|
1360
|
+
.hjm-chart-legend__button[aria-pressed="true"] { background: var(--hjm-color-surface); }
|
|
1361
|
+
.hjm-chart-legend__button:focus-visible { outline: var(--hjm-focus-width) solid var(--hjm-color-focus); outline-offset: var(--hjm-focus-offset); }
|
|
1362
|
+
.hjm-chart-legend__swatch { flex: none; inline-size: 10px; block-size: 10px; border-radius: 3px; }
|
|
1363
|
+
.hjm-chart-legend[data-interactive] .hjm-chart-legend__label { flex: 1; }
|
|
1364
|
+
.hjm-chart-legend__value { font-weight: var(--hjm-font-weight-bold); font-variant-numeric: tabular-nums; }
|
|
1222
1365
|
|
|
1223
1366
|
/* Mentions ---------------------------------------------------------------- */
|
|
1224
1367
|
/*
|
|
@@ -2696,7 +2839,7 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
|
|
|
2696
2839
|
.hjm-date-picker__calendar-close:focus-visible { outline: 2px solid var(--hjm-color-focus); outline-offset: 2px; }
|
|
2697
2840
|
|
|
2698
2841
|
/* Reserve a common annotation row; dates with a dot must not sit above their neighbours. */
|
|
2699
|
-
.hjm-calendar__day-label { display: grid; place-items: center; inline-size: var(--hjm-calendar-cell-size); min-block-size: var(--hjm-calendar-cell-size); border: 1px solid transparent; border-radius: var(--hjm-radius-full); }
|
|
2842
|
+
.hjm-calendar__day-label { display: grid; place-items: center; inline-size: var(--hjm-calendar-indicator-size, var(--hjm-calendar-cell-size)); min-block-size: var(--hjm-calendar-indicator-size, var(--hjm-calendar-cell-size)); border: 1px solid transparent; border-radius: var(--hjm-radius-full); }
|
|
2700
2843
|
|
|
2701
2844
|
.hjm-calendar__day-label { font-size: var(--hjm-type-body-size); line-height: var(--hjm-type-body-line-height); }
|
|
2702
2845
|
.hjm-calendar[data-size="large"] .hjm-calendar__day-label { font-size: var(--hjm-type-body-large-size); line-height: var(--hjm-type-body-large-line-height); }
|
|
@@ -3028,5 +3171,10 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
|
|
|
3028
3171
|
.hjm-bottom-navigation[data-selection="tile"] .hjm-bottom-navigation__list { padding: var(--hjm-space-xxs); gap: var(--hjm-space-xxs); }
|
|
3029
3172
|
.hjm-bottom-navigation[data-selection="tile"] .hjm-bottom-navigation__item[data-state="selected"] { background: var(--hjm-color-primary); color: var(--hjm-color-on-primary); }
|
|
3030
3173
|
.hjm-bottom-navigation[data-selection="tile"] .hjm-bottom-navigation__item:not([data-state="selected"]):active { background: var(--hjm-bottom-navigation-pressed-background); }
|
|
3174
|
+
/* Raised plank (bottomNavigationRecipe.raisedSurface): lower edges replace the floating shadow.
|
|
3175
|
+
Every tile reserves its edge so selection does not shift rows; the selected edge darkens the brand fill. */
|
|
3176
|
+
.hjm-bottom-navigation[data-surface="raised"] .hjm-bottom-navigation__surface { background: var(--hjm-color-surface-alt); border-color: var(--hjm-color-border-control); border-block-end-width: 4px; box-shadow: none; }
|
|
3177
|
+
.hjm-bottom-navigation[data-surface="raised"] .hjm-bottom-navigation__item { border-block-end: 3px solid transparent; }
|
|
3178
|
+
.hjm-bottom-navigation[data-surface="raised"] .hjm-bottom-navigation__item[data-state="selected"] { border-block-end-color: color-mix(in srgb, var(--hjm-color-text) 45%, transparent); }
|
|
3031
3179
|
|
|
3032
3180
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
import type { ColorReference } from "@hjmds/design-contracts/color-references";
|
|
3
|
+
import { type ComparisonRow, type DataTableCellHeat, type DataTableCellTone, type LineScoreTeam, type MatrixCell } from "@hjmds/design-contracts/components/data-table";
|
|
4
|
+
import type { HjmCompositionStyleProp } from "./composition-style.js";
|
|
5
|
+
/** Recipe colors resolve through the provider's CSS variables so a theme switch repaints without re-rendering (same as Chart). */
|
|
6
|
+
export declare function tableCssColor(reference: ColorReference): string;
|
|
7
|
+
export type TableHeatColors = Readonly<{
|
|
8
|
+
high: string;
|
|
9
|
+
low: string;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Cell emphasis as inline style. Heat paints a background *image* so pinned cells keep their opaque
|
|
13
|
+
* canvas color underneath and scrolled content never shows through the translucent tint.
|
|
14
|
+
*/
|
|
15
|
+
export declare function tableCellEmphasis(decoration: Readonly<{
|
|
16
|
+
tone?: DataTableCellTone | null;
|
|
17
|
+
heat?: DataTableCellHeat | null;
|
|
18
|
+
}> | null | undefined, heatColors?: TableHeatColors): Readonly<{
|
|
19
|
+
style: CSSProperties;
|
|
20
|
+
tone?: DataTableCellTone;
|
|
21
|
+
heat?: string;
|
|
22
|
+
}>;
|
|
23
|
+
export type LineScoreProps = Readonly<{
|
|
24
|
+
teams: readonly LineScoreTeam[];
|
|
25
|
+
/** Total column headers in order, e.g. ["R", "H", "E", "B"]. The first one decides the leader. */
|
|
26
|
+
totals: readonly string[];
|
|
27
|
+
/** Live period (1-based) to highlight. */
|
|
28
|
+
currentPeriod?: number | null;
|
|
29
|
+
minPeriods?: number;
|
|
30
|
+
/** Optional leading content per team (logo/badge). */
|
|
31
|
+
renderTeam?: (team: LineScoreTeam) => ReactNode;
|
|
32
|
+
/** Accessible name of the table (prop name kept for Native parity). */
|
|
33
|
+
accessibilityLabel: string;
|
|
34
|
+
className?: string;
|
|
35
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function LineScore({ teams, totals, currentPeriod, minPeriods, renderTeam, accessibilityLabel, className, layoutStyle }: LineScoreProps): import("react").JSX.Element;
|
|
38
|
+
export type MatrixTableProps<Key extends string = string> = Readonly<{
|
|
39
|
+
ids: readonly Key[];
|
|
40
|
+
/** Short header per id (team short name). */
|
|
41
|
+
labelOf: (id: Key) => string;
|
|
42
|
+
getCell: (row: Key, column: Key) => MatrixCell | null;
|
|
43
|
+
/** Row id to highlight (my team). */
|
|
44
|
+
highlightId?: Key | null;
|
|
45
|
+
heatColors?: TableHeatColors;
|
|
46
|
+
accessibilityLabel: string;
|
|
47
|
+
className?: string;
|
|
48
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
49
|
+
}>;
|
|
50
|
+
export declare function MatrixTable<Key extends string = string>({ ids, labelOf, getCell, highlightId, heatColors, accessibilityLabel, className, layoutStyle }: MatrixTableProps<Key>): import("react").JSX.Element;
|
|
51
|
+
export type ComparisonTableProps = Readonly<{
|
|
52
|
+
rows: readonly ComparisonRow[];
|
|
53
|
+
/** Side names and accent colors (team colors). */
|
|
54
|
+
sides: readonly [Readonly<{
|
|
55
|
+
name: string;
|
|
56
|
+
color: string;
|
|
57
|
+
}>, Readonly<{
|
|
58
|
+
name: string;
|
|
59
|
+
color: string;
|
|
60
|
+
}>];
|
|
61
|
+
accessibilityLabel: string;
|
|
62
|
+
className?: string;
|
|
63
|
+
layoutStyle?: HjmCompositionStyleProp;
|
|
64
|
+
}>;
|
|
65
|
+
export declare function ComparisonTable({ rows, sides, accessibilityLabel, className, layoutStyle }: ComparisonTableProps): import("react").JSX.Element;
|
|
66
|
+
//# sourceMappingURL=table-family.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-family.d.ts","sourceRoot":"","sources":["../src/table-family.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { dataTableFamilyRecipe, dataTableRecipe, formatLineScoreCell, resolveComparisonRow, resolveLineScore, resolveMatrix, } from "@hjmds/design-contracts/components/data-table";
|
|
3
|
+
import { classNames } from "./internal.js";
|
|
4
|
+
/*
|
|
5
|
+
* Table family companions (2026-10-14): layouts products kept hand-building next to DataTable.
|
|
6
|
+
* LineScore follows MLB/ESPN game headers, MatrixTable a head-to-head cross table, ComparisonTable
|
|
7
|
+
* the ESPN matchup rows. Geometry and rules live in the data-table contract; these only draw.
|
|
8
|
+
*/
|
|
9
|
+
function kebab(value) {
|
|
10
|
+
return value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
11
|
+
}
|
|
12
|
+
/** Recipe colors resolve through the provider's CSS variables so a theme switch repaints without re-rendering (same as Chart). */
|
|
13
|
+
export function tableCssColor(reference) {
|
|
14
|
+
const name = reference.source === "theme"
|
|
15
|
+
? `--hjm-color-${kebab(reference.key)}`
|
|
16
|
+
: reference.source === "accent" ? `--hjm-accent-${kebab(reference.key)}` : `--hjm-accent-fill-${kebab(reference.key)}`;
|
|
17
|
+
return reference.alpha === undefined || reference.alpha === 1
|
|
18
|
+
? `var(${name})`
|
|
19
|
+
: `color-mix(in srgb, var(${name}) ${Math.round(reference.alpha * 100)}%, transparent)`;
|
|
20
|
+
}
|
|
21
|
+
/** Live period and "my team" row reuse the shared selected-row tint. */
|
|
22
|
+
const familyStyle = { "--hjm-table-family-selected": tableCssColor(dataTableRecipe.row.selectedBackground) };
|
|
23
|
+
const withFamilyStyle = (layoutStyle) => ({ ...familyStyle, ...layoutStyle });
|
|
24
|
+
/**
|
|
25
|
+
* Cell emphasis as inline style. Heat paints a background *image* so pinned cells keep their opaque
|
|
26
|
+
* canvas color underneath and scrolled content never shows through the translucent tint.
|
|
27
|
+
*/
|
|
28
|
+
export function tableCellEmphasis(decoration, heatColors) {
|
|
29
|
+
if (!decoration)
|
|
30
|
+
return { style: {} };
|
|
31
|
+
const style = {};
|
|
32
|
+
const tone = decoration.tone ?? undefined, heat = decoration.heat ?? null;
|
|
33
|
+
if (tone) {
|
|
34
|
+
style.color = tableCssColor(dataTableFamilyRecipe.tone[tone].color);
|
|
35
|
+
style.fontWeight = dataTableFamilyRecipe.tone[tone].fontWeight;
|
|
36
|
+
}
|
|
37
|
+
if (heat) {
|
|
38
|
+
const base = heatColors?.[heat.side] ?? tableCssColor(dataTableFamilyRecipe.heat[heat.side]);
|
|
39
|
+
style["--hjm-data-table-heat"] = `color-mix(in srgb, ${base} ${Math.round(dataTableFamilyRecipe.heatAlpha[heat.strength] * 100)}%, transparent)`;
|
|
40
|
+
}
|
|
41
|
+
return { style: style, ...(tone ? { tone } : {}), ...(heat ? { heat: `${heat.side}-${heat.strength}` } : {}) };
|
|
42
|
+
}
|
|
43
|
+
export function LineScore({ teams, totals, currentPeriod = null, minPeriods, renderTeam, accessibilityLabel, className, layoutStyle }) {
|
|
44
|
+
const score = resolveLineScore(teams, { totals, currentPeriod, ...(minPeriods !== undefined ? { minPeriods } : {}) });
|
|
45
|
+
const periods = score.columns.filter((column) => column.kind === "period");
|
|
46
|
+
const totalColumns = score.columns.filter((column) => column.kind === "total");
|
|
47
|
+
return (_jsx("div", { className: classNames("hjm-table-family", "hjm-line-score", className), style: withFamilyStyle(layoutStyle), children: _jsxs("table", { className: "hjm-line-score__table", "aria-label": accessibilityLabel, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "hjm-line-score__team" }), periods.map((column, index) => {
|
|
48
|
+
const live = score.currentPeriod === index + 1;
|
|
49
|
+
return _jsx("th", { scope: "col", className: "hjm-line-score__period", "aria-current": live ? "true" : undefined, "data-current": live || undefined, children: column.header }, column.id);
|
|
50
|
+
}), totalColumns.map((column, index) => (_jsx("th", { scope: "col", className: classNames("hjm-line-score__total", index === 0 && "hjm-line-score__total-edge"), children: column.header }, column.id)))] }) }), _jsx("tbody", { children: teams.map((team) => (_jsxs("tr", { "data-leader": score.leaderId === team.id || undefined, children: [_jsx("th", { scope: "row", className: "hjm-line-score__team", children: _jsxs("span", { className: "hjm-line-score__team-content", children: [renderTeam?.(team), _jsx("span", { children: team.name })] }) }), periods.map((column, index) => {
|
|
51
|
+
const value = team.periods[index];
|
|
52
|
+
return _jsx("td", { className: "hjm-line-score__period", "data-current": score.currentPeriod === index + 1 || undefined, "data-scored": typeof value === "number" && value > 0 || undefined, children: formatLineScoreCell(value) }, column.id);
|
|
53
|
+
}), totalColumns.map((column, index) => (_jsx("td", { className: classNames("hjm-line-score__total", index === 0 && "hjm-line-score__total-edge"), children: team.totals[column.header] ?? "—" }, column.id)))] }, team.id))) })] }) }));
|
|
54
|
+
}
|
|
55
|
+
export function MatrixTable({ ids, labelOf, getCell, highlightId = null, heatColors, accessibilityLabel, className, layoutStyle }) {
|
|
56
|
+
const matrix = resolveMatrix(ids, getCell);
|
|
57
|
+
return (_jsx("div", { className: classNames("hjm-table-family", "hjm-matrix-table", className), style: withFamilyStyle(layoutStyle), children: _jsxs("table", { className: "hjm-matrix-table__table", "aria-label": accessibilityLabel, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "hjm-matrix-table__corner" }), ids.map((id) => _jsx("th", { scope: "col", children: labelOf(id) }, id))] }) }), _jsx("tbody", { children: matrix.map((cells, rowIndex) => {
|
|
58
|
+
const rowId = ids[rowIndex];
|
|
59
|
+
return (_jsxs("tr", { "data-highlighted": rowId === highlightId || undefined, children: [_jsx("th", { scope: "row", children: labelOf(rowId) }), cells.map((cell, columnIndex) => {
|
|
60
|
+
// The diagonal is a team against itself: blank and hidden, not "no record".
|
|
61
|
+
if (cell === "self")
|
|
62
|
+
return _jsx("td", { className: "hjm-matrix-table__self", "aria-hidden": "true" }, ids[columnIndex]);
|
|
63
|
+
const emphasis = tableCellEmphasis(cell, heatColors);
|
|
64
|
+
return _jsx("td", { className: emphasis.heat ? "hjm-table-family__heat" : undefined, style: emphasis.style, "data-tone": emphasis.tone, "data-heat": emphasis.heat, children: cell?.label ?? "—" }, ids[columnIndex]);
|
|
65
|
+
})] }, rowId));
|
|
66
|
+
}) })] }) }));
|
|
67
|
+
}
|
|
68
|
+
export function ComparisonTable({ rows, sides, accessibilityLabel, className, layoutStyle }) {
|
|
69
|
+
return (_jsxs("section", { className: classNames("hjm-table-family", "hjm-comparison-table", className), style: withFamilyStyle(layoutStyle), "aria-label": accessibilityLabel, children: [_jsx("div", { className: "hjm-comparison-table__sides", "aria-hidden": "true", children: sides.map((side, index) => _jsx("span", { style: { color: side.color }, children: side.name }, index)) }), _jsx("ul", { className: "hjm-comparison-table__rows", role: "list", children: rows.map((row) => {
|
|
70
|
+
const { winner, shares } = resolveComparisonRow(row);
|
|
71
|
+
const shown = row.display ?? [row.values[0] === null ? "—" : String(row.values[0]), row.values[1] === null ? "—" : String(row.values[1])];
|
|
72
|
+
return (_jsxs("li", { className: "hjm-comparison-table__row", "data-winner": winner ?? undefined, children: [_jsx("span", { className: "hjm-visually-hidden", children: `${row.label} ${sides[0].name} ${shown[0]}, ${sides[1].name} ${shown[1]}` }), _jsxs("div", { className: "hjm-comparison-table__values", "aria-hidden": "true", children: [_jsx("span", { "data-win": winner === 0 || undefined, children: shown[0] }), _jsx("span", { className: "hjm-comparison-table__label", children: row.label }), _jsx("span", { "data-win": winner === 1 || undefined, children: shown[1] })] }), _jsx("div", { className: "hjm-comparison-table__bars", "aria-hidden": "true", children: [0, 1].map((index) => (_jsx("span", { className: "hjm-comparison-table__track", "data-side": index, children: _jsx("span", { className: "hjm-comparison-table__bar", style: { inlineSize: `${Math.round(shares[index] * 1000) / 10}%`, background: sides[index].color, opacity: winner === null || winner === index ? 1 : 0.35 } }) }, index))) })] }, row.id));
|
|
73
|
+
}) })] }));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=table-family.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-family.js","sourceRoot":"","sources":["../src/table-family.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CSSProperties, ReactNode } from \"react\";\nimport type { ColorReference } from \"@hjmds/design-contracts/color-references\";\nimport {\n dataTableFamilyRecipe, dataTableRecipe, formatLineScoreCell, resolveComparisonRow, resolveLineScore, resolveMatrix,\n type ComparisonRow, type DataTableCellHeat, type DataTableCellTone, type LineScoreTeam, type MatrixCell,\n} from \"@hjmds/design-contracts/components/data-table\";\nimport { classNames } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\n/*\n * Table family companions (2026-10-14): layouts products kept hand-building next to DataTable.\n * LineScore follows MLB/ESPN game headers, MatrixTable a head-to-head cross table, ComparisonTable\n * the ESPN matchup rows. Geometry and rules live in the data-table contract; these only draw.\n */\n\nfunction kebab(value: string): string {\n return value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n}\n\n/** Recipe colors resolve through the provider's CSS variables so a theme switch repaints without re-rendering (same as Chart). */\nexport function tableCssColor(reference: ColorReference): string {\n const name = reference.source === \"theme\"\n ? `--hjm-color-${kebab(reference.key)}`\n : reference.source === \"accent\" ? `--hjm-accent-${kebab(reference.key)}` : `--hjm-accent-fill-${kebab(reference.key)}`;\n return reference.alpha === undefined || reference.alpha === 1\n ? `var(${name})`\n : `color-mix(in srgb, var(${name}) ${Math.round(reference.alpha * 100)}%, transparent)`;\n}\n\n/** Live period and \"my team\" row reuse the shared selected-row tint. */\nconst familyStyle = { \"--hjm-table-family-selected\": tableCssColor(dataTableRecipe.row.selectedBackground) } as CSSProperties;\nconst withFamilyStyle = (layoutStyle: HjmCompositionStyleProp | undefined) => ({ ...familyStyle, ...layoutStyle }) as CSSProperties;\n\nexport type TableHeatColors = Readonly<{ high: string; low: string }>;\n\n/**\n * Cell emphasis as inline style. Heat paints a background *image* so pinned cells keep their opaque\n * canvas color underneath and scrolled content never shows through the translucent tint.\n */\nexport function tableCellEmphasis(\n decoration: Readonly<{ tone?: DataTableCellTone | null; heat?: DataTableCellHeat | null }> | null | undefined,\n heatColors?: TableHeatColors,\n): Readonly<{ style: CSSProperties; tone?: DataTableCellTone; heat?: string }> {\n if (!decoration) return { style: {} };\n const style: Record<string, string | number> = {};\n const tone = decoration.tone ?? undefined, heat = decoration.heat ?? null;\n if (tone) {\n style.color = tableCssColor(dataTableFamilyRecipe.tone[tone].color);\n style.fontWeight = dataTableFamilyRecipe.tone[tone].fontWeight;\n }\n if (heat) {\n const base = heatColors?.[heat.side] ?? tableCssColor(dataTableFamilyRecipe.heat[heat.side]);\n style[\"--hjm-data-table-heat\"] = `color-mix(in srgb, ${base} ${Math.round(dataTableFamilyRecipe.heatAlpha[heat.strength] * 100)}%, transparent)`;\n }\n return { style: style as CSSProperties, ...(tone ? { tone } : {}), ...(heat ? { heat: `${heat.side}-${heat.strength}` } : {}) };\n}\n\n/* ---------------------------------------------- Line score ------------------------------------- */\n\nexport type LineScoreProps = Readonly<{\n teams: readonly LineScoreTeam[];\n /** Total column headers in order, e.g. [\"R\", \"H\", \"E\", \"B\"]. The first one decides the leader. */\n totals: readonly string[];\n /** Live period (1-based) to highlight. */\n currentPeriod?: number | null;\n minPeriods?: number;\n /** Optional leading content per team (logo/badge). */\n renderTeam?: (team: LineScoreTeam) => ReactNode;\n /** Accessible name of the table (prop name kept for Native parity). */\n accessibilityLabel: string;\n className?: string;\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nexport function LineScore({ teams, totals, currentPeriod = null, minPeriods, renderTeam, accessibilityLabel, className, layoutStyle }: LineScoreProps) {\n const score = resolveLineScore(teams, { totals, currentPeriod, ...(minPeriods !== undefined ? { minPeriods } : {}) });\n const periods = score.columns.filter((column) => column.kind === \"period\");\n const totalColumns = score.columns.filter((column) => column.kind === \"total\");\n return (\n <div className={classNames(\"hjm-table-family\", \"hjm-line-score\", className)} style={withFamilyStyle(layoutStyle)}>\n <table className=\"hjm-line-score__table\" aria-label={accessibilityLabel}>\n <thead>\n <tr>\n <td className=\"hjm-line-score__team\" />\n {periods.map((column, index) => {\n const live = score.currentPeriod === index + 1;\n return <th key={column.id} scope=\"col\" className=\"hjm-line-score__period\" aria-current={live ? \"true\" : undefined} data-current={live || undefined}>{column.header}</th>;\n })}\n {totalColumns.map((column, index) => (\n <th key={column.id} scope=\"col\" className={classNames(\"hjm-line-score__total\", index === 0 && \"hjm-line-score__total-edge\")}>{column.header}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {teams.map((team) => (\n <tr key={team.id} data-leader={score.leaderId === team.id || undefined}>\n <th scope=\"row\" className=\"hjm-line-score__team\">\n <span className=\"hjm-line-score__team-content\">{renderTeam?.(team)}<span>{team.name}</span></span>\n </th>\n {periods.map((column, index) => {\n const value = team.periods[index];\n return <td key={column.id} className=\"hjm-line-score__period\" data-current={score.currentPeriod === index + 1 || undefined} data-scored={typeof value === \"number\" && value > 0 || undefined}>{formatLineScoreCell(value)}</td>;\n })}\n {totalColumns.map((column, index) => (\n <td key={column.id} className={classNames(\"hjm-line-score__total\", index === 0 && \"hjm-line-score__total-edge\")}>{team.totals[column.header] ?? \"—\"}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\n/* ---------------------------------------------- Matrix table ----------------------------------- */\n\nexport type MatrixTableProps<Key extends string = string> = Readonly<{\n ids: readonly Key[];\n /** Short header per id (team short name). */\n labelOf: (id: Key) => string;\n getCell: (row: Key, column: Key) => MatrixCell | null;\n /** Row id to highlight (my team). */\n highlightId?: Key | null;\n heatColors?: TableHeatColors;\n accessibilityLabel: string;\n className?: string;\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nexport function MatrixTable<Key extends string = string>({ ids, labelOf, getCell, highlightId = null, heatColors, accessibilityLabel, className, layoutStyle }: MatrixTableProps<Key>) {\n const matrix = resolveMatrix(ids, getCell);\n return (\n <div className={classNames(\"hjm-table-family\", \"hjm-matrix-table\", className)} style={withFamilyStyle(layoutStyle)}>\n <table className=\"hjm-matrix-table__table\" aria-label={accessibilityLabel}>\n <thead>\n <tr>\n <td className=\"hjm-matrix-table__corner\" />\n {ids.map((id) => <th key={id} scope=\"col\">{labelOf(id)}</th>)}\n </tr>\n </thead>\n <tbody>\n {matrix.map((cells, rowIndex) => {\n const rowId = ids[rowIndex]!;\n return (\n <tr key={rowId} data-highlighted={rowId === highlightId || undefined}>\n <th scope=\"row\">{labelOf(rowId)}</th>\n {cells.map((cell, columnIndex) => {\n // The diagonal is a team against itself: blank and hidden, not \"no record\".\n if (cell === \"self\") return <td key={ids[columnIndex]} className=\"hjm-matrix-table__self\" aria-hidden=\"true\" />;\n const emphasis = tableCellEmphasis(cell, heatColors);\n return <td key={ids[columnIndex]} className={emphasis.heat ? \"hjm-table-family__heat\" : undefined} style={emphasis.style} data-tone={emphasis.tone} data-heat={emphasis.heat}>{cell?.label ?? \"—\"}</td>;\n })}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n\n/* ---------------------------------------------- Comparison table ------------------------------- */\n\nexport type ComparisonTableProps = Readonly<{\n rows: readonly ComparisonRow[];\n /** Side names and accent colors (team colors). */\n sides: readonly [Readonly<{ name: string; color: string }>, Readonly<{ name: string; color: string }>];\n accessibilityLabel: string;\n className?: string;\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nexport function ComparisonTable({ rows, sides, accessibilityLabel, className, layoutStyle }: ComparisonTableProps) {\n return (\n <section className={classNames(\"hjm-table-family\", \"hjm-comparison-table\", className)} style={withFamilyStyle(layoutStyle)} aria-label={accessibilityLabel}>\n <div className=\"hjm-comparison-table__sides\" aria-hidden=\"true\">\n {sides.map((side, index) => <span key={index} style={{ color: side.color }}>{side.name}</span>)}\n </div>\n <ul className=\"hjm-comparison-table__rows\" role=\"list\">\n {rows.map((row) => {\n const { winner, shares } = resolveComparisonRow(row);\n const shown = row.display ?? [row.values[0] === null ? \"—\" : String(row.values[0]), row.values[1] === null ? \"—\" : String(row.values[1])];\n return (\n <li key={row.id} className=\"hjm-comparison-table__row\" data-winner={winner ?? undefined}>\n {/* One sentence for assistive tech; the visual grid and bars are decoration. */}\n <span className=\"hjm-visually-hidden\">{`${row.label} ${sides[0].name} ${shown[0]}, ${sides[1].name} ${shown[1]}`}</span>\n <div className=\"hjm-comparison-table__values\" aria-hidden=\"true\">\n <span data-win={winner === 0 || undefined}>{shown[0]}</span>\n <span className=\"hjm-comparison-table__label\">{row.label}</span>\n <span data-win={winner === 1 || undefined}>{shown[1]}</span>\n </div>\n <div className=\"hjm-comparison-table__bars\" aria-hidden=\"true\">\n {([0, 1] as const).map((index) => (\n <span key={index} className=\"hjm-comparison-table__track\" data-side={index}>\n <span\n className=\"hjm-comparison-table__bar\"\n style={{ inlineSize: `${Math.round(shares[index] * 1000) / 10}%`, background: sides[index].color, opacity: winner === null || winner === index ? 1 : 0.35 }}\n />\n </span>\n ))}\n </div>\n </li>\n );\n })}\n </ul>\n </section>\n );\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { resolvePasswordRules, type TaskChoice, type PasswordRule, type PriceOption } from "@hjmds/design-contracts/task-controls";
|
|
2
|
+
import { type PasswordFieldProps } from "./forms.js";
|
|
3
|
+
export type SplitActionProps = Readonly<{
|
|
4
|
+
modes: readonly TaskChoice[];
|
|
5
|
+
modeId: string;
|
|
6
|
+
onModeChange: (id: string) => void;
|
|
7
|
+
onExecute: (modeId: string) => void | Promise<void>;
|
|
8
|
+
executeLabel: (mode: TaskChoice) => string;
|
|
9
|
+
menuLabel: string;
|
|
10
|
+
dismissLabel: string;
|
|
11
|
+
errorLabel: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function SplitAction(props: SplitActionProps): import("react").JSX.Element;
|
|
15
|
+
export type AccountSwitcherProps = Readonly<{
|
|
16
|
+
accounts: readonly TaskChoice[];
|
|
17
|
+
accountId: string;
|
|
18
|
+
label: string;
|
|
19
|
+
dismissLabel: string;
|
|
20
|
+
errorLabel: string;
|
|
21
|
+
onSwitch: (accountId: string) => void | Promise<void>;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function AccountSwitcher(props: AccountSwitcherProps): import("react").JSX.Element;
|
|
25
|
+
export type PasswordRulesFieldProps = Omit<PasswordFieldProps, "value" | "defaultValue" | "onValueChange"> & Readonly<{
|
|
26
|
+
label: string;
|
|
27
|
+
value: string;
|
|
28
|
+
onValueChange: (value: string) => void;
|
|
29
|
+
rules: readonly PasswordRule[];
|
|
30
|
+
progressLabel: string;
|
|
31
|
+
summaryLabel: (satisfied: number, total: number) => string;
|
|
32
|
+
ruleLabel: (rule: ReturnType<typeof resolvePasswordRules>["rules"][number]) => string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function PasswordRulesField({ rules, progressLabel, summaryLabel, ruleLabel, ...field }: PasswordRulesFieldProps): import("react").JSX.Element;
|
|
35
|
+
export type PricingChoiceProps = Readonly<{
|
|
36
|
+
label: string;
|
|
37
|
+
options: readonly PriceOption[];
|
|
38
|
+
selectedId: string;
|
|
39
|
+
onSelectionChange: (id: string) => void;
|
|
40
|
+
formatPrice: (option: PriceOption) => string;
|
|
41
|
+
continueLabel: string;
|
|
42
|
+
errorLabel: string;
|
|
43
|
+
onContinue: (option: PriceOption) => void | Promise<void>;
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
export declare function PricingChoice(props: PricingChoiceProps): import("react").JSX.Element;
|
|
47
|
+
//# sourceMappingURL=task-controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-controls.d.ts","sourceRoot":"","sources":["../src/task-controls.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqB,oBAAoB,EAAsB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAG1K,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkBpE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjF,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,YAAY,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAAC;IAChG,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACjF,CAAC,CAAC;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAOlD;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAC5G,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC3E,CAAC,CAAC;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BAO1D;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,cAAc,GAAG,eAAe,CAAC,GAAG,QAAQ,CAAC;IACpH,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACrG,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAClF,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC;CACvF,CAAC,CAAC;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,+BAMtH;AAED,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5G,WAAW,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IACxF,UAAU,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC/E,CAAC,CAAC;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,+BAUtD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { resolveTaskChoice, resolvePasswordRules, resolvePriceOption } from "@hjmds/design-contracts/task-controls";
|
|
4
|
+
import { Button } from "./actions.js";
|
|
5
|
+
import { Menu } from "./overlays.js";
|
|
6
|
+
import { PasswordField } from "./forms.js";
|
|
7
|
+
import { RadioGroup } from "./selection.js";
|
|
8
|
+
import { Card } from "./display.js";
|
|
9
|
+
import { Progress } from "./feedback.js";
|
|
10
|
+
import { Stack, Text } from "./layout.js";
|
|
11
|
+
import { useCompositionAction } from "./internal/use-composition-action.js";
|
|
12
|
+
function ChoiceMenu(props) {
|
|
13
|
+
const available = props.choices.some(choice => !choice.disabled);
|
|
14
|
+
if (!available)
|
|
15
|
+
return _jsx(Button, { disabled: true, children: props.label });
|
|
16
|
+
return _jsx(Menu, { label: props.label, trigger: _jsx(Button, { tone: "secondary", loading: props.pending === true, children: props.label }), disabled: props.disabled, items: props.choices, selectionMode: "single", value: props.selectedId, onValueChange: () => undefined, onActionAfterDismiss: props.onChoose });
|
|
17
|
+
}
|
|
18
|
+
function Failure({ failed, label }) {
|
|
19
|
+
return failed ? _jsx(Text, { role: "alert", tone: "danger", style: { textAlign: "center" }, children: label }) : null;
|
|
20
|
+
}
|
|
21
|
+
export function SplitAction(props) {
|
|
22
|
+
const mode = resolveTaskChoice(props.modes, props.modeId), action = useCompositionAction(props.disabled);
|
|
23
|
+
const locked = props.disabled === true || action.pending;
|
|
24
|
+
return _jsxs(Stack, { gap: "sm", children: [_jsxs(Stack, { axis: "inline", gap: "xs", wrap: true, children: [_jsx(Button, { loading: action.pending, disabled: props.disabled === true || mode.disabled === true, onClick: () => { if (!mode.disabled)
|
|
25
|
+
void action.run(mode.id, () => props.onExecute(mode.id)); }, children: props.executeLabel(mode) }), _jsx(ChoiceMenu, { choices: props.modes, selectedId: mode.id, label: props.menuLabel, dismissLabel: props.dismissLabel, disabled: locked, onChoose: id => { if (!locked && !resolveTaskChoice(props.modes, id).disabled)
|
|
26
|
+
props.onModeChange(id); } })] }), _jsx(Failure, { failed: action.failed, label: props.errorLabel })] });
|
|
27
|
+
}
|
|
28
|
+
export function AccountSwitcher(props) {
|
|
29
|
+
const account = resolveTaskChoice(props.accounts, props.accountId), action = useCompositionAction(props.disabled);
|
|
30
|
+
const locked = props.disabled === true || action.pending;
|
|
31
|
+
// The controlled account stays confirmed until the product completes the session change.
|
|
32
|
+
return _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { emphasis: "strong", children: account.label }), _jsx(ChoiceMenu, { choices: props.accounts, selectedId: account.id, label: props.label, dismissLabel: props.dismissLabel, disabled: locked, pending: action.pending, onChoose: id => { if (id !== account.id && !resolveTaskChoice(props.accounts, id).disabled)
|
|
33
|
+
void action.run(id, () => props.onSwitch(id)); } }), _jsx(Failure, { failed: action.failed, label: props.errorLabel })] });
|
|
34
|
+
}
|
|
35
|
+
export function PasswordRulesField({ rules, progressLabel, summaryLabel, ruleLabel, ...field }) {
|
|
36
|
+
const state = resolvePasswordRules(field.value, rules);
|
|
37
|
+
return _jsxs(Stack, { gap: "sm", children: [_jsx(PasswordField, { ...field }), _jsx(Progress, { label: progressLabel, value: state.satisfied, max: state.total, valueText: summaryLabel(state.satisfied, state.total) }), state.rules.map(rule => _jsx(Text, { variant: "caption", tone: rule.satisfied ? "primary" : "muted", children: ruleLabel(rule) }, rule.id))] });
|
|
38
|
+
}
|
|
39
|
+
export function PricingChoice(props) {
|
|
40
|
+
const option = resolvePriceOption(props.options, props.selectedId), action = useCompositionAction(props.disabled);
|
|
41
|
+
const locked = props.disabled === true || action.pending;
|
|
42
|
+
return _jsx(Card, { title: props.label, children: _jsxs(Stack, { gap: "md", children: [_jsx(RadioGroup, { accessibilityLabel: props.label, presentation: "card", value: option.id, disabled: locked, items: props.options.map(price => ({ value: price.id, label: price.label, description: props.formatPrice(price), disabled: price.disabled === true })), onValueChange: id => { if (id !== null && !locked && !resolveTaskChoice(props.options, id).disabled)
|
|
43
|
+
props.onSelectionChange(id); } }), _jsx(Text, { emphasis: "strong", children: props.formatPrice(option) }), option.benefits.map((benefit, index) => _jsx(Text, { children: benefit }, index)), _jsx(Button, { loading: action.pending, disabled: props.disabled === true || option.disabled === true, onClick: () => { if (!option.disabled)
|
|
44
|
+
void action.run(option.id, () => props.onContinue(Object.freeze({ ...option, benefits: Object.freeze([...option.benefits]) }))); }, children: props.continueLabel }), _jsx(Failure, { failed: action.failed, label: props.errorLabel })] }) });
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=task-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-controls.js","sourceRoot":"","sources":["../src/task-controls.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { resolveTaskChoice, resolvePasswordRules, resolvePriceOption, type TaskChoice, type PasswordRule, type PriceOption } from \"@hjmds/design-contracts/task-controls\";\nimport { Button } from \"./actions.js\";\nimport { Menu } from \"./overlays.js\";\nimport { PasswordField, type PasswordFieldProps } from \"./forms.js\";\nimport { RadioGroup } from \"./selection.js\";\nimport { Card } from \"./display.js\";\nimport { Progress } from \"./feedback.js\";\nimport { Stack, Text } from \"./layout.js\";\nimport { useCompositionAction } from \"./internal/use-composition-action.js\";\n\ntype ChoiceMenuProps = Readonly<{ choices: readonly TaskChoice[]; selectedId: string; label: string; dismissLabel: string; disabled: boolean; pending?: boolean; onChoose: (id: string) => void }>;\nfunction ChoiceMenu(props: ChoiceMenuProps) {\n const available = props.choices.some(choice => !choice.disabled);\n if (!available) return <Button disabled>{props.label}</Button>;\n return <Menu label={props.label} trigger={<Button tone=\"secondary\" loading={props.pending === true}>{props.label}</Button>} disabled={props.disabled}\n items={props.choices} selectionMode=\"single\" value={props.selectedId} onValueChange={() => undefined} onActionAfterDismiss={props.onChoose} />;\n}\nfunction Failure({ failed, label }: Readonly<{ failed: boolean; label: string }>) {\n return failed ? <Text role=\"alert\" tone=\"danger\" style={{ textAlign: \"center\" }}>{label}</Text> : null;\n}\n\nexport type SplitActionProps = Readonly<{\n modes: readonly TaskChoice[]; modeId: string; onModeChange: (id: string) => void;\n onExecute: (modeId: string) => void | Promise<void>; executeLabel: (mode: TaskChoice) => string;\n menuLabel: string; dismissLabel: string; errorLabel: string; disabled?: boolean;\n}>;\nexport function SplitAction(props: SplitActionProps) {\n const mode = resolveTaskChoice(props.modes, props.modeId), action = useCompositionAction(props.disabled);\n const locked = props.disabled === true || action.pending;\n return <Stack gap=\"sm\"><Stack axis=\"inline\" gap=\"xs\" wrap>\n <Button loading={action.pending} disabled={props.disabled === true || mode.disabled === true} onClick={() => { if (!mode.disabled) void action.run(mode.id, () => props.onExecute(mode.id)); }}>{props.executeLabel(mode)}</Button>\n <ChoiceMenu choices={props.modes} selectedId={mode.id} label={props.menuLabel} dismissLabel={props.dismissLabel} disabled={locked} onChoose={id => { if (!locked && !resolveTaskChoice(props.modes, id).disabled) props.onModeChange(id); }} />\n </Stack><Failure failed={action.failed} label={props.errorLabel} /></Stack>;\n}\n\nexport type AccountSwitcherProps = Readonly<{\n accounts: readonly TaskChoice[]; accountId: string; label: string; dismissLabel: string; errorLabel: string;\n onSwitch: (accountId: string) => void | Promise<void>; disabled?: boolean;\n}>;\nexport function AccountSwitcher(props: AccountSwitcherProps) {\n const account = resolveTaskChoice(props.accounts, props.accountId), action = useCompositionAction(props.disabled);\n const locked = props.disabled === true || action.pending;\n // The controlled account stays confirmed until the product completes the session change.\n return <Stack gap=\"sm\"><Text emphasis=\"strong\">{account.label}</Text><ChoiceMenu choices={props.accounts} selectedId={account.id} label={props.label} dismissLabel={props.dismissLabel} disabled={locked} pending={action.pending}\n onChoose={id => { if (id !== account.id && !resolveTaskChoice(props.accounts, id).disabled) void action.run(id, () => props.onSwitch(id)); }} />\n <Failure failed={action.failed} label={props.errorLabel} /></Stack>;\n}\n\nexport type PasswordRulesFieldProps = Omit<PasswordFieldProps, \"value\" | \"defaultValue\" | \"onValueChange\"> & Readonly<{\n label: string; value: string; onValueChange: (value: string) => void; rules: readonly PasswordRule[];\n progressLabel: string; summaryLabel: (satisfied: number, total: number) => string;\n ruleLabel: (rule: ReturnType<typeof resolvePasswordRules>[\"rules\"][number]) => string;\n}>;\nexport function PasswordRulesField({ rules, progressLabel, summaryLabel, ruleLabel, ...field }: PasswordRulesFieldProps) {\n const state = resolvePasswordRules(field.value, rules);\n return <Stack gap=\"sm\"><PasswordField {...field} />\n <Progress label={progressLabel} value={state.satisfied} max={state.total} valueText={summaryLabel(state.satisfied, state.total)} />\n {state.rules.map(rule => <Text key={rule.id} variant=\"caption\" tone={rule.satisfied ? \"primary\" : \"muted\"}>{ruleLabel(rule)}</Text>)}\n </Stack>;\n}\n\nexport type PricingChoiceProps = Readonly<{\n label: string; options: readonly PriceOption[]; selectedId: string; onSelectionChange: (id: string) => void;\n formatPrice: (option: PriceOption) => string; continueLabel: string; errorLabel: string;\n onContinue: (option: PriceOption) => void | Promise<void>; disabled?: boolean;\n}>;\nexport function PricingChoice(props: PricingChoiceProps) {\n const option = resolvePriceOption(props.options, props.selectedId), action = useCompositionAction(props.disabled);\n const locked = props.disabled === true || action.pending;\n return <Card title={props.label}><Stack gap=\"md\"><RadioGroup accessibilityLabel={props.label} presentation=\"card\" value={option.id} disabled={locked}\n items={props.options.map(price => ({ value: price.id, label: price.label, description: props.formatPrice(price), disabled: price.disabled === true }))}\n onValueChange={id => { if (id !== null && !locked && !resolveTaskChoice(props.options, id).disabled) props.onSelectionChange(id); }} />\n <Text emphasis=\"strong\">{props.formatPrice(option)}</Text>\n {option.benefits.map((benefit, index) => <Text key={index}>{benefit}</Text>)}\n <Button loading={action.pending} disabled={props.disabled === true || option.disabled === true} onClick={() => { if (!option.disabled) void action.run(option.id, () => props.onContinue(Object.freeze({ ...option, benefits: Object.freeze([...option.benefits]) }))); }}>{props.continueLabel}</Button>\n <Failure failed={action.failed} label={props.errorLabel} /></Stack></Card>;\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type ToggleFeedbackEffect, type ToggleFeedbackState } from "@hjmds/design-contracts/toggle-feedback";
|
|
3
|
+
export type ToggleFeedbackButtonProps = Readonly<{
|
|
4
|
+
entityId: string;
|
|
5
|
+
state: ToggleFeedbackState;
|
|
6
|
+
effect: ToggleFeedbackEffect;
|
|
7
|
+
locale: string;
|
|
8
|
+
icon?: ReactNode;
|
|
9
|
+
renderIcon?: (state: Readonly<{
|
|
10
|
+
selected: boolean;
|
|
11
|
+
pending: boolean;
|
|
12
|
+
}>) => ReactNode;
|
|
13
|
+
labels: Readonly<{
|
|
14
|
+
select: string;
|
|
15
|
+
selected: string;
|
|
16
|
+
pending: string;
|
|
17
|
+
count: string;
|
|
18
|
+
error: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Update confirmed state from the real API snapshot before resolving. */
|
|
21
|
+
onSelectedChange: (selected: boolean) => void | Promise<void>;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
active?: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function ToggleFeedbackButton(props: ToggleFeedbackButtonProps): import("react").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=toggle-feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toggle-feedback.d.ts","sourceRoot":"","sources":["../src/toggle-feedback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1D,OAAO,EAAwG,KAAK,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AA0BpN,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC7G,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC;IACrF,MAAM,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtG,0EAA0E;IAC1E,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CACrG,CAAC,CAAC;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,+BAGpE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { resolveToggleFeedbackProposal, resolveToggleFeedbackFrame, toggleFeedbackPaths, toggleFeedbackRecipe } from "@hjmds/design-contracts/toggle-feedback";
|
|
5
|
+
import { Button } from "./actions.js";
|
|
6
|
+
import { AnimatedStatistic } from "./statistic-motion.js";
|
|
7
|
+
import { Stack, Text } from "./layout.js";
|
|
8
|
+
import { useHjmTheme } from "./provider.js";
|
|
9
|
+
import { useCompositionAction } from "./internal/use-composition-action.js";
|
|
10
|
+
function FeedbackGlyph({ effect, selected, active, icon }) {
|
|
11
|
+
const glyph = useRef(null), circle = useRef(null), rays = useRef(null), previous = useRef(selected);
|
|
12
|
+
const reduced = useHjmTheme().environment.reducedMotion;
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const entering = selected && !previous.current;
|
|
15
|
+
previous.current = selected;
|
|
16
|
+
let frame = 0, start;
|
|
17
|
+
const draw = (p) => { const f = resolveToggleFeedbackFrame(effect, p); glyph.current?.style.setProperty("transform", `translateY(${f.translateY}px) scale(${f.scale})`); circle.current?.setAttribute("r", String(f.radius)); circle.current?.setAttribute("opacity", effect === "ripple" ? String(f.opacity) : "0"); rays.current?.setAttribute("d", f.rays); rays.current?.setAttribute("opacity", effect === "burst" ? String(f.opacity) : "0"); };
|
|
18
|
+
const settle = () => { cancelAnimationFrame(frame); draw(1); };
|
|
19
|
+
const animate = (now) => { start ??= now; const p = Math.min(1, (now - start) / toggleFeedbackRecipe.duration); draw(p); if (p < 1)
|
|
20
|
+
frame = requestAnimationFrame(animate); };
|
|
21
|
+
// The old selected state consumes the event; environment changes never replay it.
|
|
22
|
+
if (!entering || reduced || !active || document.hidden)
|
|
23
|
+
settle();
|
|
24
|
+
else {
|
|
25
|
+
draw(0);
|
|
26
|
+
frame = requestAnimationFrame(animate);
|
|
27
|
+
}
|
|
28
|
+
const visibility = () => { if (document.hidden)
|
|
29
|
+
settle(); };
|
|
30
|
+
document.addEventListener("visibilitychange", visibility);
|
|
31
|
+
return () => { cancelAnimationFrame(frame); document.removeEventListener("visibilitychange", visibility); };
|
|
32
|
+
}, [selected, effect, active, reduced]);
|
|
33
|
+
const f = resolveToggleFeedbackFrame(effect, 1);
|
|
34
|
+
return _jsxs("span", { "aria-hidden": "true", style: { position: "relative", display: "inline-flex", width: toggleFeedbackRecipe.glyphSize, height: toggleFeedbackRecipe.glyphSize }, children: [_jsx("span", { ref: glyph, style: { display: "inline-flex" }, children: icon ?? _jsx("svg", { width: 24, height: 24, viewBox: "0 0 24 24", focusable: "false", children: _jsx("path", { d: toggleFeedbackPaths[effect], fill: selected ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinejoin: "round" }) }) }), _jsxs("svg", { "data-toggle-effect": effect, width: 40, height: 40, viewBox: "0 0 40 40", focusable: "false", style: { position: "absolute", left: -8, top: -8, pointerEvents: "none" }, fill: "none", stroke: "currentColor", strokeWidth: 1.7, children: [_jsx("circle", { ref: circle, cx: 20, cy: 20, r: f.radius, opacity: 0 }), _jsx("path", { ref: rays, d: f.rays, opacity: 0, strokeLinecap: "round" })] })] });
|
|
35
|
+
}
|
|
36
|
+
export function ToggleFeedbackButton(props) {
|
|
37
|
+
if (!props.entityId.trim())
|
|
38
|
+
throw new TypeError("Toggle feedback requires a stable entity id");
|
|
39
|
+
return _jsx(ToggleFeedbackBody, { ...props }, props.entityId);
|
|
40
|
+
}
|
|
41
|
+
function ToggleFeedbackBody(props) {
|
|
42
|
+
resolveToggleFeedbackProposal(props.state, props.state.selected);
|
|
43
|
+
const active = props.active !== false, action = useCompositionAction(props.disabled === true || !active), state = action.active ?? props.state;
|
|
44
|
+
const icon = props.renderIcon ? props.renderIcon({ selected: state.selected, pending: action.pending }) : props.icon;
|
|
45
|
+
return _jsxs(Stack, { gap: "sm", children: [_jsxs(Stack, { axis: "inline", gap: "md", align: "center", wrap: true, children: [_jsx(Button, { tone: "secondary", selected: state.selected, loading: action.pending, disabled: props.disabled === true || !active, "aria-label": action.pending ? props.labels.pending : undefined, leading: _jsx(FeedbackGlyph, { effect: props.effect, selected: state.selected, active: active, ...(icon === undefined ? {} : { icon }) }), onClick: () => { const proposal = resolveToggleFeedbackProposal(props.state, !props.state.selected); void action.run(proposal, () => props.onSelectedChange(proposal.selected)); }, children: state.selected ? props.labels.selected : props.labels.select }), _jsx(AnimatedStatistic, { density: "compact", descriptor: { id: "selection-count", label: props.labels.count }, value: state.count, locale: props.locale, animated: active })] }), action.failed ? _jsx(Text, { role: "alert", tone: "danger", style: { textAlign: "center" }, children: props.labels.error }) : null] });
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=toggle-feedback.js.map
|