@roadlittledawn/docs-design-system-react 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,3 +41,6 @@ export interface CollapserProps {
41
41
  stepNumber?: number;
42
42
  }
43
43
  export declare function Collapser({ title, id, defaultOpen, open: controlledOpen, onToggle, children, className, align, icon, stepNumber, }: CollapserProps): import("react/jsx-runtime").JSX.Element;
44
+ export declare namespace Collapser {
45
+ var displayName: string;
46
+ }
@@ -1,25 +1,18 @@
1
1
  'use client';
2
2
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
- import { useState, useRef, useEffect } from "react";
3
+ import { useState } from "react";
4
4
  import { useKeyPress } from "../hooks/useKeyPress";
5
5
  export function Collapser(_a) {
6
6
  var title = _a.title, id = _a.id, _b = _a.defaultOpen, defaultOpen = _b === void 0 ? false : _b, controlledOpen = _a.open, onToggle = _a.onToggle, children = _a.children, _c = _a.className, className = _c === void 0 ? "" : _c, _d = _a.align, align = _d === void 0 ? 'left' : _d, icon = _a.icon, stepNumber = _a.stepNumber;
7
7
  var _e = useState(defaultOpen), uncontrolledOpen = _e[0], setUncontrolledOpen = _e[1];
8
8
  var isControlled = controlledOpen !== undefined;
9
9
  var isOpen = isControlled ? controlledOpen : uncontrolledOpen;
10
- var _f = useState(undefined), height = _f[0], setHeight = _f[1];
11
- var contentRef = useRef(null);
12
10
  // Keyboard shortcuts: 's' or 'f' to show, 'h' to hide
13
11
  useKeyPress(['s', 'f', 'h'], function (e) {
14
12
  if (!isControlled) {
15
13
  setUncontrolledOpen(e.key !== 'h');
16
14
  }
17
15
  });
18
- useEffect(function () {
19
- if (contentRef.current) {
20
- setHeight(contentRef.current.scrollHeight);
21
- }
22
- }, [children]);
23
16
  var toggleOpen = function () {
24
17
  if (onToggle) {
25
18
  onToggle();
@@ -35,7 +28,6 @@ export function Collapser(_a) {
35
28
  ]
36
29
  .filter(Boolean)
37
30
  .join(" ");
38
- return (_jsxs("div", { className: collapserClasses, children: [_jsxs("button", { onClick: toggleOpen, type: "button", className: "dds-collapser-button", "aria-expanded": isOpen, children: [stepNumber !== undefined && (_jsxs("span", { className: "dds-collapser-step-number", children: [stepNumber, "."] })), icon && (_jsx("span", { className: "dds-collapser-header-icon", "aria-hidden": "true", children: icon })), _jsx("h5", { id: id, className: "dds-collapser-title", children: title }), _jsx("svg", { className: "dds-collapser-icon ".concat(isOpen ? "dds-collapser-icon-open" : ""), width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "6 9 12 15 18 9" }) })] }), _jsx("div", { className: "dds-collapser-content-wrapper", style: {
39
- height: isOpen ? height : 0,
40
- }, children: _jsx("div", { ref: contentRef, className: "dds-collapser-content", children: children }) })] }));
31
+ return (_jsxs("div", { className: collapserClasses, children: [_jsxs("button", { onClick: toggleOpen, type: "button", className: "dds-collapser-button", "aria-expanded": isOpen, children: [stepNumber !== undefined && (_jsxs("span", { className: "dds-collapser-step-number", children: [stepNumber, "."] })), icon && (_jsx("span", { className: "dds-collapser-header-icon", "aria-hidden": "true", children: icon })), _jsx("h5", { id: id, className: "dds-collapser-title", children: title }), _jsx("svg", { className: "dds-collapser-icon ".concat(isOpen ? "dds-collapser-icon-open" : ""), width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "6 9 12 15 18 9" }) })] }), _jsx("div", { className: "dds-collapser-content-wrapper".concat(isOpen ? " dds-collapser-content-wrapper--open" : ""), children: _jsx("div", { className: "dds-collapser-content", children: children }) })] }));
41
32
  }
33
+ Collapser.displayName = 'Collapser';
@@ -21,7 +21,6 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
21
21
  };
