mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-25 09:14:39 +02:00
feat: add i18n localization with language switcher to all web apps
- Add svelte-i18n configuration with SSR support to all web apps - Create LanguageSelector component for each app with brand colors - Add German and English locale files - Integrate language switcher into login pages via headerControls snippet - Fix Tailwind v4 @source directives for shared package scanning - Update AppSlider styling to match login container design Apps updated: - Memoro (gold #f8d62b) - Märchenzauber (pink #FF6B9D) - ManaDeck (purple #8b5cf6) - ManaCore (indigo #6366f1) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bd869dfe09
commit
926ca231b5
147 changed files with 7090 additions and 2276 deletions
|
|
@ -137,6 +137,8 @@
|
|||
|
||||
<!-- Actions -->
|
||||
{#if actions}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div class="data-card__actions flex-shrink-0 flex items-center gap-1" onclick={(e) => e.stopPropagation()}>
|
||||
{@render actions()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
required?: boolean;
|
||||
autocomplete?: HTMLInputAttributes['autocomplete'];
|
||||
class?: string;
|
||||
id?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -26,7 +28,9 @@
|
|||
disabled = false,
|
||||
required = false,
|
||||
autocomplete,
|
||||
class: className = ''
|
||||
class: className = '',
|
||||
id = `input-${Math.random().toString(36).slice(2, 9)}`,
|
||||
name
|
||||
}: Props = $props();
|
||||
|
||||
function handleInput(e: Event) {
|
||||
|
|
@ -43,15 +47,17 @@
|
|||
|
||||
<div class="flex flex-col gap-1.5 {className}">
|
||||
{#if label}
|
||||
<label class="text-sm font-medium text-theme">
|
||||
<label for={id} class="text-sm font-medium text-theme">
|
||||
{label}
|
||||
{#if required}
|
||||
<span class="text-red-500">*</span>
|
||||
{/if}
|
||||
</label>
|
||||
{/if}
|
||||
|
||||
|
||||
<input
|
||||
{id}
|
||||
{name}
|
||||
{type}
|
||||
{value}
|
||||
{placeholder}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
<script lang="ts">
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
import type { SelectOption } from './Select.types';
|
||||
|
||||
interface Props {
|
||||
/** Current selected value */
|
||||
|
|
@ -24,6 +20,8 @@
|
|||
required?: boolean;
|
||||
/** Additional CSS classes */
|
||||
class?: string;
|
||||
/** Unique ID for accessibility */
|
||||
id?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -35,7 +33,8 @@
|
|||
error,
|
||||
disabled = false,
|
||||
required = false,
|
||||
class: className = ''
|
||||
class: className = '',
|
||||
id = `select-${Math.random().toString(36).slice(2, 9)}`
|
||||
}: Props = $props();
|
||||
|
||||
function handleChange(e: Event) {
|
||||
|
|
@ -47,7 +46,7 @@
|
|||
|
||||
<div class="select-wrapper {className}">
|
||||
{#if label}
|
||||
<label class="select-label">
|
||||
<label for={id} class="select-label">
|
||||
{label}
|
||||
{#if required}
|
||||
<span class="select-required">*</span>
|
||||
|
|
@ -57,6 +56,7 @@
|
|||
|
||||
<div class="select-container">
|
||||
<select
|
||||
{id}
|
||||
{value}
|
||||
{disabled}
|
||||
{required}
|
||||
|
|
|
|||
5
packages/shared-ui/src/molecules/Select.types.ts
Normal file
5
packages/shared-ui/src/molecules/Select.types.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@
|
|||
autoResize?: boolean;
|
||||
/** Additional CSS classes */
|
||||
class?: string;
|
||||
/** Unique ID for accessibility */
|
||||
id?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -41,7 +43,8 @@
|
|||
disabled = false,
|
||||
required = false,
|
||||
autoResize = false,
|
||||
class: className = ''
|
||||
class: className = '',
|
||||
id = `textarea-${Math.random().toString(36).slice(2, 9)}`
|
||||
}: Props = $props();
|
||||
|
||||
let textareaElement: HTMLTextAreaElement | null = $state(null);
|
||||
|
|
@ -68,7 +71,7 @@
|
|||
|
||||
<div class="textarea-wrapper {className}">
|
||||
{#if label}
|
||||
<label class="textarea-label">
|
||||
<label for={id} class="textarea-label">
|
||||
{label}
|
||||
{#if required}
|
||||
<span class="textarea-required">*</span>
|
||||
|
|
@ -77,6 +80,7 @@
|
|||
{/if}
|
||||
|
||||
<textarea
|
||||
{id}
|
||||
bind:this={textareaElement}
|
||||
{value}
|
||||
{placeholder}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
: 'bg-menu'} {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}"
|
||||
role="switch"
|
||||
aria-checked={isOn}
|
||||
aria-label="Toggle"
|
||||
{disabled}
|
||||
>
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
onclick={handleClick}
|
||||
onkeydown={handleKeyDown}
|
||||
role={clickable ? 'button' : undefined}
|
||||
tabindex={clickable ? 0 : -1}
|
||||
tabindex={clickable ? 0 : undefined}
|
||||
>
|
||||
<!-- Color indicator dot -->
|
||||
<div class="h-2 w-2 rounded-full" style="background-color: {tagColor}"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue