@keyfunc/keybricks-ds 1.0.2 → 1.0.4
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/DEPLOY.md +3 -3
- package/INTEGRATION.md +24 -5
- package/_adherence.oxlintrc.json +21 -6
- package/components/_cards/radio.card.html +46 -0
- package/components/_cards/segmented-control.card.html +39 -0
- package/components/_cards/toggles.card.html +44 -55
- package/components/_shared/src-buttons.js +1 -1
- package/components/_shared/src-data.js +1 -1
- package/components/_shared/src-display.js +1 -1
- package/components/_shared/src-feedback.js +1 -1
- package/components/_shared/src-forms.js +1 -1
- package/components/_shared/src-navigation.js +1 -1
- package/components/_shared/src-pipeline.js +1 -1
- package/components/_shared/src-tabs.js +1 -1
- package/components/_shared/view-source.js +9 -4
- package/components/_vue/index.mjs +21 -0
- package/components/buttons/Button.vue +0 -20
- package/components/buttons/ButtonGroup.vue +16 -0
- package/components/data/KeyValue.vue +0 -16
- package/components/data/KeyValueList.vue +12 -0
- package/components/data/ListRow.vue +1 -1
- package/components/display/AreaChart.vue +14 -0
- package/components/display/Avatar.vue +0 -17
- package/components/display/AvatarGroup.vue +13 -0
- package/components/display/Badge.vue +0 -34
- package/components/display/Card.vue +0 -15
- package/components/display/Donut.vue +17 -0
- package/components/display/IconTile.vue +13 -0
- package/components/display/MiniBars.vue +11 -0
- package/components/display/ProgressBar.vue +0 -54
- package/components/display/StatCard.vue +11 -0
- package/components/display/StatusBadge.vue +11 -0
- package/components/feedback/ActivityItem.vue +1 -32
- package/components/feedback/Alert.vue +0 -36
- package/components/feedback/DotActivity.vue +15 -0
- package/components/feedback/EmptyState.vue +12 -0
- package/components/feedback/LogViewer.vue +8 -0
- package/components/feedback/Modal.vue +0 -45
- package/components/feedback/Skeleton.vue +6 -0
- package/components/feedback/Spinner.vue +6 -0
- package/components/feedback/Toast.vue +19 -0
- package/components/forms/Combobox.vue +1 -1
- package/components/forms/DatePicker.vue +1 -1
- package/components/forms/Input.jsx +2 -1
- package/components/forms/Input.vue +2 -24
- package/components/forms/Radio.d.ts +28 -0
- package/components/forms/Radio.jsx +67 -0
- package/components/forms/Radio.vue +17 -8
- package/components/forms/RadioGroup.vue +22 -4
- package/components/forms/Switch.d.ts +32 -24
- package/components/forms/Switch.jsx +87 -35
- package/components/forms/Switch.vue +56 -11
- package/components/forms/Textarea.vue +20 -0
- package/components/forms/forms.prompt.md +4 -1
- package/components/forms/input-base.js +3 -0
- package/components/index.d.ts +1 -0
- package/components/index.js +2 -1
- package/components/navigation/Breadcrumb.vue +0 -22
- package/components/navigation/PageHeading.vue +9 -0
- package/components/navigation/PageToolbar.vue +15 -0
- package/components/navigation/ThemeToggle.vue +18 -0
- package/components/navigation/TopBar.vue +0 -32
- package/components/pipeline/NodeCard.vue +2 -1
- package/components/pipeline/PaletteItem.vue +1 -1
- package/components/pipeline/PalettePanel.jsx +2 -8
- package/components/pipeline/PalettePanel.vue +1 -8
- package/components/pipeline/pipeline-catalog.js +8 -0
- package/components/tabs/PanelTabs.vue +10 -0
- package/components/tabs/PillTabs.vue +10 -0
- package/components/tabs/SegmentedControl.d.ts +10 -3
- package/components/tabs/SegmentedControl.jsx +24 -9
- package/components/tabs/SegmentedControl.vue +19 -7
- package/components/tabs/Tabs.vue +0 -28
- package/components/tabs/tabs.prompt.md +3 -1
- package/package.json +1 -1
- package/tokens/tokens.json +1 -1
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
import Icon from '../icons/Icon.vue'
|
|
4
|
-
import {
|
|
4
|
+
import { PIPELINE_CATALOG } from './pipeline-catalog.js'
|
|
5
5
|
import ViewToggle from '../tabs/ViewToggle.vue'
|
|
6
6
|
import PaletteItem from './PaletteItem.vue'
|
|
7
7
|
import PaletteListItem from './PaletteListItem.vue'
|
|
8
8
|
|
|
9
|
-
export const PIPELINE_CATALOG = [
|
|
10
|
-
{ label: 'Data Source', icon: 'table', hue: 'source', items: [{ label: 'Detail Sheet', icon: 'table' }, { label: 'Model', icon: 'layers' }, { label: 'Dimension', icon: 'grid-3x3' }] },
|
|
11
|
-
{ label: 'Data Join', icon: 'text-search', hue: 'join', items: [{ label: 'Lookup Data', icon: 'text-search' }, { label: 'Association Match', icon: 'shuffle' }] },
|
|
12
|
-
{ label: 'Data Transformation', icon: 'sliders-horizontal', hue: 'transform', items: [{ label: 'Field Value Calculation', icon: 'sliders-horizontal' }, { label: 'Field Split', icon: 'split' }, { label: 'Row to Column', icon: 'arrow-right-left' }, { label: 'Column to Row', icon: 'menu' }, { label: 'Multi-Condition Judge', icon: 'funnel' }, { label: 'Regional Calculation', icon: 'chart-no-axes-column' }, { label: 'Advanced Transformation', icon: 'trending-up' }, { label: 'JS Enhanced Processing', icon: 'code' }] },
|
|
13
|
-
{ label: 'Data Merge', icon: 'database', hue: 'merge', items: [{ label: 'Group Calculation', icon: 'database' }, { label: 'Concat Merge', icon: 'merge' }] },
|
|
14
|
-
{ label: 'Data Output', icon: 'table', hue: 'output', items: [{ label: 'Detail Sheet', icon: 'table' }, { label: 'Model', icon: 'layers' }, { label: 'Dimension', icon: 'grid-3x3' }] },
|
|
15
|
-
]
|
|
16
9
|
// 240px "Components" palette panel with list/grid toggle and collapsible hued categories.
|
|
17
10
|
const props = defineProps({ catalog: { type: Array, default: () => PIPELINE_CATALOG }, height: { type: [String, Number], default: '100%' }, style: Object })
|
|
18
11
|
const emit = defineEmits(['pick'])
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The full component set of the pipeline editor, verbatim from the file. Shared by PalettePanel.vue; also re-exported by PalettePanel.jsx.
|
|
2
|
+
export const PIPELINE_CATALOG = [
|
|
3
|
+
{ label: 'Data Source', icon: 'table', hue: 'source', items: [{ label: 'Detail Sheet', icon: 'table' }, { label: 'Model', icon: 'layers' }, { label: 'Dimension', icon: 'grid-3x3' }] },
|
|
4
|
+
{ label: 'Data Join', icon: 'text-search', hue: 'join', items: [{ label: 'Lookup Data', icon: 'text-search' }, { label: 'Association Match', icon: 'shuffle' }] },
|
|
5
|
+
{ label: 'Data Transformation', icon: 'sliders-horizontal', hue: 'transform', items: [{ label: 'Field Value Calculation', icon: 'sliders-horizontal' }, { label: 'Field Split', icon: 'split' }, { label: 'Row to Column', icon: 'arrow-right-left' }, { label: 'Column to Row', icon: 'menu' }, { label: 'Multi-Condition Judge', icon: 'funnel' }, { label: 'Regional Calculation', icon: 'chart-no-axes-column' }, { label: 'Advanced Transformation', icon: 'trending-up' }, { label: 'JS Enhanced Processing', icon: 'code' }] },
|
|
6
|
+
{ label: 'Data Merge', icon: 'database', hue: 'merge', items: [{ label: 'Group Calculation', icon: 'database' }, { label: 'Concat Merge', icon: 'merge' }] },
|
|
7
|
+
{ label: 'Data Output', icon: 'table', hue: 'output', items: [{ label: 'Detail Sheet', icon: 'table' }, { label: 'Model', icon: 'layers' }, { label: 'Dimension', icon: 'grid-3x3' }] },
|
|
8
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
|
|
3
|
+
const emit = defineEmits(['change'])
|
|
4
|
+
</script>
|
|
5
|
+
<template>
|
|
6
|
+
<div :style="{ display: 'flex', borderBottom: '1px solid var(--border-subtle)', ...style }">
|
|
7
|
+
<button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
|
|
8
|
+
:style="{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '8px 12px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--action-primary)' : 'var(--text-secondary)' }">{{ t }}</button>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
|
|
3
|
+
const emit = defineEmits(['change'])
|
|
4
|
+
</script>
|
|
5
|
+
<template>
|
|
6
|
+
<div :style="{ display: 'flex', gap: 8, ...style }">
|
|
7
|
+
<button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
|
|
8
|
+
:style="{ background: t === active ? 'var(--bg-active)' : 'transparent', border: 'none', cursor: 'pointer', height: 28, padding: '0 11px', borderRadius: 'var(--radius-md)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--text-heading)' : 'var(--text-secondary)' }">{{ t }}</button>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
/** Inset segmented control with a raised active segment
|
|
2
|
+
/** Inset segmented control with a raised active segment — switches views of the same data, applies instantly. ← → move between enabled segments. */
|
|
3
3
|
export interface SegmentedControlProps {
|
|
4
|
-
/** Plain labels, or {label, icon} entries */
|
|
5
|
-
options?: (string | { label: string; icon?: string })[];
|
|
4
|
+
/** Plain labels, or { label, icon?, disabled? } entries. Value is matched by label. */
|
|
5
|
+
options?: (string | { label: string; icon?: string; disabled?: boolean })[];
|
|
6
6
|
value?: string;
|
|
7
7
|
onChange?: (value: string) => void;
|
|
8
|
+
/** md 32px (default) · sm 26px */
|
|
8
9
|
size?: "sm" | "md";
|
|
10
|
+
/** Hide labels; each label becomes the segment's aria-label and tooltip */
|
|
11
|
+
iconOnly?: boolean;
|
|
12
|
+
/** Greys the whole control */
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
/** Stretch to the container, segments share the width evenly */
|
|
15
|
+
fullWidth?: boolean;
|
|
9
16
|
style?: React.CSSProperties;
|
|
10
17
|
}
|
|
11
18
|
export declare function SegmentedControl(props: SegmentedControlProps): JSX.Element;
|
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Icon } from "../icons/Icon.jsx";
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Segmented control — inset track, raised active segment. Switches views of the same data, applies instantly.
|
|
6
|
+
* options: string[] | { label, icon?, disabled? }[] value/onChange by label
|
|
7
|
+
* size "md" (32) | "sm" (26) · iconOnly hides labels (label becomes aria-label / title)
|
|
8
|
+
* disabled greys the whole control · fullWidth stretches segments evenly
|
|
9
|
+
* Keyboard: ← → move between enabled segments.
|
|
10
|
+
*/
|
|
11
|
+
export function SegmentedControl({ options = [], value, onChange, size = "md", iconOnly, disabled, fullWidth, style }) {
|
|
6
12
|
const h = size === "sm" ? 26 : 32;
|
|
13
|
+
const opts = options.map((o) => (typeof o === "string" ? { label: o } : o));
|
|
14
|
+
const enabled = opts.filter((o) => !o.disabled);
|
|
15
|
+
const onKey = (e) => {
|
|
16
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight" || disabled || !enabled.length) return;
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
const i = enabled.findIndex((o) => o.label === value);
|
|
19
|
+
const n = e.key === "ArrowRight" ? enabled[(i + 1) % enabled.length] : enabled[(i - 1 + enabled.length) % enabled.length];
|
|
20
|
+
onChange && onChange(n.label);
|
|
21
|
+
};
|
|
7
22
|
return (
|
|
8
|
-
<div style={{ display: "inline-flex", padding: 2, gap: 2, background: "var(--bg-inset)", border: "1px solid var(--border-control)", borderRadius: "var(--radius-md)", boxSizing: "border-box", ...style }}>
|
|
9
|
-
{
|
|
10
|
-
const
|
|
11
|
-
const is = value === label;
|
|
23
|
+
<div role="tablist" aria-disabled={disabled || undefined} onKeyDown={onKey} style={{ display: fullWidth ? "flex" : "inline-flex", padding: 2, gap: 2, background: "var(--bg-inset)", border: "1px solid var(--border-control)", borderRadius: "var(--radius-md)", boxSizing: "border-box", opacity: disabled ? .5 : 1, ...style }}>
|
|
24
|
+
{opts.map((o) => {
|
|
25
|
+
const is = value === o.label, off = disabled || o.disabled;
|
|
12
26
|
return (
|
|
13
|
-
<button key={label} type="button"
|
|
14
|
-
|
|
15
|
-
{
|
|
27
|
+
<button key={o.label} type="button" role="tab" aria-selected={is} aria-label={iconOnly ? o.label : undefined} title={iconOnly ? o.label : undefined} disabled={off} tabIndex={is ? 0 : -1}
|
|
28
|
+
onClick={() => !off && onChange && onChange(o.label)}
|
|
29
|
+
style={{ height: h - 6, padding: iconOnly ? "0 8px" : "0 10px", flex: fullWidth ? 1 : "none", justifyContent: "center", display: "inline-flex", alignItems: "center", gap: 6, borderRadius: "var(--radius-sm)", border: "none", cursor: off ? "not-allowed" : "pointer", background: is ? "var(--bg-surface)" : "transparent", boxShadow: is ? "0 1px 2px rgba(0,0,0,.08)" : "none", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: size === "sm" ? "var(--text-xs)" : "var(--text-sm)", color: is ? "var(--text-heading)" : "var(--text-secondary)", opacity: o.disabled && !disabled ? .45 : 1, whiteSpace: "nowrap", transition: "var(--t-hover)" }}>
|
|
30
|
+
{o.icon ? <Icon name={o.icon} size={13} /> : null}{iconOnly ? null : o.label}
|
|
16
31
|
</button>
|
|
17
32
|
);
|
|
18
33
|
})}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
2
3
|
import Icon from '../icons/Icon.vue'
|
|
3
|
-
// Segmented control — inset track, raised active segment.
|
|
4
|
-
|
|
4
|
+
// Segmented control — inset track, raised active segment. Switches views of the same data, applies instantly.
|
|
5
|
+
// options: string[] | { label, icon?, disabled? }[] · v-model by label · size md (32) | sm (26)
|
|
6
|
+
// iconOnly hides labels (label → aria-label/title) · disabled greys the control · fullWidth stretches segments · ← → keyboard.
|
|
7
|
+
const props = defineProps({ options: { type: Array, default: () => [] }, modelValue: String, size: { type: String, default: 'md' }, iconOnly: Boolean, disabled: Boolean, fullWidth: Boolean, style: Object })
|
|
5
8
|
const emit = defineEmits(['update:modelValue'])
|
|
6
|
-
|
|
9
|
+
const opts = computed(() => props.options.map(o => typeof o === 'string' ? { label: o } : o))
|
|
10
|
+
const enabled = computed(() => opts.value.filter(o => !o.disabled))
|
|
11
|
+
const h = computed(() => props.size === 'sm' ? 26 : 32)
|
|
12
|
+
const pick = (o) => { if (!(props.disabled || o.disabled)) emit('update:modelValue', o.label) }
|
|
13
|
+
function onKey(e) {
|
|
14
|
+
if ((e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') || props.disabled || !enabled.value.length) return
|
|
15
|
+
e.preventDefault()
|
|
16
|
+
const i = enabled.value.findIndex(o => o.label === props.modelValue), n = enabled.value.length
|
|
17
|
+
emit('update:modelValue', (e.key === 'ArrowRight' ? enabled.value[(i + 1) % n] : enabled.value[(i - 1 + n) % n]).label)
|
|
18
|
+
}
|
|
7
19
|
</script>
|
|
8
20
|
<template>
|
|
9
|
-
<div :style="{ display: 'inline-flex', padding:
|
|
10
|
-
<button v-for="o in
|
|
11
|
-
:style="{ height: (
|
|
12
|
-
<Icon v-if="
|
|
21
|
+
<div role="tablist" :aria-disabled="disabled || undefined" @keydown="onKey" :style="{ display: fullWidth ? 'flex' : 'inline-flex', padding: '2px', gap: '2px', background: 'var(--bg-inset)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxSizing: 'border-box', opacity: disabled ? .5 : 1, ...style }">
|
|
22
|
+
<button v-for="o in opts" :key="o.label" type="button" role="tab" :aria-selected="modelValue === o.label" :aria-label="iconOnly ? o.label : undefined" :title="iconOnly ? o.label : undefined" :disabled="disabled || o.disabled" :tabindex="modelValue === o.label ? 0 : -1" @click="pick(o)"
|
|
23
|
+
:style="{ height: (h - 6) + 'px', padding: iconOnly ? '0 8px' : '0 10px', flex: fullWidth ? 1 : 'none', justifyContent: 'center', display: 'inline-flex', alignItems: 'center', gap: '6px', borderRadius: 'var(--radius-sm)', border: 'none', cursor: (disabled || o.disabled) ? 'not-allowed' : 'pointer', background: modelValue === o.label ? 'var(--bg-surface)' : 'transparent', boxShadow: modelValue === o.label ? '0 1px 2px rgba(0,0,0,.08)' : 'none', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: size === 'sm' ? 'var(--text-xs)' : 'var(--text-sm)', color: modelValue === o.label ? 'var(--text-heading)' : 'var(--text-secondary)', opacity: o.disabled && !disabled ? .45 : 1, whiteSpace: 'nowrap', transition: 'var(--t-hover)' }">
|
|
24
|
+
<Icon v-if="o.icon" :name="o.icon" :size="13" /><template v-if="!iconOnly">{{ o.label }}</template>
|
|
13
25
|
</button>
|
|
14
26
|
</div>
|
|
15
27
|
</template>
|
package/components/tabs/Tabs.vue
CHANGED
|
@@ -9,31 +9,3 @@ const emit = defineEmits(['change'])
|
|
|
9
9
|
:style="{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '9px 16px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', color: t === active ? 'var(--action-primary)' : 'var(--text-body)' }">{{ t }}</button>
|
|
10
10
|
</div>
|
|
11
11
|
</template>
|
|
12
|
-
|
|
13
|
-
<!-- PanelTabs — compact panel tabs (Node Info · Calc Rules · Output Fields · SQL Preview), 12px. -->
|
|
14
|
-
<!--
|
|
15
|
-
<script setup name="PanelTabs">
|
|
16
|
-
defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
|
|
17
|
-
const emit = defineEmits(['change'])
|
|
18
|
-
</script>
|
|
19
|
-
<template>
|
|
20
|
-
<div :style="{ display: 'flex', borderBottom: '1px solid var(--border-subtle)', ...style }">
|
|
21
|
-
<button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
|
|
22
|
-
:style="{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '8px 12px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--action-primary)' : 'var(--text-secondary)' }">{{ t }}</button>
|
|
23
|
-
</div>
|
|
24
|
-
</template>
|
|
25
|
-
-->
|
|
26
|
-
|
|
27
|
-
<!-- PillTabs — text pill filter tabs (For Review · To be Submitted · For Approval). Active = filled pill. -->
|
|
28
|
-
<!--
|
|
29
|
-
<script setup name="PillTabs">
|
|
30
|
-
defineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })
|
|
31
|
-
const emit = defineEmits(['change'])
|
|
32
|
-
</script>
|
|
33
|
-
<template>
|
|
34
|
-
<div :style="{ display: 'flex', gap: 8, ...style }">
|
|
35
|
-
<button v-for="t in tabs" :key="t" type="button" @click="emit('change', t)"
|
|
36
|
-
:style="{ background: t === active ? 'var(--bg-active)' : 'transparent', border: 'none', cursor: 'pointer', height: 28, padding: '0 11px', borderRadius: 'var(--radius-md)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--text-heading)' : 'var(--text-secondary)' }">{{ t }}</button>
|
|
37
|
-
</div>
|
|
38
|
-
</template>
|
|
39
|
-
-->
|
|
@@ -7,8 +7,10 @@ Tab systems: `Tabs` (13px underline — page level), `PanelTabs` (12px underline
|
|
|
7
7
|
<ViewToggle value={view} onChange={setView}/>
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
`SegmentedControl` (addition) for view/range switches
|
|
10
|
+
`SegmentedControl` (addition) for view/range switches — same data, instant; never a saved value and never page navigation. `iconOnly`, per-option `disabled`, `fullWidth`:
|
|
11
11
|
|
|
12
12
|
```jsx
|
|
13
13
|
<SegmentedControl options={[{label:"List",icon:"list"},{label:"Grid",icon:"layout-grid"}]} value={v} onChange={setV}/>
|
|
14
|
+
<SegmentedControl iconOnly options={[{icon:"table",label:"Table"},{icon:"chart-column",label:"Chart"}]} value={m} onChange={setM}/>
|
|
15
|
+
<SegmentedControl fullWidth options={["Overview","Details","History"]} value={t} onChange={setT}/>
|
|
14
16
|
```
|
package/package.json
CHANGED