@manakind/manuia-ui 0.2.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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/goal-card/goal-card.css +592 -0
- package/dist/goal-card/goal-card.d.ts +92 -0
- package/dist/goal-card/goal-card.d.ts.map +1 -0
- package/dist/goal-card/goal-card.js +66 -0
- package/dist/goal-card/goal-card.js.map +1 -0
- package/dist/goal-card/index.d.ts +3 -0
- package/dist/goal-card/index.d.ts.map +1 -0
- package/dist/goal-card/index.js +2 -0
- package/dist/goal-card/index.js.map +1 -0
- package/dist/goal-categories/goal-categories.css +51 -0
- package/dist/goal-categories/goal-categories.d.ts +55 -0
- package/dist/goal-categories/goal-categories.d.ts.map +1 -0
- package/dist/goal-categories/goal-categories.js +103 -0
- package/dist/goal-categories/goal-categories.js.map +1 -0
- package/dist/goal-categories/index.d.ts +3 -0
- package/dist/goal-categories/index.d.ts.map +1 -0
- package/dist/goal-categories/index.js +2 -0
- package/dist/goal-categories/index.js.map +1 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.css +10 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.d.ts +11 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.d.ts.map +1 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.js +34 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.js.map +1 -0
- package/dist/goal-tab-trigger/index.d.ts +3 -0
- package/dist/goal-tab-trigger/index.d.ts.map +1 -0
- package/dist/goal-tab-trigger/index.js +2 -0
- package/dist/goal-tab-trigger/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type IconSource } from '@manakind/ui-core';
|
|
2
|
+
import { type GoalCategory } from '../goal-categories';
|
|
3
|
+
import './goal-card.css';
|
|
4
|
+
/** Manuia's goal timeframes, longest horizon first. */
|
|
5
|
+
export type GoalType = '5 Year' | '1 Year' | 'Quarterly' | 'Monthly' | 'Weekly';
|
|
6
|
+
export type GoalStatus = 'Not Started' | 'In Progress' | 'Completed';
|
|
7
|
+
export interface GoalMilestone {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
completed: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface GoalCardProps {
|
|
13
|
+
/** Goal title. Rendered as the card's heading. */
|
|
14
|
+
title: string;
|
|
15
|
+
/** Optional supporting copy. */
|
|
16
|
+
description?: string;
|
|
17
|
+
/** Timeframe. Drives the card's visual tier. */
|
|
18
|
+
type: GoalType;
|
|
19
|
+
/** Progress status, shown as a badge. */
|
|
20
|
+
status: GoalStatus;
|
|
21
|
+
/**
|
|
22
|
+
* Glyph for this card's status, supplied by the product so this package
|
|
23
|
+
* never imports an icon set — same contract as `typeIcon`.
|
|
24
|
+
*
|
|
25
|
+
* Strongly recommended. The status badge collapses to the glyph alone on a
|
|
26
|
+
* narrow card, and without one it collapses to a bare colour chip, which
|
|
27
|
+
* says nothing to anyone who cannot separate the tints. Pass a glyph whose
|
|
28
|
+
* outline differs per status, not just its hue.
|
|
29
|
+
*/
|
|
30
|
+
statusIcon?: IconSource;
|
|
31
|
+
/** Categories this goal belongs to. */
|
|
32
|
+
categories?: GoalCategory[];
|
|
33
|
+
/** How many categories to show before collapsing into `+N more`. */
|
|
34
|
+
maxVisibleCategories?: number;
|
|
35
|
+
/** Milestones, rendered as a progress bar plus a checklist. */
|
|
36
|
+
milestones?: GoalMilestone[];
|
|
37
|
+
/** Pre-formatted target date. Formatting is the caller's job — this package takes no date dependency. */
|
|
38
|
+
targetDate?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Icon shown beside the timeframe badge.
|
|
41
|
+
*
|
|
42
|
+
* Supplied by the product so this package never imports an icon set. The
|
|
43
|
+
* Humanity Plus apps pass a Lineicons Pro component here.
|
|
44
|
+
*/
|
|
45
|
+
typeIcon?: IconSource;
|
|
46
|
+
/** Glyph for a completed milestone. Falls back to a filled dot. */
|
|
47
|
+
milestoneIcon?: IconSource;
|
|
48
|
+
/**
|
|
49
|
+
* Emblem shown on the higher tiers — the product uses a crown for 5 Year,
|
|
50
|
+
* a shield for 1 Year, and a compass for Quarterly.
|
|
51
|
+
*
|
|
52
|
+
* Purely decorative: it is `aria-hidden`, because the timeframe is already
|
|
53
|
+
* announced as text beside it. Monthly and Weekly carry no emblem, so pass
|
|
54
|
+
* nothing for those.
|
|
55
|
+
*/
|
|
56
|
+
emblemIcon?: IconSource;
|
|
57
|
+
/**
|
|
58
|
+
* Heading level for the title. Defaults to `h3`.
|
|
59
|
+
*
|
|
60
|
+
* A grid of these usually sits under a section heading, so `h3` is right;
|
|
61
|
+
* override it if the surrounding outline differs.
|
|
62
|
+
*/
|
|
63
|
+
titleAs?: 'h2' | 'h3' | 'h4';
|
|
64
|
+
/** Extra class names. */
|
|
65
|
+
className?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A Manuia goal, rendered as a card.
|
|
69
|
+
*
|
|
70
|
+
* Composes `ui-core`'s `Card`, `Badge`, and `Progress` with Manuia's own
|
|
71
|
+
* concepts — timeframes, categories, milestones — which is exactly why it
|
|
72
|
+
* lives in the product package rather than in `ui-core`.
|
|
73
|
+
*
|
|
74
|
+
* Longer horizons are given more visual weight ("prestige tiers"). That is a
|
|
75
|
+
* deliberate product signal, not decoration for its own sake: a five-year goal
|
|
76
|
+
* should not look like a weekly one.
|
|
77
|
+
*
|
|
78
|
+
* The card is styled as interactive but is **not** operable on its own — wrap
|
|
79
|
+
* it in a link or button so it is reachable by keyboard, exactly as `Card`'s
|
|
80
|
+
* `interactive` prop documents.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* The three highest tiers carry the full prestige treatment — gradient wash,
|
|
84
|
+
* coloured glow shadow, corner ornament, and emblem — driven by the `ochre`,
|
|
85
|
+
* `sage`, and `sienna` ramps in `@manakind/manuia-tokens`.
|
|
86
|
+
*
|
|
87
|
+
* Those ramp values are interim: generated in OKLCH rather than taken from
|
|
88
|
+
* designed colours, so the shapes are right and the exact hues are pending a
|
|
89
|
+
* design pass. See issue #34.
|
|
90
|
+
*/
|
|
91
|
+
export declare function GoalCard({ title, description, type, status, statusIcon, categories, maxVisibleCategories, milestones, targetDate, typeIcon, milestoneIcon, emblemIcon, titleAs, className, }: GoalCardProps): import("react/jsx-runtime").JSX.Element;
|
|
92
|
+
//# sourceMappingURL=goal-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-card.d.ts","sourceRoot":"","sources":["../../src/goal-card/goal-card.tsx"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,UAAU,EAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,iBAAiB,CAAC;AAEzB,uDAAuD;AACvD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEhF,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,GAAG,WAAW,CAAC;AAErE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,EAAE,QAAQ,CAAC;IACf,yCAAyC;IACzC,MAAM,EAAE,UAAU,CAAC;IACnB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,uCAAuC;IACvC,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,oEAAoE;IACpE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,mEAAmE;IACnE,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC7B,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2BD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,MAAM,EACN,UAAU,EACV,UAAe,EACf,oBAAwB,EACxB,UAAU,EACV,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EACV,OAAc,EACd,SAAS,GACV,EAAE,aAAa,2CA8Gf"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Card, CardContent, CardHeader, CardTitle, cx, Icon, Progress, } from '@manakind/ui-core';
|
|
3
|
+
import { GoalCategories } from '../goal-categories';
|
|
4
|
+
import './goal-card.css';
|
|
5
|
+
const STATUS_VARIANT = {
|
|
6
|
+
'Not Started': 'muted',
|
|
7
|
+
'In Progress': 'info',
|
|
8
|
+
Completed: 'success',
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Emblem glyph size per tier, mirroring the product (20 / 16 / 14). The ring
|
|
12
|
+
* around it is sized in CSS from the tier tokens; this is the icon inside it.
|
|
13
|
+
*/
|
|
14
|
+
const TIER_EMBLEM_ICON_SIZE = {
|
|
15
|
+
'5 Year': 'lg',
|
|
16
|
+
'1 Year': 'md',
|
|
17
|
+
Quarterly: 'sm',
|
|
18
|
+
};
|
|
19
|
+
/** Maps a timeframe to its CSS tier modifier. */
|
|
20
|
+
const TIER_CLASS = {
|
|
21
|
+
'5 Year': 'legendary',
|
|
22
|
+
'1 Year': 'epic',
|
|
23
|
+
Quarterly: 'rare',
|
|
24
|
+
Monthly: 'common',
|
|
25
|
+
Weekly: 'plain',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A Manuia goal, rendered as a card.
|
|
29
|
+
*
|
|
30
|
+
* Composes `ui-core`'s `Card`, `Badge`, and `Progress` with Manuia's own
|
|
31
|
+
* concepts — timeframes, categories, milestones — which is exactly why it
|
|
32
|
+
* lives in the product package rather than in `ui-core`.
|
|
33
|
+
*
|
|
34
|
+
* Longer horizons are given more visual weight ("prestige tiers"). That is a
|
|
35
|
+
* deliberate product signal, not decoration for its own sake: a five-year goal
|
|
36
|
+
* should not look like a weekly one.
|
|
37
|
+
*
|
|
38
|
+
* The card is styled as interactive but is **not** operable on its own — wrap
|
|
39
|
+
* it in a link or button so it is reachable by keyboard, exactly as `Card`'s
|
|
40
|
+
* `interactive` prop documents.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* The three highest tiers carry the full prestige treatment — gradient wash,
|
|
44
|
+
* coloured glow shadow, corner ornament, and emblem — driven by the `ochre`,
|
|
45
|
+
* `sage`, and `sienna` ramps in `@manakind/manuia-tokens`.
|
|
46
|
+
*
|
|
47
|
+
* Those ramp values are interim: generated in OKLCH rather than taken from
|
|
48
|
+
* designed colours, so the shapes are right and the exact hues are pending a
|
|
49
|
+
* design pass. See issue #34.
|
|
50
|
+
*/
|
|
51
|
+
export function GoalCard({ title, description, type, status, statusIcon, categories = [], maxVisibleCategories = 3, milestones, targetDate, typeIcon, milestoneIcon, emblemIcon, titleAs = 'h3', className, }) {
|
|
52
|
+
const completed = milestones?.filter((m) => m.completed).length ?? 0;
|
|
53
|
+
const total = milestones?.length ?? 0;
|
|
54
|
+
/*
|
|
55
|
+
* Three decorative, inert spans are always in the tree, and CSS decides per
|
|
56
|
+
* tier whether they show: the ornament frame and the four corner marks
|
|
57
|
+
* (brackets on 5 Year, dots on 1 Year), and the hairline closing the header
|
|
58
|
+
* band, which is hidden below the top two tiers.
|
|
59
|
+
*
|
|
60
|
+
* Rendered unconditionally rather than branched on `type` so the tier
|
|
61
|
+
* treatment stays entirely in `goal-card.css` — one place to read what a
|
|
62
|
+
* tier looks like, and no second definition here to drift from it.
|
|
63
|
+
*/
|
|
64
|
+
return (_jsxs(Card, { interactive: true, className: cx('mui-goal-card', `mui-goal-card--${TIER_CLASS[type]}`, className), children: [_jsx("span", { className: "mui-goal-card__ornament", "aria-hidden": "true" }), _jsxs("span", { className: "mui-goal-card__corners", "aria-hidden": "true", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {}), _jsx("span", {})] }), _jsxs(CardHeader, { className: "mui-goal-card__header", children: [_jsxs("div", { className: "mui-goal-card__meta", children: [_jsxs("span", { className: "mui-goal-card__meta-start", children: [emblemIcon && (_jsx("span", { className: "mui-goal-card__emblem", "aria-hidden": "true", children: _jsx(Icon, { source: emblemIcon, size: TIER_EMBLEM_ICON_SIZE[type] ?? 'md', decorative: true }) })), _jsxs("span", { className: "mui-goal-card__type", children: [typeIcon && _jsx(Icon, { source: typeIcon, size: "sm", decorative: true }), type] })] }), _jsx(Badge, { variant: STATUS_VARIANT[status], appearance: "plain", icon: statusIcon, "aria-label": status, children: status })] }), _jsx(GoalCategories, { className: "mui-goal-card__categories", categories: categories, maxVisible: maxVisibleCategories }), _jsx(CardTitle, { as: titleAs, className: "mui-goal-card__title", children: title }), _jsx("span", { className: "mui-goal-card__divider", "aria-hidden": "true" })] }), _jsxs(CardContent, { className: "mui-goal-card__content", children: [_jsx("p", { className: "mui-goal-card__description", children: description || 'No description provided.' }), total > 0 && (_jsxs("div", { className: "mui-goal-card__milestones", children: [_jsx(Progress, { size: "sm", value: completed, max: total, label: _jsx("span", { className: "mui-goal-card__milestones-label", children: "Milestones" }), valueText: `${completed} of ${total}` }), _jsx("ul", { className: "mui-goal-card__milestone-list", children: milestones?.map((milestone) => (_jsxs("li", { className: cx('mui-goal-card__milestone', milestone.completed && 'mui-goal-card__milestone--done'), children: [_jsx("span", { className: "mui-goal-card__milestone-marker", "aria-hidden": "true", children: milestone.completed && milestoneIcon && (_jsx(Icon, { source: milestoneIcon, size: "sm", decorative: true })) }), _jsx("span", { className: "mui-goal-card__milestone-title", children: milestone.title })] }, milestone.id))) })] })), targetDate && (_jsxs("p", { className: "mui-goal-card__target", children: [_jsx("span", { className: "mui-goal-card__target-label", children: "Target:" }), " ", targetDate] }))] })] }));
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=goal-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-card.js","sourceRoot":"","sources":["../../src/goal-card/goal-card.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/goal-card/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/goal-card/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.mui-goal-categories {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-wrap: wrap;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: var(--semantic-space-gap) var(--semantic-space-gap-relaxed);
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
list-style: none;
|
|
10
|
+
|
|
11
|
+
& .mui-goal-categories__item {
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--semantic-space-gap-tight);
|
|
15
|
+
color: var(--semantic-color-text-muted);
|
|
16
|
+
font-size: var(--primitives-font-size-sm);
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* `--mui-goal-category` is set per item from the category's stored
|
|
22
|
+
* colour, falling back to the primary action colour for a category that
|
|
23
|
+
* has none yet.
|
|
24
|
+
*
|
|
25
|
+
* The dot is deliberately NOT load-bearing. A category colour is user
|
|
26
|
+
* data — any colour at all, including one that matches the card surface —
|
|
27
|
+
* so no contrast floor can be promised for it, and promising one would be
|
|
28
|
+
* a claim this component cannot keep. The name sits beside it as text on
|
|
29
|
+
* the card surface at that surface's own checked contrast, which is what
|
|
30
|
+
* actually carries the category.
|
|
31
|
+
*
|
|
32
|
+
* The ring exists so the dot does not vanish entirely against a
|
|
33
|
+
* near-surface colour. It is a visibility floor for a decoration, not a
|
|
34
|
+
* contrast guarantee.
|
|
35
|
+
*/
|
|
36
|
+
& .mui-goal-categories__dot {
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
width: var(--component-goal-categories-dot-size);
|
|
39
|
+
height: var(--component-goal-categories-dot-size);
|
|
40
|
+
border-radius: var(--semantic-radius-pill);
|
|
41
|
+
background-color: var(--mui-goal-category, var(--semantic-color-action-primary));
|
|
42
|
+
box-shadow: 0 0 0 1px var(--semantic-color-border-subtle);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* No colour override: the overflow item is the same muted text as the
|
|
47
|
+
* rest, and its lack of a dot already sets it apart. It keeps a class so
|
|
48
|
+
* tests and products have a hook.
|
|
49
|
+
*/
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import './goal-categories.css';
|
|
2
|
+
export interface GoalCategory {
|
|
3
|
+
/** Stable identifier. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Display name. Carries the meaning — never rely on the colour alone. */
|
|
6
|
+
name: string;
|
|
7
|
+
/**
|
|
8
|
+
* Resolved CSS colour for this category.
|
|
9
|
+
*
|
|
10
|
+
* Category colours are **data, not design tokens** — each category row
|
|
11
|
+
* stores a colour the user picked, so the value arrives at runtime rather
|
|
12
|
+
* than from the token pipeline. It is applied through a custom property
|
|
13
|
+
* rather than an inline `background-color` so the CSS keeps ownership of
|
|
14
|
+
* how the colour is used.
|
|
15
|
+
*
|
|
16
|
+
* Validated before it reaches the style attribute — see `safeColor` and the
|
|
17
|
+
* comment above it. A value that is not a recognisable CSS colour is ignored.
|
|
18
|
+
*
|
|
19
|
+
* When omitted or rejected the dot falls back to the product's primary
|
|
20
|
+
* action colour.
|
|
21
|
+
*/
|
|
22
|
+
color?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface GoalCategoriesProps {
|
|
25
|
+
/** Categories to show. Renders nothing when empty. */
|
|
26
|
+
categories: GoalCategory[];
|
|
27
|
+
/**
|
|
28
|
+
* Show at most this many, then a `+N more`. Omit to show all.
|
|
29
|
+
*
|
|
30
|
+
* Normalized before use: truncated toward zero, clamped at zero, and a
|
|
31
|
+
* non-finite value is treated as "no limit" rather than hiding everything.
|
|
32
|
+
*/
|
|
33
|
+
maxVisible?: number;
|
|
34
|
+
/** Extra class names. */
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The categories a Manuia goal belongs to, as dotted text.
|
|
39
|
+
*
|
|
40
|
+
* A Manuia-specific composition: "a goal belongs to N categories, each with a
|
|
41
|
+
* user-chosen colour, collapsing after N visible" is a product concept and
|
|
42
|
+
* does not belong in `ui-core`.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* The name is always rendered as text on the card surface, so it inherits
|
|
46
|
+
* that surface's checked contrast and the grouping survives for anyone who
|
|
47
|
+
* cannot distinguish the colours. The dot is reinforcement, never the signal.
|
|
48
|
+
*
|
|
49
|
+
* This used to be a row of filled pills, which forced the category colour to
|
|
50
|
+
* be composited to 25% so text could sit on it — no fixed text colour clears
|
|
51
|
+
* 4.5:1 over an arbitrary user-chosen colour. Moving the colour into a dot
|
|
52
|
+
* removed the constraint rather than working around it.
|
|
53
|
+
*/
|
|
54
|
+
export declare function GoalCategories({ categories, maxVisible, className }: GoalCategoriesProps): import("react/jsx-runtime").JSX.Element | null;
|
|
55
|
+
//# sourceMappingURL=goal-categories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-categories.d.ts","sourceRoot":"","sources":["../../src/goal-categories/goal-categories.tsx"],"names":[],"mappings":"AAEA,OAAO,uBAAuB,CAAC;AA+D/B,MAAM,WAAW,YAAY;IAC3B,yBAAyB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,mBAAmB,kDA0CxF"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '@manakind/ui-core';
|
|
3
|
+
import './goal-categories.css';
|
|
4
|
+
/*
|
|
5
|
+
* The colour arrives from outside the token pipeline, so this component
|
|
6
|
+
* validates it instead of trusting its caller.
|
|
7
|
+
*
|
|
8
|
+
* Two separate reasons, and the second is the one that bites in practice.
|
|
9
|
+
*
|
|
10
|
+
* **Injection, on the server.** React escapes HTML, not CSS. For a *custom*
|
|
11
|
+
* property `react-dom/server` serialises the string into the markup with only
|
|
12
|
+
* the five HTML entities replaced — the semicolon is not among them — so
|
|
13
|
+
* `red;position:fixed;inset:0` closes the declaration and the browser parses
|
|
14
|
+
* the tail as further rules. Enough to cover the page or hide a control.
|
|
15
|
+
* (React's client path goes through `style.setProperty()`, and CSSOM rejects
|
|
16
|
+
* a value containing `;`, so this is specifically an SSR exposure.)
|
|
17
|
+
*
|
|
18
|
+
* **Invisible dots, everywhere.** A value that is not a colour at all is the
|
|
19
|
+
* likelier input — a product mapping its own palette names through, say. Once
|
|
20
|
+
* it is *set*, the `var(--mui-goal-category, …)` fallback in the CSS no longer
|
|
21
|
+
* applies, and the `background-color` it feeds is invalid at computed-value
|
|
22
|
+
* time: the dot resolves to transparent rather than falling back. So the guard
|
|
23
|
+
* admits only real colour syntax; anything else is left unset, which is what
|
|
24
|
+
* makes the documented fallback actually happen.
|
|
25
|
+
*
|
|
26
|
+
* This outlived the pills-to-dots change. The mechanism moved — it was
|
|
27
|
+
* `color-mix()` going invalid, now it is `background-color` — but "a bad value
|
|
28
|
+
* silently paints nothing" is a property of the custom-property fallback, not
|
|
29
|
+
* of whatever consumed it.
|
|
30
|
+
*/
|
|
31
|
+
const CSS_COLOR_FUNCTION = /^(?:rgb|hsl|hwb|lab|lch|oklab|oklch|color)a?\([^;(){}]*\)$/i;
|
|
32
|
+
const CSS_HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
33
|
+
// The CSS named colours. A bare word outside this set is rejected rather than
|
|
34
|
+
// passed through, because an unrecognised keyword is what produces the
|
|
35
|
+
// invalid-colour case above.
|
|
36
|
+
const CSS_NAMED_COLORS = new Set(`aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue
|
|
37
|
+
blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk
|
|
38
|
+
crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki
|
|
39
|
+
darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen
|
|
40
|
+
darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue
|
|
41
|
+
dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite
|
|
42
|
+
gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki
|
|
43
|
+
lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan
|
|
44
|
+
lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen
|
|
45
|
+
lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen
|
|
46
|
+
magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen
|
|
47
|
+
mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream
|
|
48
|
+
mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid
|
|
49
|
+
palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum
|
|
50
|
+
powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown
|
|
51
|
+
seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen
|
|
52
|
+
steelblue tan teal thistle tomato transparent turquoise violet wheat white whitesmoke
|
|
53
|
+
yellow yellowgreen`.split(/\s+/));
|
|
54
|
+
function safeColor(color) {
|
|
55
|
+
const trimmed = color?.trim();
|
|
56
|
+
if (!trimmed)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (CSS_HEX.test(trimmed) || CSS_COLOR_FUNCTION.test(trimmed))
|
|
59
|
+
return trimmed;
|
|
60
|
+
return CSS_NAMED_COLORS.has(trimmed.toLowerCase()) ? trimmed : undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The categories a Manuia goal belongs to, as dotted text.
|
|
64
|
+
*
|
|
65
|
+
* A Manuia-specific composition: "a goal belongs to N categories, each with a
|
|
66
|
+
* user-chosen colour, collapsing after N visible" is a product concept and
|
|
67
|
+
* does not belong in `ui-core`.
|
|
68
|
+
*
|
|
69
|
+
* @remarks
|
|
70
|
+
* The name is always rendered as text on the card surface, so it inherits
|
|
71
|
+
* that surface's checked contrast and the grouping survives for anyone who
|
|
72
|
+
* cannot distinguish the colours. The dot is reinforcement, never the signal.
|
|
73
|
+
*
|
|
74
|
+
* This used to be a row of filled pills, which forced the category colour to
|
|
75
|
+
* be composited to 25% so text could sit on it — no fixed text colour clears
|
|
76
|
+
* 4.5:1 over an arbitrary user-chosen colour. Moving the colour into a dot
|
|
77
|
+
* removed the constraint rather than working around it.
|
|
78
|
+
*/
|
|
79
|
+
export function GoalCategories({ categories, maxVisible, className }) {
|
|
80
|
+
if (categories.length === 0)
|
|
81
|
+
return null;
|
|
82
|
+
// Normalized once, because the value feeds both an index operation and
|
|
83
|
+
// arithmetic and the two coerce differently: `slice` truncates a fractional
|
|
84
|
+
// end index while the subtraction below keeps it, so `2.5` over three
|
|
85
|
+
// categories would render two names and offer "+0.5 more". Negatives are
|
|
86
|
+
// clamped for the same reason in the other direction — `slice` reads them as
|
|
87
|
+
// an offset from the end, dropping a real category and overcounting the
|
|
88
|
+
// remainder at once. Zero stays meaningful: it collapses the set into
|
|
89
|
+
// "+N more". NaN is excluded rather than clamped: it survives both Math
|
|
90
|
+
// calls, and `slice(0, NaN)` renders nothing while `NaN > 0` suppresses the
|
|
91
|
+
// overflow item too — the whole list would vanish with no indicator that it
|
|
92
|
+
// had.
|
|
93
|
+
const limit = Number.isFinite(maxVisible)
|
|
94
|
+
? Math.max(Math.trunc(maxVisible), 0)
|
|
95
|
+
: undefined;
|
|
96
|
+
const visible = limit === undefined ? categories : categories.slice(0, limit);
|
|
97
|
+
const remaining = limit === undefined ? 0 : Math.max(categories.length - limit, 0);
|
|
98
|
+
return (_jsxs("ul", { className: cx('mui-goal-categories', className), children: [visible.map((category) => {
|
|
99
|
+
const dot = safeColor(category.color);
|
|
100
|
+
return (_jsxs("li", { className: "mui-goal-categories__item", style: dot ? { '--mui-goal-category': dot } : undefined, children: [_jsx("span", { className: "mui-goal-categories__dot", "aria-hidden": "true" }), category.name] }, category.id));
|
|
101
|
+
}), remaining > 0 && (_jsxs("li", { className: "mui-goal-categories__item mui-goal-categories__item--overflow", children: ["+", remaining, " more"] }))] }));
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=goal-categories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-categories.js","sourceRoot":"","sources":["../../src/goal-categories/goal-categories.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAEvC,OAAO,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,kBAAkB,GAAG,6DAA6D,CAAC;AACzF,MAAM,OAAO,GAAG,yDAAyD,CAAC;AAE1E,8EAA8E;AAC9E,uEAAuE;AACvE,6BAA6B;AAC7B,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B;;;;;;;;;;;;;;;;;sBAiBoB,CAAC,KAAK,CAAC,KAAK,CAAC,CAClC,CAAC;AAEF,SAAS,SAAS,CAAC,KAAyB;IAC1C,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,OAAO,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAuCD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAuB;IACvF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,uEAAuE;IACvE,4EAA4E;IAC5E,sEAAsE;IACtE,yEAAyE;IACzE,6EAA6E;IAC7E,wEAAwE;IACxE,sEAAsE;IACtE,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,OAAO;IACP,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAoB,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;IAEnF,OAAO,CACL,cAAI,SAAS,EAAE,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC,aAChD,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;gBACxB,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACtC,OAAO,CACL,cAEE,SAAS,EAAC,2BAA2B,EACrC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAE,EAAE,qBAAqB,EAAE,GAAG,EAAoB,CAAC,CAAC,CAAC,SAAS,aAE1E,eAAM,SAAS,EAAC,0BAA0B,iBAAa,MAAM,GAAG,EAC/D,QAAQ,CAAC,IAAI,KALT,QAAQ,CAAC,EAAE,CAMb,CACN,CAAC;YACJ,CAAC,CAAC,EACD,SAAS,GAAG,CAAC,IAAI,CAChB,cAAI,SAAS,EAAC,+DAA+D,kBACzE,SAAS,aACR,CACN,IACE,CACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/goal-categories/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/goal-categories/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/*
|
|
3
|
+
* Set the weight through the contract variable `ui-core`'s trigger reads,
|
|
4
|
+
* not by out-ranking its `.ds-tabs .ds-tabs__trigger` rule: that would need
|
|
5
|
+
* a `ds-` class here, which only `ui-core` may use (components.md).
|
|
6
|
+
*/
|
|
7
|
+
.mui-goal-tab-trigger--empty {
|
|
8
|
+
--ds-tabs-trigger-font-weight: var(--primitives-font-weight-regular);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TabsTrigger } from '@manakind/ui-core';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import './goal-tab-trigger.css';
|
|
4
|
+
export interface GoalTabTriggerProps extends ComponentProps<typeof TabsTrigger> {
|
|
5
|
+
/** Timeframe label, e.g. `Weekly`. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** How many goals fall in this timeframe. */
|
|
8
|
+
count: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function GoalTabTrigger({ label, count, className, ...props }: GoalTabTriggerProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=goal-tab-trigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-tab-trigger.d.ts","sourceRoot":"","sources":["../../src/goal-tab-trigger/goal-tab-trigger.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,wBAAwB,CAAC;AAEhC,MAAM,WAAW,mBAAoB,SAAQ,cAAc,CAAC,OAAO,WAAW,CAAC;IAC7E,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AA4BD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,2CAmBxF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, cx, TabsTrigger } from '@manakind/ui-core';
|
|
3
|
+
import './goal-tab-trigger.css';
|
|
4
|
+
/**
|
|
5
|
+
* A Manuia timeframe tab with its goal count.
|
|
6
|
+
*
|
|
7
|
+
* Composes `ui-core`'s `TabsTrigger` and `Badge`. The pairing is product
|
|
8
|
+
* specific — "a tab that carries a count of the things behind it" is a Manuia
|
|
9
|
+
* pattern, so it lives here rather than widening `TabsTrigger`.
|
|
10
|
+
*
|
|
11
|
+
* The count badge carries an `aria-label`, because a bare number announced
|
|
12
|
+
* after the label tells a screen reader user nothing. Tabs with no goals are
|
|
13
|
+
* rendered in a lighter weight, but they stay reachable — the count itself is
|
|
14
|
+
* the information.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Counts stop rendering their true value here and switch to `99+`.
|
|
18
|
+
*
|
|
19
|
+
* The cap bounds how wide the badge can get: without it a five-digit count
|
|
20
|
+
* would stretch the tab arbitrarily. At `99+` the badge widens from a circle
|
|
21
|
+
* into a pill, which is what every other library does with this case and
|
|
22
|
+
* costs nothing structurally — the height is pinned, so nothing reflows and
|
|
23
|
+
* the tab strip keeps its nested radii.
|
|
24
|
+
*
|
|
25
|
+
* The cap is display only. The `aria-label` always announces the real
|
|
26
|
+
* number, so nothing is hidden from a screen reader.
|
|
27
|
+
*/
|
|
28
|
+
const MAX_DISPLAYED_COUNT = 99;
|
|
29
|
+
export function GoalTabTrigger({ label, count, className, ...props }) {
|
|
30
|
+
const hasGoals = count > 0;
|
|
31
|
+
const overflows = count > MAX_DISPLAYED_COUNT;
|
|
32
|
+
return (_jsxs(TabsTrigger, { className: cx('mui-goal-tab-trigger', !hasGoals && 'mui-goal-tab-trigger--empty', className), ...props, children: [_jsx("span", { children: label }), _jsx(Badge, { shape: "circle", variant: hasGoals ? 'success' : 'outline', "aria-label": `${count} ${count === 1 ? 'goal' : 'goals'}`, children: overflows ? `${MAX_DISPLAYED_COUNT}+` : count })] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=goal-tab-trigger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-tab-trigger.js","sourceRoot":"","sources":["../../src/goal-tab-trigger/goal-tab-trigger.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,wBAAwB,CAAC;AAShC;;;;;;;;;;;GAWG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,MAAM,UAAU,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAuB;IACvF,MAAM,QAAQ,GAAG,KAAK,GAAG,CAAC,CAAC;IAC3B,MAAM,SAAS,GAAG,KAAK,GAAG,mBAAmB,CAAC;IAE9C,OAAO,CACL,MAAC,WAAW,IACV,SAAS,EAAE,EAAE,CAAC,sBAAsB,EAAE,CAAC,QAAQ,IAAI,6BAA6B,EAAE,SAAS,CAAC,KACxF,KAAK,aAET,yBAAO,KAAK,GAAQ,EACpB,KAAC,KAAK,IACJ,KAAK,EAAC,QAAQ,EACd,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,gBAC7B,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,YAEvD,SAAS,CAAC,CAAC,CAAC,GAAG,mBAAmB,GAAG,CAAC,CAAC,CAAC,KAAK,GACxC,IACI,CACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/goal-tab-trigger/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/goal-tab-trigger/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@manakind/manuia-ui` is the Manuia-flavored surface of the Manakind design system.
|
|
3
|
+
*
|
|
4
|
+
* It re-exports the shared base components from `@manakind/ui-core` and adds
|
|
5
|
+
* the Manuia-specific compositions assembled from them.
|
|
6
|
+
*
|
|
7
|
+
* The Manuia theme is provided at runtime by importing
|
|
8
|
+
* `@manakind/manuia-tokens/css/theme` (and the underlying token layers) — see
|
|
9
|
+
* `.storybook/preview.ts` for the canonical setup.
|
|
10
|
+
*/
|
|
11
|
+
export * from '@manakind/ui-core';
|
|
12
|
+
export * from './goal-card';
|
|
13
|
+
export * from './goal-categories';
|
|
14
|
+
export * from './goal-tab-trigger';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@manakind/manuia-ui` is the Manuia-flavored surface of the Manakind design system.
|
|
3
|
+
*
|
|
4
|
+
* It re-exports the shared base components from `@manakind/ui-core` and adds
|
|
5
|
+
* the Manuia-specific compositions assembled from them.
|
|
6
|
+
*
|
|
7
|
+
* The Manuia theme is provided at runtime by importing
|
|
8
|
+
* `@manakind/manuia-tokens/css/theme` (and the underlying token layers) — see
|
|
9
|
+
* `.storybook/preview.ts` for the canonical setup.
|
|
10
|
+
*/
|
|
11
|
+
export * from '@manakind/ui-core';
|
|
12
|
+
export * from './goal-card';
|
|
13
|
+
export * from './goal-categories';
|
|
14
|
+
export * from './goal-tab-trigger';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@manakind/manuia-ui",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Humanity-Plus/Manakind.git",
|
|
9
|
+
"directory": "packages/manuia-ui"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"sideEffects": [
|
|
18
|
+
"**/*.css"
|
|
19
|
+
],
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.build.json && bunx tsx ../ds-config/scripts/copy-assets.ts src dist .css",
|
|
30
|
+
"test": "vitest run --passWithNoTests",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"lint:css": "stylelint --allow-empty-input 'src/**/*.css'",
|
|
33
|
+
"lint": "biome lint .",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"storybook": "storybook dev -p 6006",
|
|
36
|
+
"build-storybook": "storybook build",
|
|
37
|
+
"vr": "playwright test"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@manakind/manuia-tokens": "0.2.0",
|
|
41
|
+
"@manakind/ui-core": "0.2.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"react": "^19.2.5",
|
|
45
|
+
"react-dom": "^19.2.5"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@manakind/ds-config": "*",
|
|
49
|
+
"@playwright/test": "1.63.0",
|
|
50
|
+
"@storybook/addon-a11y": "^10.3.6",
|
|
51
|
+
"@storybook/addon-docs": "^10.3.6",
|
|
52
|
+
"@storybook/addon-vitest": "10.3.6",
|
|
53
|
+
"@storybook/react-vite": "^10.3.6",
|
|
54
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
55
|
+
"@testing-library/react": "^16.3.2",
|
|
56
|
+
"@testing-library/user-event": "^14.6.1",
|
|
57
|
+
"@types/react": "^19.2.14",
|
|
58
|
+
"@types/react-dom": "^19.2.3",
|
|
59
|
+
"@vitest/browser": "4.1.11",
|
|
60
|
+
"@vitest/browser-playwright": "4.1.11",
|
|
61
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
62
|
+
"jest-axe": "^10.0.0",
|
|
63
|
+
"jsdom": "^29.1.1",
|
|
64
|
+
"playwright": "1.63.0",
|
|
65
|
+
"react": "^19.2.5",
|
|
66
|
+
"react-dom": "^19.2.5",
|
|
67
|
+
"storybook": "^10.3.6",
|
|
68
|
+
"storybook-addon-pseudo-states": "10.3.6",
|
|
69
|
+
"stylelint": "^17.9.1",
|
|
70
|
+
"stylelint-declaration-strict-value": "^1.11.1",
|
|
71
|
+
"typescript": "^6.0.3",
|
|
72
|
+
"vite": "^8.0.10",
|
|
73
|
+
"vitest": "4.1.11"
|
|
74
|
+
}
|
|
75
|
+
}
|