From f52f93708990a9b44ad72009f293b0131806b8b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Apr 2026 22:08:33 +0000 Subject: [PATCH 1/5] fix(input,chip): align states and variants with Figma - input: visible dark-mode default border, focus override stays primary in dark, brighter error text (error-600 light / error-300 dark), icon color dims correctly when disabled - chip: add `official` variant (V-400 secondary) for "Oficial" badges, matching the Figma legend --- crates/leptos/src/chip.rs | 1 + js/react/lib/components/chip/chip.const.ts | 2 ++ .../lib/components/chip/chip.showcase.tsx | 9 ++++++- styles/components/chip.css | 8 ++++++ styles/components/input.css | 26 ++++++++++++------- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/crates/leptos/src/chip.rs b/crates/leptos/src/chip.rs index 1e38387..ed87320 100644 --- a/crates/leptos/src/chip.rs +++ b/crates/leptos/src/chip.rs @@ -8,6 +8,7 @@ use crate::icons::{Location, StarBold}; pub enum Variant { #[default] Featured, + Official, Numeric, Description, Location, diff --git a/js/react/lib/components/chip/chip.const.ts b/js/react/lib/components/chip/chip.const.ts index 33aaaee..90e465e 100644 --- a/js/react/lib/components/chip/chip.const.ts +++ b/js/react/lib/components/chip/chip.const.ts @@ -2,6 +2,7 @@ import { Location, StarBold } from "@/icons"; export const variants = { featured: "rustlanges-chip--featured", + official: "rustlanges-chip--official", numeric: "rustlanges-chip--numeric", description: "rustlanges-chip--description", location: "rustlanges-chip--location", @@ -12,6 +13,7 @@ export type ChipVariants = keyof typeof variants; export const icons = { featured: StarBold, + official: null, numeric: null, description: null, location: Location, diff --git a/js/react/lib/components/chip/chip.showcase.tsx b/js/react/lib/components/chip/chip.showcase.tsx index 0ab44af..6ae3cd8 100644 --- a/js/react/lib/components/chip/chip.showcase.tsx +++ b/js/react/lib/components/chip/chip.showcase.tsx @@ -6,7 +6,14 @@ registerCase("Chip", { variant: { kind: "string", default: "featured", - options: ["featured", "numeric", "description", "location", "small"], + options: [ + "featured", + "official", + "numeric", + "description", + "location", + "small", + ], }, label: { kind: "string", diff --git a/styles/components/chip.css b/styles/components/chip.css index 6c53cb3..6e8d3dc 100644 --- a/styles/components/chip.css +++ b/styles/components/chip.css @@ -12,6 +12,14 @@ @apply p-[4px] px-[12px]; } + .rustlanges-chip--official { + @apply rounded-[20px] border; + @apply bg-secondary-400 border-black text-black; + @apply min-h-[32px]; + @apply text-sm; + @apply p-[4px] px-[12px]; + } + .rustlanges-chip--numeric { @apply rounded-[20px] border; @apply bg-primary-200 border-black text-black; diff --git a/styles/components/input.css b/styles/components/input.css index ec68e56..e6b0c69 100644 --- a/styles/components/input.css +++ b/styles/components/input.css @@ -3,20 +3,26 @@ @apply flex items-center gap-2 rounded-xl px-[10px] py-3 transition-colors; @apply bg-light border-1 border-black text-black hover:bg-neutral-100; @apply dark:bg-neutral-950 dark:text-white dark:hover:bg-neutral-900; + @apply dark:border-neutral-800; - &:has(> :active), - &:has(> :focus) { - @apply border-primary-500; + &:has(> :focus-visible), + &:has(> :focus), + &:has(> :active:not(:disabled)) { + @apply border-primary-500 dark:border-primary-500; } @variant has-disabled { - @apply cursor-not-allowed border-neutral-400 bg-neutral-100 text-neutral-600; - @apply dark:bg-neutral-900 dark:text-neutral-400; + @apply cursor-not-allowed border-neutral-300 bg-neutral-100 text-neutral-600; + @apply hover:bg-neutral-100; + @apply dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400; + @apply dark:hover:bg-neutral-900; } } - .rustlanges-input--error { - @apply border-error-600; + .rustlanges-input--error, + .rustlanges-input--error:has(> :focus), + .rustlanges-input--error:has(> :active) { + @apply border-error-600 dark:border-error-500; } .rustlanges-input__container { @@ -24,7 +30,7 @@ } .rustlanges-input__error { - @apply text-error-800 dark:text-error-300 mt-1 text-sm; + @apply text-error-600 dark:text-error-300 mt-1 text-sm; } .rustlanges-input__inner { @@ -34,10 +40,10 @@ } .rustlanges-input__icon { - @apply text-neutral-600; + @apply text-neutral-600 dark:text-neutral-400; &:has(+ :disabled) { - @apply text-neutral-400; + @apply text-neutral-400 dark:text-neutral-600; } } } From 50b14a8d4cbab3b8f32bd12221ecdbb6cd4a48b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Apr 2026 22:16:24 +0000 Subject: [PATCH 2/5] feat(vue): add Input, Chip, Tag, Avatar, Collaborators components Port the same Figma-aligned components already available in the React lib so the Vue lib reaches feature parity. Components reuse the shared CSS classes from styles/, including the new chip 'official' (V-400) variant and the updated input states. - rs-input: error message + icon slot - rs-chip: featured / official / numeric / description / location / small - rs-tag: default + selected, polymorphic via `as` - rs-avatar: polymorphic via `as` - rs-collaborators: stacked avatars + GitHub source button - rs-button: support `as="a"` for link variants - icons: add rs-star-bold, rs-github --- js/vue/lib/components/rs-avatar.showcase.vue | 21 ++++++ js/vue/lib/components/rs-avatar.vue | 34 +++++++++ js/vue/lib/components/rs-button.vue | 8 ++- js/vue/lib/components/rs-chip.showcase.vue | 27 ++++++++ js/vue/lib/components/rs-chip.vue | 59 ++++++++++++++++ .../components/rs-collaborators.showcase.vue | 41 +++++++++++ js/vue/lib/components/rs-collaborators.vue | 69 +++++++++++++++++++ js/vue/lib/components/rs-input.showcase.vue | 25 +++++++ js/vue/lib/components/rs-input.vue | 53 ++++++++++++++ js/vue/lib/components/rs-tag.showcase.vue | 22 ++++++ js/vue/lib/components/rs-tag.vue | 32 +++++++++ js/vue/lib/icons/index.ts | 2 + js/vue/lib/icons/rs-github.vue | 15 ++++ js/vue/lib/icons/rs-star-bold.vue | 15 ++++ js/vue/lib/index.ts | 8 ++- js/vue/lib/showcases.ts | 5 ++ 16 files changed, 432 insertions(+), 4 deletions(-) create mode 100644 js/vue/lib/components/rs-avatar.showcase.vue create mode 100644 js/vue/lib/components/rs-avatar.vue create mode 100644 js/vue/lib/components/rs-chip.showcase.vue create mode 100644 js/vue/lib/components/rs-chip.vue create mode 100644 js/vue/lib/components/rs-collaborators.showcase.vue create mode 100644 js/vue/lib/components/rs-collaborators.vue create mode 100644 js/vue/lib/components/rs-input.showcase.vue create mode 100644 js/vue/lib/components/rs-input.vue create mode 100644 js/vue/lib/components/rs-tag.showcase.vue create mode 100644 js/vue/lib/components/rs-tag.vue create mode 100644 js/vue/lib/icons/rs-github.vue create mode 100644 js/vue/lib/icons/rs-star-bold.vue diff --git a/js/vue/lib/components/rs-avatar.showcase.vue b/js/vue/lib/components/rs-avatar.showcase.vue new file mode 100644 index 0000000..a8b9a44 --- /dev/null +++ b/js/vue/lib/components/rs-avatar.showcase.vue @@ -0,0 +1,21 @@ + + + diff --git a/js/vue/lib/components/rs-avatar.vue b/js/vue/lib/components/rs-avatar.vue new file mode 100644 index 0000000..044e177 --- /dev/null +++ b/js/vue/lib/components/rs-avatar.vue @@ -0,0 +1,34 @@ + + + diff --git a/js/vue/lib/components/rs-button.vue b/js/vue/lib/components/rs-button.vue index 7964ec0..97f5e37 100644 --- a/js/vue/lib/components/rs-button.vue +++ b/js/vue/lib/components/rs-button.vue @@ -8,13 +8,15 @@ const variants = { type ButtonVariants = keyof typeof variants; -const { variant } = defineProps<{ +const { variant, as } = defineProps<{ variant?: ButtonVariants; + as?: "button" | "a"; }>(); diff --git a/js/vue/lib/components/rs-chip.showcase.vue b/js/vue/lib/components/rs-chip.showcase.vue new file mode 100644 index 0000000..4bc7ef1 --- /dev/null +++ b/js/vue/lib/components/rs-chip.showcase.vue @@ -0,0 +1,27 @@ + + + diff --git a/js/vue/lib/components/rs-chip.vue b/js/vue/lib/components/rs-chip.vue new file mode 100644 index 0000000..90a67b9 --- /dev/null +++ b/js/vue/lib/components/rs-chip.vue @@ -0,0 +1,59 @@ + + + diff --git a/js/vue/lib/components/rs-collaborators.showcase.vue b/js/vue/lib/components/rs-collaborators.showcase.vue new file mode 100644 index 0000000..127ce48 --- /dev/null +++ b/js/vue/lib/components/rs-collaborators.showcase.vue @@ -0,0 +1,41 @@ + + + diff --git a/js/vue/lib/components/rs-collaborators.vue b/js/vue/lib/components/rs-collaborators.vue new file mode 100644 index 0000000..38c334d --- /dev/null +++ b/js/vue/lib/components/rs-collaborators.vue @@ -0,0 +1,69 @@ + + + diff --git a/js/vue/lib/components/rs-input.showcase.vue b/js/vue/lib/components/rs-input.showcase.vue new file mode 100644 index 0000000..efdd608 --- /dev/null +++ b/js/vue/lib/components/rs-input.showcase.vue @@ -0,0 +1,25 @@ + + + diff --git a/js/vue/lib/components/rs-input.vue b/js/vue/lib/components/rs-input.vue new file mode 100644 index 0000000..b5fcd6e --- /dev/null +++ b/js/vue/lib/components/rs-input.vue @@ -0,0 +1,53 @@ + + + diff --git a/js/vue/lib/components/rs-tag.showcase.vue b/js/vue/lib/components/rs-tag.showcase.vue new file mode 100644 index 0000000..1fd0c9f --- /dev/null +++ b/js/vue/lib/components/rs-tag.showcase.vue @@ -0,0 +1,22 @@ + + + diff --git a/js/vue/lib/components/rs-tag.vue b/js/vue/lib/components/rs-tag.vue new file mode 100644 index 0000000..37b5bdb --- /dev/null +++ b/js/vue/lib/components/rs-tag.vue @@ -0,0 +1,32 @@ + + + diff --git a/js/vue/lib/icons/index.ts b/js/vue/lib/icons/index.ts index d66803f..4ab9419 100644 --- a/js/vue/lib/icons/index.ts +++ b/js/vue/lib/icons/index.ts @@ -1 +1,3 @@ export { default as RsLocation } from "./rs-location.vue"; +export { default as RsStarBold } from "./rs-star-bold.vue"; +export { default as RsGithub } from "./rs-github.vue"; diff --git a/js/vue/lib/icons/rs-github.vue b/js/vue/lib/icons/rs-github.vue new file mode 100644 index 0000000..162a565 --- /dev/null +++ b/js/vue/lib/icons/rs-github.vue @@ -0,0 +1,15 @@ + diff --git a/js/vue/lib/icons/rs-star-bold.vue b/js/vue/lib/icons/rs-star-bold.vue new file mode 100644 index 0000000..468d86d --- /dev/null +++ b/js/vue/lib/icons/rs-star-bold.vue @@ -0,0 +1,15 @@ + diff --git a/js/vue/lib/index.ts b/js/vue/lib/index.ts index 2eeb370..7939e7c 100644 --- a/js/vue/lib/index.ts +++ b/js/vue/lib/index.ts @@ -1,2 +1,8 @@ -export * from "./components/rs-button.showcase.vue"; +export { default as RsButton } from "./components/rs-button.vue"; +export { default as RsInput } from "./components/rs-input.vue"; +export { default as RsChip } from "./components/rs-chip.vue"; +export { default as RsTag } from "./components/rs-tag.vue"; +export { default as RsAvatar } from "./components/rs-avatar.vue"; +export { default as RsCollaborators } from "./components/rs-collaborators.vue"; + export * from "./icons"; diff --git a/js/vue/lib/showcases.ts b/js/vue/lib/showcases.ts index 9121649..5f55dd9 100644 --- a/js/vue/lib/showcases.ts +++ b/js/vue/lib/showcases.ts @@ -1 +1,6 @@ export { default as ButtonShowcase } from "./components/rs-button.showcase.vue"; +export { default as InputShowcase } from "./components/rs-input.showcase.vue"; +export { default as ChipShowcase } from "./components/rs-chip.showcase.vue"; +export { default as TagShowcase } from "./components/rs-tag.showcase.vue"; +export { default as AvatarShowcase } from "./components/rs-avatar.showcase.vue"; +export { default as CollaboratorsShowcase } from "./components/rs-collaborators.showcase.vue"; From 2d505e0206a374009f35ad62196a8722dd80a50f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Apr 2026 22:26:43 +0000 Subject: [PATCH 3/5] feat(vue): port Card, Badge, Dropdown, ProgressBar, InputSearch, Calendar Continue Vue parity with React for the remaining Figma components: - rs-card: polymorphic via 'as', clickable / disabled flags - rs-badge: completed / reading / pending / unread variants, text / default / numeric types - rs-dropdown: status dropdown with arrow up/down, emits 'change' - rs-progress-bar: percentage label with ferris icon, invert when <25 - rs-input-search: search field + optional filter modal with tags - rs-calendar: monthly grid with single / multiple / range modes, reuses ported date utils (getCalendarDays, addMonths, subMonths) - icons: search, filter, arrow-up/down/left/right, ferris All components reuse the existing styles/components/*.css classes so themes (light/dark) and Figma-aligned states are inherited. --- js/vue/lib/components/rs-badge.showcase.vue | 25 +++ js/vue/lib/components/rs-badge.vue | 60 +++++++ js/vue/lib/components/rs-calendar-day.vue | 44 +++++ .../lib/components/rs-calendar.showcase.vue | 17 ++ js/vue/lib/components/rs-calendar.vue | 157 ++++++++++++++++++ js/vue/lib/components/rs-card.showcase.vue | 18 ++ js/vue/lib/components/rs-card.vue | 33 ++++ .../lib/components/rs-dropdown.showcase.vue | 17 ++ js/vue/lib/components/rs-dropdown.vue | 99 +++++++++++ .../components/rs-input-search.showcase.vue | 28 ++++ js/vue/lib/components/rs-input-search.vue | 106 ++++++++++++ .../components/rs-progress-bar.showcase.vue | 15 ++ js/vue/lib/components/rs-progress-bar.vue | 46 +++++ js/vue/lib/icons/index.ts | 7 + js/vue/lib/icons/rs-arrow-down.vue | 18 ++ js/vue/lib/icons/rs-arrow-left.vue | 18 ++ js/vue/lib/icons/rs-arrow-right.vue | 18 ++ js/vue/lib/icons/rs-arrow-up.vue | 15 ++ js/vue/lib/icons/rs-ferris.vue | 31 ++++ js/vue/lib/icons/rs-filter.vue | 15 ++ js/vue/lib/icons/rs-search.vue | 17 ++ js/vue/lib/index.ts | 7 + js/vue/lib/showcases.ts | 6 + js/vue/lib/utils/date.ts | 57 +++++++ 24 files changed, 874 insertions(+) create mode 100644 js/vue/lib/components/rs-badge.showcase.vue create mode 100644 js/vue/lib/components/rs-badge.vue create mode 100644 js/vue/lib/components/rs-calendar-day.vue create mode 100644 js/vue/lib/components/rs-calendar.showcase.vue create mode 100644 js/vue/lib/components/rs-calendar.vue create mode 100644 js/vue/lib/components/rs-card.showcase.vue create mode 100644 js/vue/lib/components/rs-card.vue create mode 100644 js/vue/lib/components/rs-dropdown.showcase.vue create mode 100644 js/vue/lib/components/rs-dropdown.vue create mode 100644 js/vue/lib/components/rs-input-search.showcase.vue create mode 100644 js/vue/lib/components/rs-input-search.vue create mode 100644 js/vue/lib/components/rs-progress-bar.showcase.vue create mode 100644 js/vue/lib/components/rs-progress-bar.vue create mode 100644 js/vue/lib/icons/rs-arrow-down.vue create mode 100644 js/vue/lib/icons/rs-arrow-left.vue create mode 100644 js/vue/lib/icons/rs-arrow-right.vue create mode 100644 js/vue/lib/icons/rs-arrow-up.vue create mode 100644 js/vue/lib/icons/rs-ferris.vue create mode 100644 js/vue/lib/icons/rs-filter.vue create mode 100644 js/vue/lib/icons/rs-search.vue create mode 100644 js/vue/lib/utils/date.ts diff --git a/js/vue/lib/components/rs-badge.showcase.vue b/js/vue/lib/components/rs-badge.showcase.vue new file mode 100644 index 0000000..2d20003 --- /dev/null +++ b/js/vue/lib/components/rs-badge.showcase.vue @@ -0,0 +1,25 @@ + + + diff --git a/js/vue/lib/components/rs-badge.vue b/js/vue/lib/components/rs-badge.vue new file mode 100644 index 0000000..6759fe7 --- /dev/null +++ b/js/vue/lib/components/rs-badge.vue @@ -0,0 +1,60 @@ + + + diff --git a/js/vue/lib/components/rs-calendar-day.vue b/js/vue/lib/components/rs-calendar-day.vue new file mode 100644 index 0000000..a92e075 --- /dev/null +++ b/js/vue/lib/components/rs-calendar-day.vue @@ -0,0 +1,44 @@ + + + diff --git a/js/vue/lib/components/rs-calendar.showcase.vue b/js/vue/lib/components/rs-calendar.showcase.vue new file mode 100644 index 0000000..2429165 --- /dev/null +++ b/js/vue/lib/components/rs-calendar.showcase.vue @@ -0,0 +1,17 @@ + + + diff --git a/js/vue/lib/components/rs-calendar.vue b/js/vue/lib/components/rs-calendar.vue new file mode 100644 index 0000000..fc93e2e --- /dev/null +++ b/js/vue/lib/components/rs-calendar.vue @@ -0,0 +1,157 @@ + + + diff --git a/js/vue/lib/components/rs-card.showcase.vue b/js/vue/lib/components/rs-card.showcase.vue new file mode 100644 index 0000000..ba25124 --- /dev/null +++ b/js/vue/lib/components/rs-card.showcase.vue @@ -0,0 +1,18 @@ + + + diff --git a/js/vue/lib/components/rs-card.vue b/js/vue/lib/components/rs-card.vue new file mode 100644 index 0000000..2e903cf --- /dev/null +++ b/js/vue/lib/components/rs-card.vue @@ -0,0 +1,33 @@ + + + diff --git a/js/vue/lib/components/rs-dropdown.showcase.vue b/js/vue/lib/components/rs-dropdown.showcase.vue new file mode 100644 index 0000000..47fd646 --- /dev/null +++ b/js/vue/lib/components/rs-dropdown.showcase.vue @@ -0,0 +1,17 @@ + + + diff --git a/js/vue/lib/components/rs-dropdown.vue b/js/vue/lib/components/rs-dropdown.vue new file mode 100644 index 0000000..f2c1893 --- /dev/null +++ b/js/vue/lib/components/rs-dropdown.vue @@ -0,0 +1,99 @@ + + + diff --git a/js/vue/lib/components/rs-input-search.showcase.vue b/js/vue/lib/components/rs-input-search.showcase.vue new file mode 100644 index 0000000..3678f08 --- /dev/null +++ b/js/vue/lib/components/rs-input-search.showcase.vue @@ -0,0 +1,28 @@ + + + diff --git a/js/vue/lib/components/rs-input-search.vue b/js/vue/lib/components/rs-input-search.vue new file mode 100644 index 0000000..a2b7a0d --- /dev/null +++ b/js/vue/lib/components/rs-input-search.vue @@ -0,0 +1,106 @@ + + + diff --git a/js/vue/lib/components/rs-progress-bar.showcase.vue b/js/vue/lib/components/rs-progress-bar.showcase.vue new file mode 100644 index 0000000..dad200a --- /dev/null +++ b/js/vue/lib/components/rs-progress-bar.showcase.vue @@ -0,0 +1,15 @@ + + + diff --git a/js/vue/lib/components/rs-progress-bar.vue b/js/vue/lib/components/rs-progress-bar.vue new file mode 100644 index 0000000..ff88457 --- /dev/null +++ b/js/vue/lib/components/rs-progress-bar.vue @@ -0,0 +1,46 @@ + + + diff --git a/js/vue/lib/icons/index.ts b/js/vue/lib/icons/index.ts index 4ab9419..d21e5cf 100644 --- a/js/vue/lib/icons/index.ts +++ b/js/vue/lib/icons/index.ts @@ -1,3 +1,10 @@ export { default as RsLocation } from "./rs-location.vue"; export { default as RsStarBold } from "./rs-star-bold.vue"; export { default as RsGithub } from "./rs-github.vue"; +export { default as RsSearch } from "./rs-search.vue"; +export { default as RsFilter } from "./rs-filter.vue"; +export { default as RsArrowDown } from "./rs-arrow-down.vue"; +export { default as RsArrowUp } from "./rs-arrow-up.vue"; +export { default as RsArrowLeft } from "./rs-arrow-left.vue"; +export { default as RsArrowRight } from "./rs-arrow-right.vue"; +export { default as RsFerris } from "./rs-ferris.vue"; diff --git a/js/vue/lib/icons/rs-arrow-down.vue b/js/vue/lib/icons/rs-arrow-down.vue new file mode 100644 index 0000000..e35bc82 --- /dev/null +++ b/js/vue/lib/icons/rs-arrow-down.vue @@ -0,0 +1,18 @@ + diff --git a/js/vue/lib/icons/rs-arrow-left.vue b/js/vue/lib/icons/rs-arrow-left.vue new file mode 100644 index 0000000..87c43b5 --- /dev/null +++ b/js/vue/lib/icons/rs-arrow-left.vue @@ -0,0 +1,18 @@ + diff --git a/js/vue/lib/icons/rs-arrow-right.vue b/js/vue/lib/icons/rs-arrow-right.vue new file mode 100644 index 0000000..13efb2d --- /dev/null +++ b/js/vue/lib/icons/rs-arrow-right.vue @@ -0,0 +1,18 @@ + diff --git a/js/vue/lib/icons/rs-arrow-up.vue b/js/vue/lib/icons/rs-arrow-up.vue new file mode 100644 index 0000000..14a4cf8 --- /dev/null +++ b/js/vue/lib/icons/rs-arrow-up.vue @@ -0,0 +1,15 @@ + diff --git a/js/vue/lib/icons/rs-ferris.vue b/js/vue/lib/icons/rs-ferris.vue new file mode 100644 index 0000000..6c28a47 --- /dev/null +++ b/js/vue/lib/icons/rs-ferris.vue @@ -0,0 +1,31 @@ + diff --git a/js/vue/lib/icons/rs-filter.vue b/js/vue/lib/icons/rs-filter.vue new file mode 100644 index 0000000..a150b4a --- /dev/null +++ b/js/vue/lib/icons/rs-filter.vue @@ -0,0 +1,15 @@ + diff --git a/js/vue/lib/icons/rs-search.vue b/js/vue/lib/icons/rs-search.vue new file mode 100644 index 0000000..52c764c --- /dev/null +++ b/js/vue/lib/icons/rs-search.vue @@ -0,0 +1,17 @@ + diff --git a/js/vue/lib/index.ts b/js/vue/lib/index.ts index 7939e7c..45efc61 100644 --- a/js/vue/lib/index.ts +++ b/js/vue/lib/index.ts @@ -4,5 +4,12 @@ export { default as RsChip } from "./components/rs-chip.vue"; export { default as RsTag } from "./components/rs-tag.vue"; export { default as RsAvatar } from "./components/rs-avatar.vue"; export { default as RsCollaborators } from "./components/rs-collaborators.vue"; +export { default as RsCard } from "./components/rs-card.vue"; +export { default as RsBadge } from "./components/rs-badge.vue"; +export { default as RsDropdown } from "./components/rs-dropdown.vue"; +export { default as RsProgressBar } from "./components/rs-progress-bar.vue"; +export { default as RsInputSearch } from "./components/rs-input-search.vue"; +export { default as RsCalendar } from "./components/rs-calendar.vue"; +export { default as RsCalendarDay } from "./components/rs-calendar-day.vue"; export * from "./icons"; diff --git a/js/vue/lib/showcases.ts b/js/vue/lib/showcases.ts index 5f55dd9..bbce3e8 100644 --- a/js/vue/lib/showcases.ts +++ b/js/vue/lib/showcases.ts @@ -4,3 +4,9 @@ export { default as ChipShowcase } from "./components/rs-chip.showcase.vue"; export { default as TagShowcase } from "./components/rs-tag.showcase.vue"; export { default as AvatarShowcase } from "./components/rs-avatar.showcase.vue"; export { default as CollaboratorsShowcase } from "./components/rs-collaborators.showcase.vue"; +export { default as CardShowcase } from "./components/rs-card.showcase.vue"; +export { default as BadgeShowcase } from "./components/rs-badge.showcase.vue"; +export { default as DropdownShowcase } from "./components/rs-dropdown.showcase.vue"; +export { default as ProgressBarShowcase } from "./components/rs-progress-bar.showcase.vue"; +export { default as InputSearchShowcase } from "./components/rs-input-search.showcase.vue"; +export { default as CalendarShowcase } from "./components/rs-calendar.showcase.vue"; diff --git a/js/vue/lib/utils/date.ts b/js/vue/lib/utils/date.ts new file mode 100644 index 0000000..3c7db2b --- /dev/null +++ b/js/vue/lib/utils/date.ts @@ -0,0 +1,57 @@ +type CalendarDay = { + date: Date; + currentMonth: boolean; +}; + +export const getCalendarDays = (baseDate = new Date()): CalendarDay[] => { + const days: CalendarDay[] = []; + + const year = baseDate.getFullYear(); + const month = baseDate.getMonth(); + + const firstOfMonth = new Date(year, month, 1); + const lastOfMonth = new Date(year, month + 1, 0); + + const totalDays = lastOfMonth.getDate(); + + const startWeekday = (firstOfMonth.getDay() + 6) % 7; + const endWeekday = (lastOfMonth.getDay() + 6) % 7; + + if (startWeekday > 0) { + const prevMonthLastDay = new Date(year, month, 0).getDate(); + for (let i = startWeekday - 1; i >= 0; i--) { + const day = prevMonthLastDay - i; + days.push({ + date: new Date(year, month - 1, day), + currentMonth: false, + }); + } + } + + for (let i = 1; i <= totalDays; i++) { + days.push({ + date: new Date(year, month, i), + currentMonth: true, + }); + } + + const remaining = 6 - endWeekday; + for (let i = 1; i <= remaining; i++) { + days.push({ + date: new Date(year, month + 1, i), + currentMonth: false, + }); + } + + return days; +}; + +export const addMonths = (date: Date, amount: number): Date => { + const year = date.getFullYear(); + const month = date.getMonth() + amount; + return new Date(year, month, 1); +}; + +export const subMonths = (date: Date, amount: number): Date => { + return addMonths(date, -amount); +}; From 303287c2928ea8873f0c1c92dc97a756fe4e3ac2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Apr 2026 23:30:58 +0000 Subject: [PATCH 4/5] feat: complete Vue parity + level up component APIs across frameworks Vue (new components): - RsLevel: N1/N2/N3/Op variants, polymorphic via 'as' - RsRadio: native with v-model support - RsContactForm: full form with validation, emits 'submit' on success - DropdownTree: Start / Subject / Topic / SubTopic / End subcomponents matching the React composite API (5 .vue files + barrel) API improvements (libraries used from external apps): Vue - v-model on RsInput, RsInputSearch, RsDropdown, RsTag (:selected), RsCalendar (v-model:value), RsRadio - All form-like components forward $attrs via inheritAttrs:false + v-bind=$attrs so name/id/aria-* etc. reach the underlying element - RsTag now supports default slot and 'li' as polymorphic target React - forwardRef on Input and InputSearch (refs reach the native ) - Input gains containerClassName for outer wrapper styling - InputSearch exports InputSearchOption / InputSearchProps Leptos - Input accepts placeholder, name, type, value props --- crates/leptos/src/input.rs | 10 +- .../input-search/input-search.component.tsx | 157 ++++++++-------- js/react/lib/components/input/input.tsx | 63 ++++--- js/react/lib/components/input/input.types.ts | 5 + .../dropdown-tree/dropdown-tree.showcase.vue | 40 +++++ js/vue/lib/components/dropdown-tree/index.ts | 5 + .../dropdown-tree/rs-dropdown-tree-end.vue | 29 +++ .../dropdown-tree/rs-dropdown-tree-start.vue | 44 +++++ .../rs-dropdown-tree-subject.vue | 46 +++++ .../rs-dropdown-tree-subtopic.vue | 39 ++++ .../dropdown-tree/rs-dropdown-tree-topic.vue | 30 ++++ .../lib/components/rs-calendar.showcase.vue | 3 +- js/vue/lib/components/rs-calendar.vue | 22 ++- .../components/rs-contact-form.showcase.vue | 10 ++ js/vue/lib/components/rs-contact-form.vue | 169 ++++++++++++++++++ .../lib/components/rs-dropdown.showcase.vue | 4 +- js/vue/lib/components/rs-dropdown.vue | 23 ++- js/vue/lib/components/rs-input-search.vue | 10 ++ js/vue/lib/components/rs-input.vue | 16 ++ js/vue/lib/components/rs-level.showcase.vue | 16 ++ js/vue/lib/components/rs-level.vue | 47 +++++ js/vue/lib/components/rs-radio.showcase.vue | 16 ++ js/vue/lib/components/rs-radio.vue | 43 +++++ js/vue/lib/components/rs-tag.vue | 19 +- js/vue/lib/index.ts | 4 + js/vue/lib/showcases.ts | 4 + 26 files changed, 752 insertions(+), 122 deletions(-) create mode 100644 js/vue/lib/components/dropdown-tree/dropdown-tree.showcase.vue create mode 100644 js/vue/lib/components/dropdown-tree/index.ts create mode 100644 js/vue/lib/components/dropdown-tree/rs-dropdown-tree-end.vue create mode 100644 js/vue/lib/components/dropdown-tree/rs-dropdown-tree-start.vue create mode 100644 js/vue/lib/components/dropdown-tree/rs-dropdown-tree-subject.vue create mode 100644 js/vue/lib/components/dropdown-tree/rs-dropdown-tree-subtopic.vue create mode 100644 js/vue/lib/components/dropdown-tree/rs-dropdown-tree-topic.vue create mode 100644 js/vue/lib/components/rs-contact-form.showcase.vue create mode 100644 js/vue/lib/components/rs-contact-form.vue create mode 100644 js/vue/lib/components/rs-level.showcase.vue create mode 100644 js/vue/lib/components/rs-level.vue create mode 100644 js/vue/lib/components/rs-radio.showcase.vue create mode 100644 js/vue/lib/components/rs-radio.vue diff --git a/crates/leptos/src/input.rs b/crates/leptos/src/input.rs index aa7054e..9c1408e 100644 --- a/crates/leptos/src/input.rs +++ b/crates/leptos/src/input.rs @@ -12,7 +12,11 @@ pub fn Input( #[prop(into)] disabled: ReadSignal, #[prop(into, optional)] class: String, #[prop(into, optional)] error_message: String, - #[prop(into)] icon: Option, + #[prop(into, optional)] placeholder: String, + #[prop(into, optional, default = "text".to_string())] r#type: String, + #[prop(into, optional)] name: String, + #[prop(into, optional)] value: String, + #[prop(into, optional)] icon: Option, ) -> impl IntoView { let input_class = crate::tw!( concat!(BASE_CLASS, "-input"), @@ -29,6 +33,10 @@ pub fn Input( {has_error.get().then_some( diff --git a/js/react/lib/components/input-search/input-search.component.tsx b/js/react/lib/components/input-search/input-search.component.tsx index 5423313..803fff9 100644 --- a/js/react/lib/components/input-search/input-search.component.tsx +++ b/js/react/lib/components/input-search/input-search.component.tsx @@ -4,94 +4,97 @@ import { Search } from "@/icons/search"; import { cn } from "@/utils/tw-merge"; import { Fragment } from "react/jsx-runtime"; import { Tag } from "../tag"; -import { InputHTMLAttributes } from "react"; +import { forwardRef, InputHTMLAttributes } from "react"; -type Option = { label: string; value: string }; -type InputSearchProps = { - filters?: Array