diff --git a/.changeset/sortable-timeline-mcp-spec.md b/.changeset/sortable-timeline-mcp-spec.md new file mode 100644 index 00000000..b522b285 --- /dev/null +++ b/.changeset/sortable-timeline-mcp-spec.md @@ -0,0 +1,5 @@ +--- +"@sanring/cli": minor +--- + +Add `sortable` for pointer and keyboard reordering. Timeline separators now draw the rail and default marker. MCP adds `get_component_spec` so agents get authoring contracts instead of inventing APIs. diff --git a/README.md b/README.md index 49ab8131..0a3e6c6d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Components are copied directly into your source code — you own them and can mo ## Available Components -accordion · alert · alert-dialog · aspect-ratio · avatar · badge · breadcrumb · button · calendar · card · carousel · checkbox · collapsible · combobox · command · context-menu · date-picker · dialog · divider · dropdown-menu · field · file-upload · hover-card · input · label · link · navigation-menu · otp-input · pagination · popover · progress · radio · resizable · scroll-area · select · sheet · sidebar · skeleton · slider · spinner · stepper · switch · table · tabs · tag · textarea · timeline · toast · toggle · tooltip · transfer · tree +accordion · alert · alert-dialog · aspect-ratio · avatar · badge · breadcrumb · button · calendar · card · carousel · checkbox · collapsible · combobox · command · context-menu · date-picker · dialog · divider · dropdown-menu · field · file-upload · hover-card · input · label · link · navigation-menu · otp-input · pagination · popover · progress · radio · resizable · scroll-area · select · sheet · sidebar · skeleton · slider · sortable · spinner · stepper · switch · table · tabs · tag · textarea · timeline · toast · toggle · tooltip · transfer · tree --- diff --git a/apps/docs/src/app/app.routes.ts b/apps/docs/src/app/app.routes.ts index 6c0da791..1293e6a2 100644 --- a/apps/docs/src/app/app.routes.ts +++ b/apps/docs/src/app/app.routes.ts @@ -353,6 +353,13 @@ export const routes: Routes = [ (m) => m.SliderPageComponent, ), }, + { + path: 'sortable', + loadComponent: () => + import('./pages/components/sortable/sortable-page.component').then( + (m) => m.SortablePageComponent, + ), + }, { path: 'spinner', loadComponent: () => diff --git a/apps/docs/src/app/i18n/locales/en/common.ts b/apps/docs/src/app/i18n/locales/en/common.ts index acbc00a8..0c400beb 100644 --- a/apps/docs/src/app/i18n/locales/en/common.ts +++ b/apps/docs/src/app/i18n/locales/en/common.ts @@ -78,6 +78,7 @@ export const commonTranslations = { 'component.sidebar': 'Sidebar', 'component.skeleton': 'Skeleton', 'component.slider': 'Slider', + 'component.sortable': 'Sortable', 'component.stepper': 'Stepper', 'component.switch': 'Switch', 'component.table': 'Table', diff --git a/apps/docs/src/app/i18n/locales/en/components/index.ts b/apps/docs/src/app/i18n/locales/en/components/index.ts index 06e96de6..4778d891 100644 --- a/apps/docs/src/app/i18n/locales/en/components/index.ts +++ b/apps/docs/src/app/i18n/locales/en/components/index.ts @@ -37,6 +37,7 @@ import { sheetTranslations } from './sheet'; import { sidebarTranslations } from './sidebar'; import { skeletonTranslations } from './skeleton'; import { sliderTranslations } from './slider'; +import { sortableTranslations } from './sortable'; import { spinnerTranslations } from './spinner'; import { stepperTranslations } from './stepper'; import { switchTranslations } from './switch'; @@ -91,6 +92,7 @@ export const componentTranslations = { ...sidebarTranslations, ...skeletonTranslations, ...sliderTranslations, + ...sortableTranslations, ...spinnerTranslations, ...stepperTranslations, ...switchTranslations, diff --git a/apps/docs/src/app/i18n/locales/en/components/sortable.ts b/apps/docs/src/app/i18n/locales/en/components/sortable.ts new file mode 100644 index 00000000..2239d40c --- /dev/null +++ b/apps/docs/src/app/i18n/locales/en/components/sortable.ts @@ -0,0 +1,40 @@ +export const sortableTranslations = { + 'sortable.description': + 'A single-list reorder primitive. Pointer drag uses Angular CDK; keyboard arrows move the focused item. Cards and other content compose through the item directive.', + 'sortable.demo.handle': 'Drag handle', + 'sortable.demo.horizontal': 'Horizontal', + 'sortable.demo.alpha': 'Inbox digest', + 'sortable.demo.alphaDescription': 'Summarize unread threads each morning.', + 'sortable.demo.beta': 'Release checklist', + 'sortable.demo.betaDescription': 'Gate publish on review and smoke tests.', + 'sortable.demo.gamma': 'Weekly report', + 'sortable.demo.gammaDescription': 'Ship a compact status note on Fridays.', + 'sortable.demo.reorder': 'Reorder', + 'sortable.installation.description': + 'Add the component with the CLI, then import the list, item, and optional handle directives.', + 'sortable.usage.description': + 'Bind the same array to data and @for. On sorted, replace the array so Angular can reconcile the new order.', + 'sortable.composition.description': + 'The list owns order. Items are any host element. An optional handle limits pointer dragging to that control.', + 'sortable.api.description': 'Inputs, outputs, and directives for a single sortable list.', + 'sortable.api.class.description': 'Additional classes merged with the list host.', + 'sortable.api.data.description': + 'Required array to reorder in place. sorted emits a shallow copy after each move.', + 'sortable.api.orientation.description': + 'Layout and lock axis for pointer dragging. vertical uses Arrow Up/Down; horizontal uses Arrow Left/Right.', + 'sortable.api.disabled.description': 'Disables pointer and keyboard reordering for the whole list.', + 'sortable.api.sorted.description': 'Emits a new array after a successful reorder.', + 'sortable.api.itemDisabled.description': 'Disables one item without locking the rest of the list.', + 'sortable.api.handle.description': + 'Optional drag handle. When present, pointer dragging starts from the handle instead of the whole item.', + 'sortable.accessibility.description': + 'The list uses role=list and items use role=listitem. Disabled items leave the tab order. Prefer a named handle button when the item is a dense card.', + 'sortable.keyboard.description': 'Focus an item, then move it with arrows. Tab walks the list.', + 'sortable.keyboard.arrowsVertical': + 'Moves the focused item up or down in a vertical list.', + 'sortable.keyboard.arrowsHorizontal': + 'Moves the focused item left or right in a horizontal list.', + 'sortable.keyboard.tabShiftTab': 'Moves focus between sortable items in document order.', + 'sortable.stateModel.description': + 'data is the source of order. Reorder mutates that array and emits sorted with a copy. disabled on the list or an item blocks moves without clearing data.', +} as const; diff --git a/apps/docs/src/app/i18n/locales/en/components/timeline.ts b/apps/docs/src/app/i18n/locales/en/components/timeline.ts index d362ad40..cf0599ec 100644 --- a/apps/docs/src/app/i18n/locales/en/components/timeline.ts +++ b/apps/docs/src/app/i18n/locales/en/components/timeline.ts @@ -2,13 +2,14 @@ export const timelineTranslations = { 'timeline.description': 'Composable timeline primitives for chronological events, activity feeds, and process milestones.', 'timeline.examples.basic.description': - 'Use sanringTimeline with native list markup, then compose separators, markers, connectors, and content.', + 'A vertical list with a built-in rail. Leave the separator empty for a default marker, or project an icon or avatar.', 'timeline.usage.description': - 'Import the timeline directives and apply them to list or div-based activity markup.', + 'Each item is a separator plus content. The separator draws the connector; project a node only when the default marker is not enough.', 'timeline.installation.description': 'Install the timeline primitives and compose item, separator, and content directives where each event renders.', 'timeline.demo.horizontal': 'Horizontal', 'timeline.demo.divBased': 'Div-based timeline', + 'timeline.demo.reorder': 'Reorder', 'timeline.demo.releaseActivity': 'Release activity', 'timeline.demo.releaseActivityDescription': 'A compact activity trail for release notes and registry updates.', @@ -37,6 +38,8 @@ export const timelineTranslations = { 'timeline.demo.qaTitle': 'Quality review', 'timeline.demo.qaMeta': 'QA pass', 'timeline.demo.qaDescription': 'Visual spacing and empty states were reviewed before publishing.', + 'timeline.demo.reorder.description': + 'Compose with sortable and drag the grip to reorder. Timeline stays layout; sortable owns order. Also install sortable.', 'timeline.api.description': 'Inputs supported by the Timeline directives.', 'timeline.api.orientation.description': 'Controls whether items stack vertically or horizontally.', @@ -46,7 +49,7 @@ export const timelineTranslations = { 'Additional classes merged with each separator wrapper.', 'timeline.api.contentClass.description': 'Additional classes merged with each content container.', 'timeline.accessibility.description': - 'Timeline is a visual layout primitive with no built-in ARIA role. When timeline items represent an ordered sequence, wrap the content in a native
    or