22
22
  import { jsx as _jsx } from "react/jsx-runtime";
23
23
  import { useState, Children, cloneElement, isValidElement } from 'react';
24
- import { Collapser } from './Collapser';
25
24
  export var CollapserGroup = function (_a) {
26
25
  var children = _a.children, _b = _a.spacing, spacing = _b === void 0 ? '0.5rem' : _b, _c = _a.allowMultiple, allowMultiple = _c === void 0 ? true : _c, defaultOpen = _a.defaultOpen, onChange = _a.onChange, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.numbered, numbered = _e === void 0 ? false : _e;
27
26
  var _f = useState(function () {
@@ -45,10 +44,11 @@ export var CollapserGroup = function (_a) {
45
44
  });
46
45
  };
47
46
  return (_jsx("div", { className: "dds-collapser-group ".concat(className).trim(), style: { gap: spacing }, children: Children.map(children, function (child, index) {
47
+ var _a;
48
48
  if (!isValidElement(child))
49
49
  return child;
50
50
  // Only inject props if child is a Collapser component
51
- if (child.type === Collapser) {
51
+ if (((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) === 'Collapser') {
52
52
  return cloneElement(child, __assign(__assign(__assign({}, child.props), { open: openIndexes.includes(index), onToggle: function () { return handleToggle(index); } }), (numbered ? { stepNumber: index + 1 } : {})));
53
53
  }
54
54
  return child;
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ export interface IconProps {
3
+ /**
4
+ * SVG to render. Accepts a React SVG component (e.g. imported with SVGR or
5
+ * defined inline) or a raw SVG string.
6
+ *
7
+ * **Accessibility note:** when passing a React SVG component, do not include
8
+ * `aria-hidden`, `role`, or `aria-label` attributes in the component itself.
9
+ * The `Icon` component applies the correct accessibility attributes based on
10
+ * whether an `aria-label` prop is provided.
11
+ *
12
+ * **Security note:** when passing a raw SVG string, ensure the content is
13
+ * from a trusted source. The string is injected via `dangerouslySetInnerHTML`
14
+ * without sanitization.
15
+ */
16
+ svg: React.ComponentType<React.SVGProps<SVGSVGElement>> | string;
17
+ /**
18
+ * Width and height in pixels.
19
+ * @default 16
20
+ */
21
+ size?: number;
22
+ /** Additional CSS class names to apply to the icon element. */
23
+ className?: string;
24
+ /**
25
+ * Accessible label for the icon. When provided the icon is announced by
26
+ * screen readers with `role="img"`. When omitted the icon is treated as
27
+ * decorative and hidden from assistive technology (`aria-hidden="true"`).
28
+ */
29
+ 'aria-label'?: string;
30
+ }
31
+ /**
32
+ * Renders an SVG icon. Accepts either a React SVG component (e.g. imported
33
+ * with SVGR) or a raw SVG string, making it easy to use your own icon library
34
+ * without being tied to a specific icon set.
35
+ *
36
+ * @example
37
+ * // React SVG component
38
+ * import { ReactComponent as ChevronIcon } from './chevron.svg';
39
+ * <Icon svg={ChevronIcon} size={20} aria-label="Expand" />
40
+ *
41
+ * @example
42
+ * // Inline SVG function
43
+ * const StarIcon = (props: React.SVGProps<SVGSVGElement>) => (
44
+ * <svg viewBox="0 0 24 24" {...props}>...</svg>
45
+ * );
46
+ * <Icon svg={StarIcon} size={16} />
47
+ *
48
+ * @example
49
+ * // Raw SVG string
50
+ * <Icon svg='<svg viewBox="0 0 24 24">...</svg>' size={24} />
51
+ */
52
+ export declare function Icon({ svg, size, className, 'aria-label': ariaLabel, }: IconProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,49 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ /**
14
+ * Renders an SVG icon. Accepts either a React SVG component (e.g. imported
15
+ * with SVGR) or a raw SVG string, making it easy to use your own icon library
16
+ * without being tied to a specific icon set.
17
+ *
18
+ * @example
19
+ * // React SVG component
20
+ * import { ReactComponent as ChevronIcon } from './chevron.svg';
21
+ * <Icon svg={ChevronIcon} size={20} aria-label="Expand" />
22
+ *
23
+ * @example
24
+ * // Inline SVG function
25
+ * const StarIcon = (props: React.SVGProps<SVGSVGElement>) => (
26
+ * <svg viewBox="0 0 24 24" {...props}>...</svg>
27
+ * );
28
+ * <Icon svg={StarIcon} size={16} />
29
+ *
30
+ * @example
31
+ * // Raw SVG string
32
+ * <Icon svg='<svg viewBox="0 0 24 24">...</svg>' size={24} />
33
+ */
34
+ export function Icon(_a) {
35
+ var svg = _a.svg, _b = _a.size, size = _b === void 0 ? 16 : _b, _c = _a.className, className = _c === void 0 ? '' : _c, ariaLabel = _a["aria-label"];
36
+ var isDecorative = !ariaLabel;
37
+ var classNames = ['dds-icon', className].filter(Boolean).join(' ');
38
+ if (typeof svg === 'string') {
39
+ return (_jsx("span", __assign({ className: classNames, style: { width: size, height: size },
40
+ // eslint-disable-next-line react/no-danger
41
+ dangerouslySetInnerHTML: { __html: svg } }, (isDecorative
42
+ ? { 'aria-hidden': true }
43
+ : { role: 'img', 'aria-label': ariaLabel }))));
44
+ }
45
+ var SvgComponent = svg;
46
+ return (_jsx(SvgComponent, __assign({ className: classNames, width: size, height: size }, (isDecorative
47
+ ? { 'aria-hidden': true }
48
+ : { role: 'img', 'aria-label': ariaLabel }))));
49
+ }
@@ -0,0 +1,31 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Icon } from './Icon';
3
+ declare const meta: Meta<typeof Icon>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Icon>;
6
+ /**
7
+ * Pass any React SVG component to the `svg` prop. The component is rendered
8
+ * directly with the provided `size` applied as `width` and `height`.
9
+ */
10
+ export declare const WithReactComponent: Story;
11
+ /**
12
+ * Pass a raw SVG string to the `svg` prop. The string is injected using
13
+ * `dangerouslySetInnerHTML` — ensure the content is from a trusted source.
14
+ */
15
+ export declare const WithSvgString: Story;
16
+ /**
17
+ * When an `aria-label` is provided the icon receives `role="img"` and is
18
+ * announced by screen readers with the given label. Use this when the icon
19
+ * conveys meaning without surrounding text.
20
+ */
21
+ export declare const WithAriaLabel: Story;
22
+ /**
23
+ * Sizes from small (12 px) to large (48 px). The `size` prop sets both
24
+ * `width` and `height` so the icon is always square.
25
+ */
26
+ export declare const Sizes: Story;
27
+ /**
28
+ * Icons inherit `currentColor` for their stroke/fill, so they automatically
29
+ * adapt to the surrounding text color.
30
+ */
31
+ export declare const InheritedColor: Story;
@@ -0,0 +1,141 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { Icon } from './Icon';
14
+ // ─── Sample SVG components used in stories ────────────────────────────────────
15
+ // Note: these sample SVG components intentionally omit accessibility attributes
16
+ // (aria-hidden, role, aria-label). The Icon component applies them based on
17
+ // whether an aria-label prop is provided.
18
+ var StarIcon = function (props) { return (_jsx("svg", __assign({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props, { children: _jsx("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }) }))); };
19
+ var ChevronIcon = function (props) { return (_jsx("svg", __assign({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props, { children: _jsx("polyline", { points: "6 9 12 15 18 9" }) }))); };
20
+ var ExternalLinkIcon = function (props) { return (_jsxs("svg", __assign({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props, { children: [_jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), _jsx("polyline", { points: "15 3 21 3 21 9" }), _jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }))); };
21
+ // Raw SVG string example
22
+ var closeIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>";
23
+ // ─── Meta ─────────────────────────────────────────────────────────────────────
24
+ var meta = {
25
+ title: 'Components/Icon',
26
+ component: Icon,
27
+ tags: ['autodocs'],
28
+ decorators: [
29
+ function (Story) { return (_jsx("div", { style: { color: 'var(--dds-tabs-panel-text)' }, children: _jsx(Story, {}) })); },
30
+ ],
31
+ argTypes: {
32
+ svg: {
33
+ description: 'SVG to render — accepts a React SVG component or a raw SVG string.',
34
+ control: false,
35
+ },
36
+ size: {
37
+ control: { type: 'number', min: 8, max: 64, step: 2 },
38
+ description: 'Width and height in pixels.',
39
+ table: { defaultValue: { summary: '16' } },
40
+ },
41
+ className: {
42
+ control: 'text',
43
+ description: 'Additional CSS class names.',
44
+ table: { defaultValue: { summary: '""' } },
45
+ },
46
+ 'aria-label': {
47
+ control: 'text',
48
+ description: 'Accessible label. When provided the icon is announced by screen readers. When omitted the icon is treated as decorative and hidden from assistive technology.',
49
+ },
50
+ },
51
+ parameters: {
52
+ docs: {
53
+ description: {
54
+ component: "\nThe `Icon` component renders an SVG icon without bundling any specific icon library.\nYou bring your own SVGs \u2014 as a React component or a raw SVG string \u2014 so you are never\nforced to adopt a particular icon set.\n\n## When to Use\n\n- Rendering icons imported with SVGR (e.g. `import { ReactComponent as Star } from './star.svg'`)\n- Rendering inline SVG components defined directly in your code\n- Rendering a trusted raw SVG string from your project's assets\n\n## When Not to Use\n\n- Do not pass untrusted or user-supplied SVG strings; the raw string path uses\n `dangerouslySetInnerHTML` without sanitization.\n- When you only need a decorative shape that can be achieved with CSS alone.\n\n## Accessibility\n\n- Decorative icons (no `aria-label`) automatically receive `aria-hidden=\"true\"` so\n they are ignored by screen readers.\n- Meaningful icons (e.g. a standalone icon button) should receive a descriptive\n `aria-label` so they are announced correctly.\n ",
55
+ },
56
+ },
57
+ },
58
+ };
59
+ export default meta;
60
+ // ─── Stories ──────────────────────────────────────────────────────────────────
61
+ /**
62
+ * Pass any React SVG component to the `svg` prop. The component is rendered
63
+ * directly with the provided `size` applied as `width` and `height`.
64
+ */
65
+ export var WithReactComponent = {
66
+ args: {
67
+ svg: StarIcon,
68
+ size: 24,
69
+ },
70
+ parameters: {
71
+ docs: {
72
+ source: {
73
+ code: "const StarIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\" />\n </svg>\n);\n\n<Icon svg={StarIcon} size={24} />",
74
+ },
75
+ },
76
+ },
77
+ };
78
+ /**
79
+ * Pass a raw SVG string to the `svg` prop. The string is injected using
80
+ * `dangerouslySetInnerHTML` — ensure the content is from a trusted source.
81
+ */
82
+ export var WithSvgString = {
83
+ args: {
84
+ svg: closeIconSvg,
85
+ size: 24,
86
+ },
87
+ parameters: {
88
+ docs: {
89
+ source: {
90
+ code: "const closeIconSvg = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n\n<Icon svg={closeIconSvg} size={24} />",
91
+ },
92
+ },
93
+ },
94
+ };
95
+ /**
96
+ * When an `aria-label` is provided the icon receives `role="img"` and is
97
+ * announced by screen readers with the given label. Use this when the icon
98
+ * conveys meaning without surrounding text.
99
+ */
100
+ export var WithAriaLabel = {
101
+ args: {
102
+ svg: ExternalLinkIcon,
103
+ size: 20,
104
+ 'aria-label': 'Opens in a new tab',
105
+ },
106
+ parameters: {
107
+ docs: {
108
+ source: {
109
+ code: "<Icon svg={ExternalLinkIcon} size={20} aria-label=\"Opens in a new tab\" />",
110
+ },
111
+ },
112
+ },
113
+ };
114
+ /**
115
+ * Sizes from small (12 px) to large (48 px). The `size` prop sets both
116
+ * `width` and `height` so the icon is always square.
117
+ */
118
+ export var Sizes = {
119
+ parameters: {
120
+ docs: {
121
+ source: {
122
+ code: "<Icon svg={ChevronIcon} size={12} />\n<Icon svg={ChevronIcon} size={16} />\n<Icon svg={ChevronIcon} size={20} />\n<Icon svg={ChevronIcon} size={24} />\n<Icon svg={ChevronIcon} size={32} />\n<Icon svg={ChevronIcon} size={48} />",
123
+ },
124
+ },
125
+ },
126
+ render: function () { return (_jsx("div", { style: { display: 'flex', gap: '1rem', alignItems: 'center' }, children: [12, 16, 20, 24, 32, 48].map(function (size) { return (_jsx(Icon, { svg: ChevronIcon, size: size }, size)); }) })); },
127
+ };
128
+ /**
129
+ * Icons inherit `currentColor` for their stroke/fill, so they automatically
130
+ * adapt to the surrounding text color.
131
+ */
132
+ export var InheritedColor = {
133
+ parameters: {
134
+ docs: {
135
+ source: {
136
+ code: "<p style={{ color: '#3b82f6' }}>\n <Icon svg={StarIcon} size={16} /> Blue text with a blue icon\n</p>\n<p style={{ color: '#ef4444' }}>\n <Icon svg={StarIcon} size={16} /> Red text with a red icon\n</p>",
137
+ },
138
+ },
139
+ },
140
+ render: function () { return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '0.5rem' }, children: [_jsxs("p", { style: { color: '#3b82f6', margin: 0, display: 'flex', alignItems: 'center', gap: '0.375rem', fontFamily: 'sans-serif' }, children: [_jsx(Icon, { svg: StarIcon, size: 16 }), " Blue text with a blue icon"] }), _jsxs("p", { style: { color: '#ef4444', margin: 0, display: 'flex', alignItems: 'center', gap: '0.375rem', fontFamily: 'sans-serif' }, children: [_jsx(Icon, { svg: StarIcon, size: 16 }), " Red text with a red icon"] })] })); },
141
+ };
package/dist/index.d.ts CHANGED
@@ -14,4 +14,5 @@ export * from './components/List';
14
14
  export * from './components/Table';
15
15
  export * from './components/Grid';
16
16
  export * from './components/Breadcrumb';
17
+ export * from './components/Icon';
17
18
  export * from './hooks/useKeyPress';
package/dist/index.js CHANGED
@@ -15,5 +15,6 @@ export * from './components/List';
15
15
  export * from './components/Table';
16
16
  export * from './components/Grid';
17
17
  export * from './components/Breadcrumb';
18
+ export * from './components/Icon';
18
19
  // Export hooks
19
20
  export * from './hooks/useKeyPress';
package/dist/styles.css CHANGED
@@ -1270,10 +1270,15 @@ a.no-text-decoration {
1270
1270
  transform: rotate(180deg);
1271
1271
  }
1272
1272
  .dds-collapser-content-wrapper {
1273
- overflow: hidden;
1274
- transition: height 0.3s ease;
1273
+ display: grid;
1274
+ grid-template-rows: 0fr;
1275
+ transition: grid-template-rows 0.3s ease;
1276
+ }
1277
+ .dds-collapser-content-wrapper--open {
1278
+ grid-template-rows: 1fr;
1275
1279
  }
1276
1280
  .dds-collapser-content {
1281
+ overflow: hidden;
1277
1282
  border-top: 1px solid var(--dds-collapser-border);
1278
1283
  padding: var(--dds-collapser-content-padding);
1279
1284
  color: var(--dds-collapser-text);
@@ -2272,4 +2277,15 @@ a.dds-breadcrumb-link:hover {
2272
2277
  display: flex;
2273
2278
  }
2274
2279
  }
2280
+ .dds-icon {
2281
+ display: inline-flex;
2282
+ align-items: center;
2283
+ justify-content: center;
2284
+ flex-shrink: 0;
2285
+ }
2286
+ /* Ensure an SVG injected via a raw string fills the container */
2287
+ .dds-icon svg {
2288
+ width: 100%;
2289
+ height: 100%;
2290
+ }
2275
2291
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roadlittledawn/docs-design-system-react",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "license": "MIT",
5
5
  "description": "React components for documentation design system",
6
6
  "repository": {