tamga-ui 0.2.0 → 0.2.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.
- package/CHANGELOG.md +38 -0
- package/dist/components/appearance.d.ts +78 -0
- package/dist/components/appearance.d.ts.map +1 -0
- package/dist/components/appearance.js +73 -0
- package/dist/components/rail-link.d.ts +15 -2
- package/dist/components/rail-link.d.ts.map +1 -1
- package/dist/components/rail-link.js +11 -9
- package/dist/components/square-picker.d.ts +41 -0
- package/dist/components/square-picker.d.ts.map +1 -0
- package/dist/components/square-picker.js +90 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/kit.css +212 -4
- package/dist/lib/image.d.ts +46 -0
- package/dist/lib/image.d.ts.map +1 -0
- package/dist/lib/image.js +95 -0
- package/dist/patterns/app-shell.d.ts.map +1 -1
- package/dist/patterns/app-shell.js +2 -12
- package/dist/patterns/appearance-template.d.ts +105 -0
- package/dist/patterns/appearance-template.d.ts.map +1 -0
- package/dist/patterns/appearance-template.js +23 -0
- package/dist/patterns/index.d.ts +1 -0
- package/dist/patterns/index.d.ts.map +1 -1
- package/dist/patterns/index.js +1 -0
- package/dist/patterns/settings-template.d.ts +12 -1
- package/dist/patterns/settings-template.d.ts.map +1 -1
- package/dist/patterns/settings-template.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,44 @@ Versions follow [semver](https://semver.org/). Through `0.x`, breaking changes m
|
|
|
5
5
|
|
|
6
6
|
> 🇹🇷 Türkçe için [tamga.org.tr/tr](https://tamga.org.tr/tr).
|
|
7
7
|
|
|
8
|
+
## 0.2.1
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`AppearanceTemplate`** (`tamga-ui/patterns`) — the whole appearance screen: logo, mark, brand
|
|
13
|
+
colour, theme and sidebar width. This screen had been built twice, by hand, in two products, and
|
|
14
|
+
the second one came out bare: a hex field instead of swatches, three words instead of three
|
|
15
|
+
pictures, no logo area at all. Handing over the parts and saying "arrange them yourself" is what
|
|
16
|
+
let them drift; that is the job the pattern layer exists for (ADR-0004).
|
|
17
|
+
|
|
18
|
+
It stores NOTHING and applies NOTHING. `value` in, `onChange` and `onSave` out, every word from
|
|
19
|
+
`labels`. Where the preference lives (session, account, browser) is a product decision, and
|
|
20
|
+
turning the chosen colour into tokens stays the product's call too, because the root element is
|
|
21
|
+
its own.
|
|
22
|
+
|
|
23
|
+
- **`ColorSwatches`, `ThemeCards`, `ImageField`** — the parts, for a screen that needs a different
|
|
24
|
+
arrangement. `ThemeCards` draws a miniature of each option rather than naming it; a theme is not
|
|
25
|
+
chosen by a word, it is chosen by seeing the result.
|
|
26
|
+
- **`SquarePicker`** — dragging a square over an uploaded logo to cut a mark out of it. A mark
|
|
27
|
+
cannot be derived automatically, and this is the honest alternative: the human says where it is.
|
|
28
|
+
- **`prepareImage` and `cropSquare`** (`tamga-ui`) — resize and square-crop an uploaded file, with
|
|
29
|
+
no library: a `<canvas>`. The ratio is kept and nothing is cropped silently; SVG passes through
|
|
30
|
+
untouched, because baking a vector into a 512px PNG stops it being sharp.
|
|
31
|
+
- **`SettingsPanel.accent`** — a coloured left edge on a section block.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **Completed steps were darker than everything else on the screen.** `StepStrip` filled them with
|
|
36
|
+
`--color-accent-line`, which is the accent's TEXT variant: it is derived by SEARCHING for 4.5
|
|
37
|
+
contrast against the page, so it is always darker than the face. Every button and badge on the
|
|
38
|
+
same screen used `--color-accent`, and the gap widened with each new brand colour. The ink on top
|
|
39
|
+
was measured against `accent`, never against `accent-line`, so that pairing was never checked at
|
|
40
|
+
all. Filled accent objects now follow the button's contract everywhere: `accent` for the fill,
|
|
41
|
+
`accent-shadow` for the edge, `accent-ink` for the ink.
|
|
42
|
+
|
|
43
|
+
Nothing errored here: the colours were valid and the contrast gates passed. `check-css` now
|
|
44
|
+
refuses `--color-accent-line` as a fill, so this class of mistake cannot pass silently again.
|
|
45
|
+
|
|
8
46
|
## 0.2.0
|
|
9
47
|
|
|
10
48
|
Most of this release came out of gaps found by two people using the kit **from the outside**:
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type SwatchOption = {
|
|
3
|
+
/** The hex the swatch applies. TR: Kutunun uyguladığı hex. */
|
|
4
|
+
hex: string;
|
|
5
|
+
/** Its name, translated, used as the accessible label. TR: Çevrilmiş adı; erişilebilir ad olarak kullanılıyor. */
|
|
6
|
+
label: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Brand colour chosen from a few squares, or written by hand.
|
|
10
|
+
* TR: Birkaç kareden seçilen ya da elle yazılan marka rengi.
|
|
11
|
+
*/
|
|
12
|
+
export declare function ColorSwatches({ options, value, onChange, customLabel, custom, className, }: {
|
|
13
|
+
options: readonly SwatchOption[];
|
|
14
|
+
value: string;
|
|
15
|
+
onChange: (hex: string) => void;
|
|
16
|
+
/** Accessible name of the "my own colour" control. TR: "Kendi rengim" kontrolünün erişilebilir adı. */
|
|
17
|
+
customLabel: string;
|
|
18
|
+
/** Whether a free colour is offered at all. TR: Serbest rengin hiç sunulup sunulmadığı. */
|
|
19
|
+
custom?: boolean;
|
|
20
|
+
className?: string;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
22
|
+
export type ThemeChoice = "light" | "dark" | "system";
|
|
23
|
+
/**
|
|
24
|
+
* Theme picked from three little pictures of the panel.
|
|
25
|
+
* TR: Panelin üç küçük resminden seçilen tema.
|
|
26
|
+
*
|
|
27
|
+
* "Light · Dark · System" were three words, and a theme is not chosen with
|
|
28
|
+
* words: the person wants to see the result. "System" shows both halves in one
|
|
29
|
+
* frame, which is exactly what it means.
|
|
30
|
+
* TR: "Açık · Koyu · Sistem" üç kelimeydi, ve bir tema kelimeyle seçilmiyor:
|
|
31
|
+
* kullanıcı sonucu görmek istiyor. "Sistem" iki yarımı tek karede gösteriyor,
|
|
32
|
+
* anlamı tam olarak bu.
|
|
33
|
+
*/
|
|
34
|
+
export declare function ThemeCards({ value, onChange, labels, className, }: {
|
|
35
|
+
value: ThemeChoice;
|
|
36
|
+
onChange: (next: ThemeChoice) => void;
|
|
37
|
+
/** The three names and the group's accessible name. TR: Üç ad ve kümenin erişilebilir adı. */
|
|
38
|
+
labels: {
|
|
39
|
+
light: string;
|
|
40
|
+
dark: string;
|
|
41
|
+
system: string;
|
|
42
|
+
group: string;
|
|
43
|
+
};
|
|
44
|
+
className?: string;
|
|
45
|
+
}): import("react").JSX.Element;
|
|
46
|
+
/**
|
|
47
|
+
* One image with a preview, a replace and a remove.
|
|
48
|
+
* TR: Önizlemesi, değiştirmesi ve kaldırması olan tek bir görsel.
|
|
49
|
+
*
|
|
50
|
+
* NOT `FileUpload`: that one is a GALLERY (multi select, reorder arrows, a
|
|
51
|
+
* "cover" badge). Right for product photos, wrong for a single logo, where it
|
|
52
|
+
* offers reorder arrows with nothing to reorder.
|
|
53
|
+
* TR: `FileUpload` DEĞİL: o bir GALERİ (çoklu seçim, sıralama okları, "kapak"
|
|
54
|
+
* rozeti). Ürün fotoğrafları için doğru, tek bir logo için yanlış.
|
|
55
|
+
*/
|
|
56
|
+
export declare function ImageField({ value, onChange, maxEdge, labels, preview, extra, className, }: {
|
|
57
|
+
value: string | null;
|
|
58
|
+
onChange: (next: string | null) => void;
|
|
59
|
+
/** Longest edge of the stored image, in pixels. TR: Saklanan görselin en uzun kenarı, piksel. */
|
|
60
|
+
maxEdge: number;
|
|
61
|
+
labels: {
|
|
62
|
+
/** "Logo", "Mark": used inside the buttons. TR: "Logo", "Amblem": düğmelerin içinde geçiyor. */
|
|
63
|
+
name: string;
|
|
64
|
+
upload: string;
|
|
65
|
+
replace: string;
|
|
66
|
+
remove: string;
|
|
67
|
+
empty: string;
|
|
68
|
+
errorType: string;
|
|
69
|
+
errorSize: string;
|
|
70
|
+
errorUnreadable: string;
|
|
71
|
+
};
|
|
72
|
+
/** The caller draws the preview: a logo is wide, a mark is square. TR: Önizlemeyi çağıran çiziyor: logo geniş, amblem kare. */
|
|
73
|
+
preview: (src: string) => ReactNode;
|
|
74
|
+
/** An extra control beside the buttons. TR: Düğmelerin yanına giren ek kontrol. */
|
|
75
|
+
extra?: ReactNode;
|
|
76
|
+
className?: string;
|
|
77
|
+
}): import("react").JSX.Element;
|
|
78
|
+
//# sourceMappingURL=appearance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../../src/components/appearance.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAchE,MAAM,MAAM,YAAY,GAAG;IACzB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,kHAAkH;IAClH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAAW,EACX,MAAa,EACb,SAAS,GACV,EAAE;IACD,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,uGAAuG;IACvG,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAqCA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAoBtD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,8FAA8F;IAC9F,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BA6BA;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,KAAK,EACL,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACxC,iGAAiG;IACjG,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,gGAAgG;QAChG,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,+HAA+H;IAC/H,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;IACpC,mFAAmF;IACnF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAoDA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useRef, useState } from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { IMAGE_ACCEPT, prepareImage } from "../lib/image.js";
|
|
6
|
+
import { Alert } from "./primitives.js";
|
|
7
|
+
import { Button } from "./button.js";
|
|
8
|
+
/**
|
|
9
|
+
* Brand colour chosen from a few squares, or written by hand.
|
|
10
|
+
* TR: Birkaç kareden seçilen ya da elle yazılan marka rengi.
|
|
11
|
+
*/
|
|
12
|
+
export function ColorSwatches({ options, value, onChange, customLabel, custom = true, className, }) {
|
|
13
|
+
const secili = (hex) => hex.toLowerCase() === value.toLowerCase();
|
|
14
|
+
const hazirMi = options.some((o) => secili(o.hex));
|
|
15
|
+
return (_jsxs("span", { className: cn("flex flex-wrap items-center gap-3", className), children: [options.map((o) => (_jsx("button", { type: "button", className: "tamga-swatch", "data-selected": secili(o.hex), "aria-label": o.label, title: o.label, onClick: () => onChange(o.hex), children: _jsx("span", { "aria-hidden": true, style: { background: o.hex } }) }, o.hex))), custom ? (_jsxs("label", { className: "tamga-swatch", "data-selected": !hazirMi, title: customLabel, children: [_jsx("span", { className: "sr-only", children: customLabel }), _jsx("input", { type: "color", value: hazirMi ? options[0].hex : value, onChange: (e) => onChange(e.target.value), className: "tamga-color-input", "aria-label": customLabel })] })) : null] }));
|
|
16
|
+
}
|
|
17
|
+
function Yarim({ theme }) {
|
|
18
|
+
return (_jsxs("span", { className: "tamga-theme-half", "data-theme": theme, "aria-hidden": true, children: [_jsxs("span", { className: "tamga-theme-rail", children: [_jsx("span", { className: "tamga-theme-dot", "data-accent": "true" }), _jsx("span", { className: "tamga-theme-dot" }), _jsx("span", { className: "tamga-theme-dot" })] }), _jsxs("span", { className: "tamga-theme-body", children: [_jsx("span", { className: "tamga-theme-line", style: { width: "70%" } }), _jsx("span", { className: "tamga-theme-line", style: { width: "45%" } }), _jsx("span", { className: "tamga-theme-line", "data-accent": "true", style: { width: "30%" } }), _jsx("span", { className: "tamga-theme-line", style: { width: "60%" } })] })] }));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Theme picked from three little pictures of the panel.
|
|
22
|
+
* TR: Panelin üç küçük resminden seçilen tema.
|
|
23
|
+
*
|
|
24
|
+
* "Light · Dark · System" were three words, and a theme is not chosen with
|
|
25
|
+
* words: the person wants to see the result. "System" shows both halves in one
|
|
26
|
+
* frame, which is exactly what it means.
|
|
27
|
+
* TR: "Açık · Koyu · Sistem" üç kelimeydi, ve bir tema kelimeyle seçilmiyor:
|
|
28
|
+
* kullanıcı sonucu görmek istiyor. "Sistem" iki yarımı tek karede gösteriyor,
|
|
29
|
+
* anlamı tam olarak bu.
|
|
30
|
+
*/
|
|
31
|
+
export function ThemeCards({ value, onChange, labels, className, }) {
|
|
32
|
+
const secenekler = [
|
|
33
|
+
{ deger: "light", ad: labels.light },
|
|
34
|
+
{ deger: "dark", ad: labels.dark },
|
|
35
|
+
{ deger: "system", ad: labels.system },
|
|
36
|
+
];
|
|
37
|
+
return (_jsx("div", { role: "radiogroup", "aria-label": labels.group, className: cn("flex flex-wrap gap-3", className), children: secenekler.map((s) => (_jsxs("button", { type: "button", role: "radio", "aria-checked": value === s.deger, tabIndex: value === s.deger ? 0 : -1, "data-selected": value === s.deger, className: "tamga-theme-card", onClick: () => onChange(s.deger), children: [_jsxs("span", { className: "tamga-theme-box", children: [s.deger === "dark" ? _jsx(Yarim, { theme: "dark" }) : _jsx(Yarim, { theme: "light" }), s.deger === "system" ? _jsx(Yarim, { theme: "dark" }) : null] }), _jsx("span", { className: "tamga-theme-name", children: s.ad })] }, s.deger))) }));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One image with a preview, a replace and a remove.
|
|
41
|
+
* TR: Önizlemesi, değiştirmesi ve kaldırması olan tek bir görsel.
|
|
42
|
+
*
|
|
43
|
+
* NOT `FileUpload`: that one is a GALLERY (multi select, reorder arrows, a
|
|
44
|
+
* "cover" badge). Right for product photos, wrong for a single logo, where it
|
|
45
|
+
* offers reorder arrows with nothing to reorder.
|
|
46
|
+
* TR: `FileUpload` DEĞİL: o bir GALERİ (çoklu seçim, sıralama okları, "kapak"
|
|
47
|
+
* rozeti). Ürün fotoğrafları için doğru, tek bir logo için yanlış.
|
|
48
|
+
*/
|
|
49
|
+
export function ImageField({ value, onChange, maxEdge, labels, preview, extra, className, }) {
|
|
50
|
+
const id = useId();
|
|
51
|
+
const girdi = useRef(null);
|
|
52
|
+
const [hata, setHata] = useState(null);
|
|
53
|
+
async function al(file) {
|
|
54
|
+
if (!file)
|
|
55
|
+
return;
|
|
56
|
+
setHata(null);
|
|
57
|
+
try {
|
|
58
|
+
onChange(await prepareImage(file, maxEdge));
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
const kod = e instanceof Error ? e.message : "unreadable";
|
|
62
|
+
setHata(kod === "type" ? labels.errorType : kod === "size" ? labels.errorSize : labels.errorUnreadable);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
/* GİRDİ SIFIRLANIYOR: aynı dosya ikinci kez seçilince `change` hiç
|
|
66
|
+
çıkmıyor, çünkü değer değişmiyor. Kullanıcı için bu "ikinci denemede
|
|
67
|
+
çalışmıyor" demek. */
|
|
68
|
+
if (girdi.current)
|
|
69
|
+
girdi.current.value = "";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-3", className), children: [_jsx("input", { ref: girdi, id: id, type: "file", accept: IMAGE_ACCEPT, className: "sr-only", onChange: (e) => al(e.target.files?.[0]) }), _jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [_jsx("span", { className: "tamga-image-field", "data-empty": value ? undefined : "true", children: value ? preview(value) : labels.empty }), _jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { type: "button", onClick: () => girdi.current?.click(), children: value ? labels.replace : labels.upload }), extra, value ? (_jsx(Button, { type: "button", variant: "ghost", onClick: () => onChange(null), children: labels.remove })) : null] })] }), hata ? _jsx(Alert, { state: "danger", title: hata }) : null] }));
|
|
73
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ComponentType, ReactNode } from "react";
|
|
2
2
|
/**
|
|
3
3
|
* Kendi dosyasında, çünkü YENİ BİR KONTROL.
|
|
4
4
|
*
|
|
5
5
|
* Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
|
|
6
6
|
*/
|
|
7
7
|
/** İkon raylı gezinme öğesi. */
|
|
8
|
-
export declare function RailLink({ label, active, href, onClick, className, children, showLabel, }: {
|
|
8
|
+
export declare function RailLink({ label, active, href, onClick, className, children, showLabel, linkComponent: Link, }: {
|
|
9
9
|
label: string;
|
|
10
10
|
active?: boolean;
|
|
11
11
|
href?: string;
|
|
@@ -20,5 +20,18 @@ export declare function RailLink({ label, active, href, onClick, className, chil
|
|
|
20
20
|
* çubuğunda `true`.
|
|
21
21
|
*/
|
|
22
22
|
showLabel?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The router's link, so the rail does not force a full page load. Without it this component
|
|
25
|
+
* can only draw a plain `<a>`, and a shell that needs client routing ends up writing its own
|
|
26
|
+
* rail link instead of using this one. TR: Yönlendiricinin bağlantısı, ray tam sayfa
|
|
27
|
+
* yüklemeye zorlamasın diye. Olmadığında bu bileşen yalnız düz bir `<a>` çizebiliyor, ve
|
|
28
|
+
* istemci yönlendirmesi isteyen bir kabuk bunu kullanmak yerine kendi ray bağlantısını
|
|
29
|
+
* yazıyor.
|
|
30
|
+
*/
|
|
31
|
+
linkComponent?: ComponentType<{
|
|
32
|
+
href: string;
|
|
33
|
+
children?: ReactNode;
|
|
34
|
+
[k: string]: unknown;
|
|
35
|
+
}>;
|
|
23
36
|
}): import("react").JSX.Element;
|
|
24
37
|
//# sourceMappingURL=rail-link.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rail-link.d.ts","sourceRoot":"","sources":["../../src/components/rail-link.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"rail-link.d.ts","sourceRoot":"","sources":["../../src/components/rail-link.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAItD;;;;GAIG;AAEH,gCAAgC;AAChC,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,MAAc,EACd,IAAI,EACJ,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAiB,EACjB,aAAa,EAAE,IAAI,GACpB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;CAC7F,+BA2CA"}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { Tooltip } from "./overlay.js";
|
|
4
5
|
/**
|
|
5
6
|
* Kendi dosyasında, çünkü YENİ BİR KONTROL.
|
|
6
7
|
*
|
|
7
8
|
* Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
|
|
8
9
|
*/
|
|
9
10
|
/** İkon raylı gezinme öğesi. */
|
|
10
|
-
export function RailLink({ label, active = false, href, onClick, className, children, showLabel = false, }) {
|
|
11
|
+
export function RailLink({ label, active = false, href, onClick, className, children, showLabel = false, linkComponent: Link, }) {
|
|
11
12
|
const shared = {
|
|
12
13
|
className: cn("tamga-rail-link", showLabel && "tamga-rail-link-wide", className),
|
|
13
14
|
"data-active": active,
|
|
14
15
|
"aria-label": label,
|
|
15
|
-
/* Etiket görünürken `title` yoktur: görünen bir metnin üstünde beliren
|
|
16
|
-
ipucu aynı şeyi ikinci kez söyler ve imleci geciktirir. */
|
|
17
|
-
title: showLabel ? undefined : label,
|
|
18
16
|
"aria-current": active ? "page" : undefined,
|
|
19
17
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const govde = (_jsxs(_Fragment, { children: [children, showLabel && _jsx("span", { className: "min-w-0 truncate", children: label })] }));
|
|
19
|
+
const kontrol = href ? (Link ? (_jsx(Link, { href: href, ...shared, children: govde })) : (_jsx("a", { href: href, ...shared, children: govde }))) : (_jsx("button", { type: "button", onClick: onClick, ...shared, children: govde }));
|
|
20
|
+
/* İPUCU BİLEŞENİN KENDİ İŞİ, ÇAĞIRANIN DEĞİL.
|
|
21
|
+
Burada `title={label}` vardı: tarayıcının gecikmeli gri balonu, kitin her
|
|
22
|
+
yerdeki ipucundan başka türlü görünüyor. Çağıranlar bu yüzden bir de elle
|
|
23
|
+
`Tooltip` sarıyordu ve ikisi üst üste biniyordu. Etiket görünürken ipucu
|
|
24
|
+
yok: görünen bir metnin üstünde beliren ipucu aynı şeyi ikinci kez söyler. */
|
|
25
|
+
return showLabel ? (kontrol) : (_jsx(Tooltip, { label: label, placement: "right", children: kontrol }));
|
|
24
26
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LOGODAN AMBLEM SEÇME — otomatik kesme DEĞİL.
|
|
3
|
+
*
|
|
4
|
+
* Sorulan soru şuydu: yüklenen logoyu ayırıp amblemi kendiliğinden çıkaramaz
|
|
5
|
+
* mıyız? Çıkaramayız, ve denememesi gerekiyor:
|
|
6
|
+
*
|
|
7
|
+
* · Dosyada "amblem" diye işaretli bir şey yok. SVG'de bazen bir grup id'si
|
|
8
|
+
* olur ama bu tasarımcının keyfine bağlı; PNG'de hiç yok.
|
|
9
|
+
* · Konum sabit değil: amblem solda, üstte, sağda olabilir, yazının içine
|
|
10
|
+
* gömülü olabilir, ya da hiç olmayabilir (yalnız kelime-logo).
|
|
11
|
+
* · Hata SESSİZ ve KALICI olur. Yanlış kesim patlamaz; yarım bir harf
|
|
12
|
+
* panelin her sayfasının sol üstünde durur ve kimse bunun otomatik
|
|
13
|
+
* kesildiğini bilmez.
|
|
14
|
+
*
|
|
15
|
+
* Bu yüzden kesimi İNSAN yapıyor: kare bir çerçeve, sürüklenip boyutlanıyor.
|
|
16
|
+
* Sonuç tahmin değil karar.
|
|
17
|
+
*
|
|
18
|
+
* ÇERÇEVE ORANLA TUTULUYOR (0–1), piksel ile değil: önizleme ekrandan ekrana
|
|
19
|
+
* farklı ölçekte çiziliyor, oran her ölçekte aynı yeri gösteriyor. Ekrana
|
|
20
|
+
* çizilirken piksele dönüyor ve dönüşüm TEK YERDE (`kenarPx`): kesim
|
|
21
|
+
* matematiği ile çerçevenin CSS'i ayrı ayrı hesaplansaydı, kullanıcının
|
|
22
|
+
* gördüğü kare ile kesilen kare farklı olurdu — ve fark sessiz olurdu.
|
|
23
|
+
*/
|
|
24
|
+
export declare function SquarePicker({ open, source, onClose, onPick, maxEdge, labels, }: {
|
|
25
|
+
open: boolean;
|
|
26
|
+
/** The image the square is cut out of. TR: Üzerinden kesilecek görsel. */
|
|
27
|
+
source: string;
|
|
28
|
+
onClose: () => void;
|
|
29
|
+
onPick: (square: string) => void;
|
|
30
|
+
/** Edge of the produced square, in pixels. TR: Üretilen karenin kenarı, piksel. */
|
|
31
|
+
maxEdge?: number;
|
|
32
|
+
labels: {
|
|
33
|
+
title: string;
|
|
34
|
+
hint: string;
|
|
35
|
+
size: string;
|
|
36
|
+
cancel: string;
|
|
37
|
+
confirm: string;
|
|
38
|
+
close: string;
|
|
39
|
+
};
|
|
40
|
+
}): import("react").JSX.Element;
|
|
41
|
+
//# sourceMappingURL=square-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"square-picker.d.ts","sourceRoot":"","sources":["../../src/components/square-picker.tsx"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,MAAM,EACN,OAAO,EACP,MAAM,EACN,OAAa,EACb,MAAM,GACP,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH,+BAmHA"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { Button } from "./button.js";
|
|
5
|
+
import { Dialog } from "./overlay.js";
|
|
6
|
+
import { Slider } from "./slider.js";
|
|
7
|
+
import { Label } from "./surface.js";
|
|
8
|
+
import { cropSquare } from "../lib/image.js";
|
|
9
|
+
/**
|
|
10
|
+
* LOGODAN AMBLEM SEÇME — otomatik kesme DEĞİL.
|
|
11
|
+
*
|
|
12
|
+
* Sorulan soru şuydu: yüklenen logoyu ayırıp amblemi kendiliğinden çıkaramaz
|
|
13
|
+
* mıyız? Çıkaramayız, ve denememesi gerekiyor:
|
|
14
|
+
*
|
|
15
|
+
* · Dosyada "amblem" diye işaretli bir şey yok. SVG'de bazen bir grup id'si
|
|
16
|
+
* olur ama bu tasarımcının keyfine bağlı; PNG'de hiç yok.
|
|
17
|
+
* · Konum sabit değil: amblem solda, üstte, sağda olabilir, yazının içine
|
|
18
|
+
* gömülü olabilir, ya da hiç olmayabilir (yalnız kelime-logo).
|
|
19
|
+
* · Hata SESSİZ ve KALICI olur. Yanlış kesim patlamaz; yarım bir harf
|
|
20
|
+
* panelin her sayfasının sol üstünde durur ve kimse bunun otomatik
|
|
21
|
+
* kesildiğini bilmez.
|
|
22
|
+
*
|
|
23
|
+
* Bu yüzden kesimi İNSAN yapıyor: kare bir çerçeve, sürüklenip boyutlanıyor.
|
|
24
|
+
* Sonuç tahmin değil karar.
|
|
25
|
+
*
|
|
26
|
+
* ÇERÇEVE ORANLA TUTULUYOR (0–1), piksel ile değil: önizleme ekrandan ekrana
|
|
27
|
+
* farklı ölçekte çiziliyor, oran her ölçekte aynı yeri gösteriyor. Ekrana
|
|
28
|
+
* çizilirken piksele dönüyor ve dönüşüm TEK YERDE (`kenarPx`): kesim
|
|
29
|
+
* matematiği ile çerçevenin CSS'i ayrı ayrı hesaplansaydı, kullanıcının
|
|
30
|
+
* gördüğü kare ile kesilen kare farklı olurdu — ve fark sessiz olurdu.
|
|
31
|
+
*/
|
|
32
|
+
export function SquarePicker({ open, source, onClose, onPick, maxEdge = 256, labels, }) {
|
|
33
|
+
const sahne = useRef(null);
|
|
34
|
+
const [olcu, setOlcu] = useState({ en: 0, boy: 0 });
|
|
35
|
+
const [x, setX] = useState(0);
|
|
36
|
+
const [y, setY] = useState(0);
|
|
37
|
+
const [oran, setOran] = useState(0.6);
|
|
38
|
+
const [suruyor, setSuruyor] = useState(false);
|
|
39
|
+
/* Her açılışta baştan: bir önceki logonun çerçevesini yeni logoya taşımak,
|
|
40
|
+
kullanıcıya alakasız bir yeri seçtirmenin en kolay yolu. */
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!open)
|
|
43
|
+
return;
|
|
44
|
+
setX(0);
|
|
45
|
+
setY(0);
|
|
46
|
+
setOran(0.6);
|
|
47
|
+
}, [open, source]);
|
|
48
|
+
/* Sahnenin gerçek boyu ÖLÇÜLÜYOR, tahmin edilmiyor: görsel yüklendikçe ve
|
|
49
|
+
pencere daraldıkça değişiyor, ve çerçevenin pikseli ona bağlı. */
|
|
50
|
+
useLayoutEffect(() => {
|
|
51
|
+
const el = sahne.current;
|
|
52
|
+
if (!el)
|
|
53
|
+
return;
|
|
54
|
+
const olc = () => setOlcu({ en: el.clientWidth, boy: el.clientHeight });
|
|
55
|
+
olc();
|
|
56
|
+
const gozcu = new ResizeObserver(olc);
|
|
57
|
+
gozcu.observe(el);
|
|
58
|
+
return () => gozcu.disconnect();
|
|
59
|
+
}, [open, source]);
|
|
60
|
+
/* KARE, SAHNENİN KISA KENARINA GÖRE. Bir logo 350×65 ise kare en fazla o 65
|
|
61
|
+
pikselin kadarı olabilir; uzun kenara göre ölçseydik çerçeve görselin
|
|
62
|
+
dışına taşardı. Kesim fonksiyonu da aynı kuralı kullanıyor. */
|
|
63
|
+
const kenarPx = Math.min(olcu.en, olcu.boy) * oran;
|
|
64
|
+
const tasi = useCallback((olay) => {
|
|
65
|
+
const el = sahne.current;
|
|
66
|
+
if (!suruyor || !el)
|
|
67
|
+
return;
|
|
68
|
+
const k = el.getBoundingClientRect();
|
|
69
|
+
const nx = (olay.clientX - k.left - kenarPx / 2) / k.width;
|
|
70
|
+
const ny = (olay.clientY - k.top - kenarPx / 2) / k.height;
|
|
71
|
+
setX(Math.max(0, Math.min(1 - kenarPx / k.width, nx)));
|
|
72
|
+
setY(Math.max(0, Math.min(1 - kenarPx / k.height, ny)));
|
|
73
|
+
}, [suruyor, kenarPx]);
|
|
74
|
+
async function onayla() {
|
|
75
|
+
onPick(await cropSquare(source, { x, y, size: oran }, maxEdge));
|
|
76
|
+
onClose();
|
|
77
|
+
}
|
|
78
|
+
return (_jsx(Dialog, { open: open, onClose: onClose, title: labels.title, closeLabel: labels.close, footer: _jsxs(_Fragment, { children: [_jsx(Button, { type: "button", onClick: onClose, children: labels.cancel }), _jsx(Button, { variant: "primary", type: "button", onClick: onayla, children: labels.confirm })] }), children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Label, { children: labels.hint }), _jsx("div", { className: "rounded-[var(--radius-card)] border border-[var(--color-edge)] bg-[var(--color-shell)] p-4", children: _jsxs("div", { ref: sahne, onPointerDown: (e) => {
|
|
79
|
+
setSuruyor(true);
|
|
80
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
81
|
+
}, onPointerMove: tasi, onPointerUp: () => setSuruyor(false), className: "relative cursor-crosshair touch-none select-none overflow-hidden", children: [_jsx("img", { src: source, alt: "", className: "pointer-events-none block w-full", draggable: false }), _jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute border-2 border-[var(--color-accent-line)]", style: {
|
|
82
|
+
left: x * olcu.en,
|
|
83
|
+
top: y * olcu.boy,
|
|
84
|
+
width: kenarPx,
|
|
85
|
+
height: kenarPx,
|
|
86
|
+
/* Dışarısı karartılıyor: seçili kare kendi kenarlığından çok
|
|
87
|
+
çevresinin koyulmasıyla okunuyor. */
|
|
88
|
+
boxShadow: "0 0 0 9999px rgb(0 0 0 / 0.35)",
|
|
89
|
+
} })] }) }), _jsx(Slider, { label: labels.size, value: Math.round(oran * 100), onChange: (v) => setOran(v / 100), min: 15, max: 100, suffix: "%" })] }) }));
|
|
90
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,9 @@ export { LiveScope, useLiveClaim } from "./components/live-scope.js";
|
|
|
41
41
|
export { Skeleton, SkeletonText, SkeletonRows, SkeletonKpi, SkeletonCard, SkeletonTable, SkeletonOptions, SkeletonPanel, SkeletonPageBand, } from "./components/skeleton.js";
|
|
42
42
|
export { Surface, ScrollX, Rise, Swap, ListRow, CellActions, PageBand, Link, } from "./components/layout.js";
|
|
43
43
|
export { ThemeToggle, LocaleSwitcher, LogoTile, AccountButton } from "./components/chrome.js";
|
|
44
|
+
export { ColorSwatches, ThemeCards, ImageField, type SwatchOption, type ThemeChoice, } from "./components/appearance.js";
|
|
45
|
+
export { SquarePicker } from "./components/square-picker.js";
|
|
46
|
+
export { prepareImage, cropSquare, IMAGE_ACCEPT, type ImageError } from "./lib/image.js";
|
|
44
47
|
export { RailLink } from "./components/rail-link.js";
|
|
45
48
|
export { Slider } from "./components/slider.js";
|
|
46
49
|
export { Accordion, Collapsible } from "./components/disclosure.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,EACL,MAAM,EACN,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,GACP,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGjE,OAAO,EACL,MAAM,EACN,cAAc,EACd,KAAK,WAAW,EAChB,UAAU,EACV,kBAAkB,EAClB,UAAU,GACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG5G,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAGxE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAGjF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChH,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAGjG,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAG5F,OAAO,EACL,SAAS,EACT,MAAM,EACN,KAAK,EACL,UAAU,EACV,KAAK,EACL,MAAM,EACN,KAAK,GACN,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,YAAY,EACZ,KAAK,QAAQ,EACb,OAAO,EACP,OAAO,EACP,KAAK,EACL,KAAK,SAAS,EACd,aAAa,EACb,aAAa,EACb,MAAM,GACP,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,UAAU,EACV,SAAS,EACT,UAAU,EACV,SAAS,EACT,UAAU,GACX,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,eAAe,EACf,aAAa,EACb,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EACL,OAAO,EACP,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,WAAW,EACX,QAAQ,EACR,IAAI,GACL,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzF,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAG9F,OAAO,EACL,aAAa,EACb,WAAW,EACX,SAAS,EACT,WAAW,EACX,aAAa,GACd,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,KAAK,GAAG,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -54,6 +54,9 @@ export { Skeleton, SkeletonText, SkeletonRows, SkeletonKpi, SkeletonCard, Skelet
|
|
|
54
54
|
* Gerekçesi `components/layout.tsx`'in başında. */
|
|
55
55
|
export { Surface, ScrollX, Rise, Swap, ListRow, CellActions, PageBand, Link, } from "./components/layout.js";
|
|
56
56
|
export { ThemeToggle, LocaleSwitcher, LogoTile, AccountButton } from "./components/chrome.js";
|
|
57
|
+
export { ColorSwatches, ThemeCards, ImageField, } from "./components/appearance.js";
|
|
58
|
+
export { SquarePicker } from "./components/square-picker.js";
|
|
59
|
+
export { prepareImage, cropSquare, IMAGE_ACCEPT } from "./lib/image.js";
|
|
57
60
|
export { RailLink } from "./components/rail-link.js";
|
|
58
61
|
export { Slider } from "./components/slider.js";
|
|
59
62
|
/* --- açılır bölümler --------------------------------------------------- */
|
package/dist/kit.css
CHANGED
|
@@ -999,7 +999,7 @@
|
|
|
999
999
|
min-width: 28px;
|
|
1000
1000
|
height: 28px;
|
|
1001
1001
|
padding: 0 6px;
|
|
1002
|
-
border-radius: var(--radius-
|
|
1002
|
+
border-radius: var(--radius-sm);
|
|
1003
1003
|
color: var(--color-ink-soft);
|
|
1004
1004
|
font-size: var(--text-caption);
|
|
1005
1005
|
cursor: pointer;
|
|
@@ -1283,9 +1283,14 @@
|
|
|
1283
1283
|
color: var(--color-accent-line);
|
|
1284
1284
|
box-shadow: 2px 2px 0 0 var(--color-accent-line);
|
|
1285
1285
|
}
|
|
1286
|
+
/* DOLGU `accent`, KENAR `accent-shadow` — düğmenin sözleşmesinin aynısı.
|
|
1287
|
+
Burada dolgu `accent-line` idi: o bir METİN token'ı, sayfaya karşı 4.5
|
|
1288
|
+
arayarak üretiliyor, yani yüzden hep daha koyu. Sonuç ekranda şuydu: biten
|
|
1289
|
+
adımlar koyu, aynı ekrandaki her düğme ve rozet açık. Üstündeki mürekkep de
|
|
1290
|
+
`accent`e göre ölçülüyordu, `accent-line`a göre değil. */
|
|
1286
1291
|
.tamga-step-mark[data-state="done"] {
|
|
1287
|
-
border-color: var(--color-accent-
|
|
1288
|
-
background: var(--color-accent
|
|
1292
|
+
border-color: var(--color-accent-shadow);
|
|
1293
|
+
background: var(--color-accent);
|
|
1289
1294
|
color: var(--color-accent-ink);
|
|
1290
1295
|
}
|
|
1291
1296
|
.tamga-step-line {
|
|
@@ -1296,7 +1301,7 @@
|
|
|
1296
1301
|
background: var(--color-edge);
|
|
1297
1302
|
}
|
|
1298
1303
|
.tamga-step-line[data-done="true"] {
|
|
1299
|
-
background: var(--color-accent
|
|
1304
|
+
background: var(--color-accent);
|
|
1300
1305
|
}
|
|
1301
1306
|
|
|
1302
1307
|
.tamga-scrim {
|
|
@@ -1812,6 +1817,209 @@
|
|
|
1812
1817
|
animation: tamga-sheet-end var(--duration-base) var(--ease-standard);
|
|
1813
1818
|
}
|
|
1814
1819
|
|
|
1820
|
+
/* ------------------------------------------------------------------ *
|
|
1821
|
+
* GÖRÜNÜM AYARLARININ PARÇALARI.
|
|
1822
|
+
*
|
|
1823
|
+
* Üçü de bir üründe doğdu ve kite AYNI GEREKÇEYLE taşındı: her panelin bir
|
|
1824
|
+
* görünüm ayarı oluyor, ve kit onu vermediğinde her ürün kendi hex kutusunu
|
|
1825
|
+
* çiziyor. Kiti kuran bir ekip "renk seçimi" deyince altı hazır
|
|
1826
|
+
* kutuyu değil, çıplak bir metin girdisi elde ediyordu.
|
|
1827
|
+
* ------------------------------------------------------------------ */
|
|
1828
|
+
|
|
1829
|
+
/* Marka rengi kutusu. Seçili olan kitin fiziğiyle işaretli: 1px kenar artı
|
|
1830
|
+
2px sert kaydırma. Kutunun İÇİNE çentik konmuyor, çentiğin kontrastı
|
|
1831
|
+
seçilen renge göre değişiyor ve açık bir renkte kayboluyor. */
|
|
1832
|
+
.tamga-swatch {
|
|
1833
|
+
display: inline-flex;
|
|
1834
|
+
width: var(--control);
|
|
1835
|
+
height: var(--control);
|
|
1836
|
+
align-items: center;
|
|
1837
|
+
justify-content: center;
|
|
1838
|
+
border: 1px solid var(--color-edge);
|
|
1839
|
+
border-radius: var(--radius-ctl);
|
|
1840
|
+
background: var(--color-shell);
|
|
1841
|
+
cursor: pointer;
|
|
1842
|
+
transition:
|
|
1843
|
+
transform var(--duration-press) var(--ease-standard),
|
|
1844
|
+
box-shadow var(--duration-press) var(--ease-standard);
|
|
1845
|
+
}
|
|
1846
|
+
.tamga-swatch > span {
|
|
1847
|
+
width: 20px;
|
|
1848
|
+
height: 20px;
|
|
1849
|
+
border-radius: var(--radius-mark);
|
|
1850
|
+
}
|
|
1851
|
+
.tamga-swatch:hover {
|
|
1852
|
+
transform: translate(-1px, -1px);
|
|
1853
|
+
box-shadow: 3px 3px 0 var(--color-edge);
|
|
1854
|
+
}
|
|
1855
|
+
.tamga-swatch[data-selected="true"] {
|
|
1856
|
+
border-color: var(--color-accent-line);
|
|
1857
|
+
box-shadow: 2px 2px 0 var(--color-accent-line);
|
|
1858
|
+
}
|
|
1859
|
+
.tamga-swatch:active {
|
|
1860
|
+
transform: translate(2px, 2px);
|
|
1861
|
+
box-shadow: 0 0 0 var(--color-edge);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/* Tarayıcının kendi renk kutusu, kitin kenarlığıyla. */
|
|
1865
|
+
.tamga-color-input {
|
|
1866
|
+
width: 36px;
|
|
1867
|
+
height: 36px;
|
|
1868
|
+
padding: 2px;
|
|
1869
|
+
border: 1px solid var(--color-edge);
|
|
1870
|
+
border-radius: var(--radius-ctl);
|
|
1871
|
+
background: var(--color-shell);
|
|
1872
|
+
cursor: pointer;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
/* ------------------------------------------------------------------ *
|
|
1876
|
+
* TEMA KARTI — panelin minyatürü.
|
|
1877
|
+
*
|
|
1878
|
+
* "Açık · Koyu · Sistem" üç kelimeydi, ve bir tema seçimi kelimeyle
|
|
1879
|
+
* yapılmıyor: kullanıcı sonucu görmek istiyor. Her seçenek panelin
|
|
1880
|
+
* minyatürünü çiziyor (ray, gövde, satırlar) ve "Sistem" ikisini bir karede
|
|
1881
|
+
* yan yana gösteriyor, çünkü anlamı tam olarak bu.
|
|
1882
|
+
*
|
|
1883
|
+
* MİNYATÜR SABİT RENKLERLE, ve kitin tek istisnası bu. Seçenekler o an
|
|
1884
|
+
* yürürlükteki temayı değil SEÇİLİRSE ne olacağını gösteriyor; token
|
|
1885
|
+
* kullanılsaydı üçü de aynı görünürdü. Tek token: marka vurgusu, çünkü
|
|
1886
|
+
* önizlemenin yarısı o.
|
|
1887
|
+
* ------------------------------------------------------------------ */
|
|
1888
|
+
.tamga-theme-card {
|
|
1889
|
+
display: flex;
|
|
1890
|
+
flex-direction: column;
|
|
1891
|
+
gap: 8px;
|
|
1892
|
+
padding: 0;
|
|
1893
|
+
border: 0;
|
|
1894
|
+
background: none;
|
|
1895
|
+
cursor: pointer;
|
|
1896
|
+
text-align: center;
|
|
1897
|
+
}
|
|
1898
|
+
.tamga-theme-box {
|
|
1899
|
+
position: relative;
|
|
1900
|
+
display: flex;
|
|
1901
|
+
overflow: hidden;
|
|
1902
|
+
width: 112px;
|
|
1903
|
+
height: 72px;
|
|
1904
|
+
border: 1px solid var(--color-edge);
|
|
1905
|
+
border-radius: var(--radius-card);
|
|
1906
|
+
box-shadow: 2px 2px 0 var(--color-edge);
|
|
1907
|
+
transition:
|
|
1908
|
+
transform var(--duration-press) var(--ease-standard),
|
|
1909
|
+
box-shadow var(--duration-press) var(--ease-standard),
|
|
1910
|
+
border-color var(--duration-press) var(--ease-standard);
|
|
1911
|
+
}
|
|
1912
|
+
.tamga-theme-card:hover .tamga-theme-box {
|
|
1913
|
+
transform: translate(-1px, -1px);
|
|
1914
|
+
box-shadow: 3px 3px 0 var(--color-edge);
|
|
1915
|
+
}
|
|
1916
|
+
.tamga-theme-card:active .tamga-theme-box {
|
|
1917
|
+
transform: translate(2px, 2px);
|
|
1918
|
+
box-shadow: 0 0 0 var(--color-edge);
|
|
1919
|
+
}
|
|
1920
|
+
.tamga-theme-card[data-selected="true"] .tamga-theme-box {
|
|
1921
|
+
border-color: var(--color-accent-line);
|
|
1922
|
+
box-shadow: 2px 2px 0 var(--color-accent-line);
|
|
1923
|
+
}
|
|
1924
|
+
.tamga-theme-name {
|
|
1925
|
+
font-size: var(--text-small);
|
|
1926
|
+
color: var(--color-ink-faint);
|
|
1927
|
+
}
|
|
1928
|
+
.tamga-theme-card[data-selected="true"] .tamga-theme-name {
|
|
1929
|
+
font-weight: 500;
|
|
1930
|
+
color: var(--color-ink);
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
.tamga-theme-half {
|
|
1934
|
+
display: flex;
|
|
1935
|
+
min-width: 0;
|
|
1936
|
+
flex: 1;
|
|
1937
|
+
gap: 4px;
|
|
1938
|
+
padding: 6px;
|
|
1939
|
+
}
|
|
1940
|
+
.tamga-theme-half[data-theme="light"] { background: #f4f2ee; }
|
|
1941
|
+
.tamga-theme-half[data-theme="dark"] { background: #16161a; }
|
|
1942
|
+
|
|
1943
|
+
.tamga-theme-rail {
|
|
1944
|
+
display: flex;
|
|
1945
|
+
width: 12px;
|
|
1946
|
+
flex-shrink: 0;
|
|
1947
|
+
flex-direction: column;
|
|
1948
|
+
gap: 3px;
|
|
1949
|
+
border-radius: 3px;
|
|
1950
|
+
padding: 3px;
|
|
1951
|
+
}
|
|
1952
|
+
.tamga-theme-half[data-theme="light"] .tamga-theme-rail { background: #fcfcfc; }
|
|
1953
|
+
.tamga-theme-half[data-theme="dark"] .tamga-theme-rail { background: #232327; }
|
|
1954
|
+
|
|
1955
|
+
.tamga-theme-body {
|
|
1956
|
+
display: flex;
|
|
1957
|
+
min-width: 0;
|
|
1958
|
+
flex: 1;
|
|
1959
|
+
flex-direction: column;
|
|
1960
|
+
gap: 3px;
|
|
1961
|
+
border-radius: 3px;
|
|
1962
|
+
padding: 4px;
|
|
1963
|
+
}
|
|
1964
|
+
.tamga-theme-half[data-theme="light"] .tamga-theme-body { background: #fcfcfc; }
|
|
1965
|
+
.tamga-theme-half[data-theme="dark"] .tamga-theme-body { background: #232327; }
|
|
1966
|
+
|
|
1967
|
+
.tamga-theme-line {
|
|
1968
|
+
height: 3px;
|
|
1969
|
+
border-radius: 2px;
|
|
1970
|
+
}
|
|
1971
|
+
.tamga-theme-half[data-theme="light"] .tamga-theme-line { background: #d8d4cf; }
|
|
1972
|
+
.tamga-theme-half[data-theme="dark"] .tamga-theme-line { background: #3a3a42; }
|
|
1973
|
+
/* Vurgu tema kuralını YENMEK ZORUNDA: daha düşük özgüllükte yazıldığında
|
|
1974
|
+
`[data-theme] .tamga-theme-line` onu eziyor ve marka rengi önizlemede hiç
|
|
1975
|
+
görünmüyor. */
|
|
1976
|
+
.tamga-theme-half .tamga-theme-line[data-accent="true"] {
|
|
1977
|
+
background: var(--color-accent);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
.tamga-theme-dot {
|
|
1981
|
+
height: 5px;
|
|
1982
|
+
width: 5px;
|
|
1983
|
+
border-radius: 2px;
|
|
1984
|
+
}
|
|
1985
|
+
.tamga-theme-half[data-theme="light"] .tamga-theme-dot { background: #d8d4cf; }
|
|
1986
|
+
.tamga-theme-half[data-theme="dark"] .tamga-theme-dot { background: #3a3a42; }
|
|
1987
|
+
.tamga-theme-half .tamga-theme-dot[data-accent="true"] {
|
|
1988
|
+
background: var(--color-accent);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
/* Yüklenen görselin durduğu kutu. Çıplak duran bir logo "buraya yüklendi"
|
|
1992
|
+
demiyor, "burada unutulmuş" diyor. Yükseklik sabit: logo geniş, amblem
|
|
1993
|
+
kare, ve ikisi farklı yükseklikte kutularda dursaydı bloklar hizasız
|
|
1994
|
+
görünürdü. */
|
|
1995
|
+
.tamga-image-field {
|
|
1996
|
+
display: flex;
|
|
1997
|
+
align-items: center;
|
|
1998
|
+
justify-content: center;
|
|
1999
|
+
height: 72px;
|
|
2000
|
+
min-width: 96px;
|
|
2001
|
+
padding: 10px 14px;
|
|
2002
|
+
border: 1px solid var(--color-edge);
|
|
2003
|
+
border-radius: var(--radius-card);
|
|
2004
|
+
background: var(--color-shell);
|
|
2005
|
+
box-shadow: 2px 2px 0 var(--color-edge);
|
|
2006
|
+
}
|
|
2007
|
+
/* Boş hâl: kesik çizgi "burası dolabilir" diyor, düz kenar "burası boş
|
|
2008
|
+
kaldı" diyor. */
|
|
2009
|
+
.tamga-image-field[data-empty] {
|
|
2010
|
+
border-style: dashed;
|
|
2011
|
+
box-shadow: none;
|
|
2012
|
+
color: var(--color-ink-faint);
|
|
2013
|
+
font-size: var(--text-small);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/* Ayar bölümü, sol kenarında marka rengi. `SettingsPanel`in `accent`
|
|
2017
|
+
varyantı: bir görünüm ekranında bloklar birbirinden ayrılmalı ve ayıran
|
|
2018
|
+
şey rengin kendisi olmalı, çünkü ekranın konusu o. */
|
|
2019
|
+
.tamga-settings-panel-accent {
|
|
2020
|
+
border-left: 4px solid var(--color-accent);
|
|
2021
|
+
}
|
|
2022
|
+
|
|
1815
2023
|
.tamga-label {
|
|
1816
2024
|
font-size: var(--text-caption);
|
|
1817
2025
|
font-weight: 500;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning an uploaded file into something a panel can carry.
|
|
3
|
+
*
|
|
4
|
+
* NO LIBRARY, and none is needed: a `<canvas>` and `drawImage`.
|
|
5
|
+
*
|
|
6
|
+
* A DATA URI, NOT AN OBJECT URL. `URL.createObjectURL` dies with the tab, so a
|
|
7
|
+
* logo chosen today would be gone tomorrow. A data URI survives in storage and
|
|
8
|
+
* goes straight into `<img src>`. When a server arrives this file becomes an
|
|
9
|
+
* upload call and nothing else changes: screens already hold a STRING and do
|
|
10
|
+
* not care whether it is a data URI or an `https://` address.
|
|
11
|
+
*
|
|
12
|
+
* WHY IT IS RESIZED. `localStorage` holds about 5 MB per browser and a phone
|
|
13
|
+
* photo alone is 4 MB. Writing one unresized throws `QuotaExceeded`, and the
|
|
14
|
+
* thing that gets lost may not be the logo but whatever else was being saved
|
|
15
|
+
* at that moment.
|
|
16
|
+
*
|
|
17
|
+
* THE RATIO IS KEPT, NOTHING IS CROPPED. Cropping to fit a square is the most
|
|
18
|
+
* common and most silent mistake: the logo looks fine and its edge is gone.
|
|
19
|
+
* The long side is pulled to the limit, the short side follows.
|
|
20
|
+
*
|
|
21
|
+
* PNG, NOT JPEG. Logos are flat and transparent; JPEG turns transparency into
|
|
22
|
+
* solid black and rings the edges.
|
|
23
|
+
*/
|
|
24
|
+
export type ImageError = "type" | "size" | "unreadable";
|
|
25
|
+
export declare const IMAGE_ACCEPT = "image/png,image/jpeg,image/svg+xml,image/webp";
|
|
26
|
+
/**
|
|
27
|
+
* Resizes so the longest side is `maxEdge` pixels and returns a data URI.
|
|
28
|
+
*
|
|
29
|
+
* SVG PASSES THROUGH UNTOUCHED. Drawing a vector onto a canvas and writing PNG
|
|
30
|
+
* stops it being scalable: a logo baked at 512px is blurry on a 4K screen. SVG
|
|
31
|
+
* is already small and already sharp at every size.
|
|
32
|
+
*/
|
|
33
|
+
export declare function prepareImage(file: File, maxEdge: number): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Cuts a SQUARE piece out of an image: for a mark or a profile photo.
|
|
36
|
+
*
|
|
37
|
+
* `area` is given in RATIOS (0 to 1), not in the image's own pixels, because
|
|
38
|
+
* the picker runs on a preview whose scale changes from screen to screen. A
|
|
39
|
+
* ratio points at the same place at every scale.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cropSquare(source: string, area: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
size: number;
|
|
45
|
+
}, maxEdge: number): Promise<string>;
|
|
46
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/lib/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAC;AAKxD,eAAO,MAAM,YAAY,kDAAkD,CAAC;AAE5E;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB/E;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC5C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning an uploaded file into something a panel can carry.
|
|
3
|
+
*
|
|
4
|
+
* NO LIBRARY, and none is needed: a `<canvas>` and `drawImage`.
|
|
5
|
+
*
|
|
6
|
+
* A DATA URI, NOT AN OBJECT URL. `URL.createObjectURL` dies with the tab, so a
|
|
7
|
+
* logo chosen today would be gone tomorrow. A data URI survives in storage and
|
|
8
|
+
* goes straight into `<img src>`. When a server arrives this file becomes an
|
|
9
|
+
* upload call and nothing else changes: screens already hold a STRING and do
|
|
10
|
+
* not care whether it is a data URI or an `https://` address.
|
|
11
|
+
*
|
|
12
|
+
* WHY IT IS RESIZED. `localStorage` holds about 5 MB per browser and a phone
|
|
13
|
+
* photo alone is 4 MB. Writing one unresized throws `QuotaExceeded`, and the
|
|
14
|
+
* thing that gets lost may not be the logo but whatever else was being saved
|
|
15
|
+
* at that moment.
|
|
16
|
+
*
|
|
17
|
+
* THE RATIO IS KEPT, NOTHING IS CROPPED. Cropping to fit a square is the most
|
|
18
|
+
* common and most silent mistake: the logo looks fine and its edge is gone.
|
|
19
|
+
* The long side is pulled to the limit, the short side follows.
|
|
20
|
+
*
|
|
21
|
+
* PNG, NOT JPEG. Logos are flat and transparent; JPEG turns transparency into
|
|
22
|
+
* solid black and rings the edges.
|
|
23
|
+
*/
|
|
24
|
+
/** Raw file limit, before resizing. Over 8 MB is not a logo, it is an accident. */
|
|
25
|
+
const RAW_LIMIT = 8 * 1024 * 1024;
|
|
26
|
+
export const IMAGE_ACCEPT = "image/png,image/jpeg,image/svg+xml,image/webp";
|
|
27
|
+
/**
|
|
28
|
+
* Resizes so the longest side is `maxEdge` pixels and returns a data URI.
|
|
29
|
+
*
|
|
30
|
+
* SVG PASSES THROUGH UNTOUCHED. Drawing a vector onto a canvas and writing PNG
|
|
31
|
+
* stops it being scalable: a logo baked at 512px is blurry on a 4K screen. SVG
|
|
32
|
+
* is already small and already sharp at every size.
|
|
33
|
+
*/
|
|
34
|
+
export async function prepareImage(file, maxEdge) {
|
|
35
|
+
if (!file.type.startsWith("image/"))
|
|
36
|
+
throw new Error("type");
|
|
37
|
+
if (file.size > RAW_LIMIT)
|
|
38
|
+
throw new Error("size");
|
|
39
|
+
const raw = await readFile(file);
|
|
40
|
+
if (file.type === "image/svg+xml")
|
|
41
|
+
return raw;
|
|
42
|
+
const image = await loadImage(raw);
|
|
43
|
+
const scale = Math.min(1, maxEdge / Math.max(image.width, image.height));
|
|
44
|
+
if (scale >= 1)
|
|
45
|
+
return raw;
|
|
46
|
+
const canvas = document.createElement("canvas");
|
|
47
|
+
canvas.width = Math.round(image.width * scale);
|
|
48
|
+
canvas.height = Math.round(image.height * scale);
|
|
49
|
+
const ctx = canvas.getContext("2d");
|
|
50
|
+
if (!ctx)
|
|
51
|
+
return raw;
|
|
52
|
+
/* The browser's own smoothing: better than a hand-written resampler, and free. */
|
|
53
|
+
ctx.imageSmoothingQuality = "high";
|
|
54
|
+
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
55
|
+
return canvas.toDataURL("image/png");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Cuts a SQUARE piece out of an image: for a mark or a profile photo.
|
|
59
|
+
*
|
|
60
|
+
* `area` is given in RATIOS (0 to 1), not in the image's own pixels, because
|
|
61
|
+
* the picker runs on a preview whose scale changes from screen to screen. A
|
|
62
|
+
* ratio points at the same place at every scale.
|
|
63
|
+
*/
|
|
64
|
+
export async function cropSquare(source, area, maxEdge) {
|
|
65
|
+
const image = await loadImage(source);
|
|
66
|
+
/* The square is measured against the SHORT side: a 350x65 logo can hold a
|
|
67
|
+
square of at most 65px. Measured against the long side the frame would
|
|
68
|
+
fall outside the image. */
|
|
69
|
+
const edge = Math.min(image.width, image.height) * area.size;
|
|
70
|
+
const canvas = document.createElement("canvas");
|
|
71
|
+
canvas.width = maxEdge;
|
|
72
|
+
canvas.height = maxEdge;
|
|
73
|
+
const ctx = canvas.getContext("2d");
|
|
74
|
+
if (!ctx)
|
|
75
|
+
return source;
|
|
76
|
+
ctx.imageSmoothingQuality = "high";
|
|
77
|
+
ctx.drawImage(image, area.x * image.width, area.y * image.height, edge, edge, 0, 0, maxEdge, maxEdge);
|
|
78
|
+
return canvas.toDataURL("image/png");
|
|
79
|
+
}
|
|
80
|
+
function readFile(file) {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
const reader = new FileReader();
|
|
83
|
+
reader.onload = () => resolve(String(reader.result));
|
|
84
|
+
reader.onerror = () => reject(new Error("unreadable"));
|
|
85
|
+
reader.readAsDataURL(file);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function loadImage(src) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const img = new Image();
|
|
91
|
+
img.onload = () => resolve(img);
|
|
92
|
+
img.onerror = () => reject(new Error("unreadable"));
|
|
93
|
+
img.src = src;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../src/patterns/app-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,wKAAwK;IACxK,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,wJAAwJ;IACxJ,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzB,mGAAmG;IACnG,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,uIAAuI;IACvI,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,GAAG,EACH,UAAU,EACV,IAAe,EACf,MAAM,EACN,UAAU,EACV,aAAa,EAAE,IAAgB,EAC/B,MAAM,EACN,QAAQ,GACT,EAAE,aAAa,+
|
|
1
|
+
{"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../src/patterns/app-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,wKAAwK;IACxK,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,wJAAwJ;IACxJ,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzB,mGAAmG;IACnG,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,uIAAuI;IACvI,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,GAAG,EACH,UAAU,EACV,IAAe,EACf,MAAM,EACN,UAAU,EACV,aAAa,EAAE,IAAgB,EAC/B,MAAM,EACN,QAAQ,GACT,EAAE,aAAa,+BAuEf"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Icon } from "../components/icon.js";
|
|
4
|
-
import {
|
|
4
|
+
import { RailLink } from "../components/rail-link.js";
|
|
5
5
|
import { PlainLink } from "./shared.js";
|
|
6
6
|
export function AppShell({ brand, nav, activePath, rail = "narrow", topbar, railFooter, linkComponent: Link = PlainLink, labels, children, }) {
|
|
7
7
|
/* Bir giriş kendi alt ağacına sahip: `/urunler`, `/urunler/42/fotograflar`
|
|
@@ -9,15 +9,5 @@ export function AppShell({ brand, nav, activePath, rail = "narrow", topbar, rail
|
|
|
9
9
|
altındaki her rotayı yutar. */
|
|
10
10
|
const isCurrent = (href) => href === "/" ? activePath === href : activePath === href || activePath.startsWith(`${href}/`);
|
|
11
11
|
const wide = rail === "wide";
|
|
12
|
-
return (_jsxs("div", { className: "flex h-dvh w-full overflow-hidden bg-shell text-ink-soft", children: [_jsxs("aside", { className: `flex shrink-0 flex-col gap-2 bg-shell py-4 ${wide ? "w-60 px-3" : "w-20 items-center px-1"}`, "data-rail": rail, children: [brand ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-label": labels.home, className: `mb-2 flex h-10 shrink-0 items-center text-ink ${wide ? "px-2" : "w-10 justify-center"}`, children: brand }), _jsx("span", { className: `h-0 w-6 border-t border-line ${wide ? "ml-2" : ""}` })] })) : null, _jsx("nav", { "aria-label": labels.primaryNav, className: `tamga-rail-scroll flex w-full flex-1 flex-col gap-2 py-1 ${wide ? "" : "items-center"}`, children: nav.map((entry) => {
|
|
13
|
-
const current = isCurrent(entry.href);
|
|
14
|
-
const link = (_jsxs(Link, { href: entry.href, "aria-label": wide ? undefined : entry.label, "aria-current": current ? "page" : undefined, "data-active": current, "data-nav": entry.key, className: "tamga-rail-link", children: [_jsx(Icon, { icon: entry.icon, size: "base" }), wide ? _jsx("span", { className: "min-w-0 truncate", children: entry.label }) : null] }, entry.key));
|
|
15
|
-
/* DAR RAYDA KİTİN İPUCU, `title` DEĞİL.
|
|
16
|
-
Burada `title={entry.label}` vardı: tarayıcının kendi gecikmeli
|
|
17
|
-
gri balonu. Kullanan kişi bunu "ipucu yok" diye okuyor, çünkü
|
|
18
|
-
kitin her yerdeki ipucu başka türlü görünüyor ve hemen çıkıyor.
|
|
19
|
-
Geniş rayda etiket zaten yazılı; orada ipucu aynı sözcüğü ikinci
|
|
20
|
-
kez söylemek olurdu. */
|
|
21
|
-
return wide ? (link) : (_jsx(Tooltip, { label: entry.label, placement: "right", children: link }, entry.key));
|
|
22
|
-
}) }), railFooter ? _jsx("div", { className: "shrink-0", children: railFooter }) : null] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("header", { className: "tamga-gutter flex items-center gap-4 py-4", children: topbar }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden pr-4 pb-4", children: _jsx("main", { className: "tamga-surface h-full min-h-0 overflow-y-auto", children: children }) })] })] }));
|
|
12
|
+
return (_jsxs("div", { className: "flex h-dvh w-full overflow-hidden bg-shell text-ink-soft", children: [_jsxs("aside", { className: `flex shrink-0 flex-col gap-2 bg-shell py-4 ${wide ? "w-60 px-3" : "w-20 items-center px-1"}`, "data-rail": rail, children: [brand ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-label": labels.home, className: `mb-2 flex h-10 shrink-0 items-center text-ink ${wide ? "px-2" : "w-10 justify-center"}`, children: brand }), _jsx("span", { className: `h-0 w-6 border-t border-line ${wide ? "ml-2" : ""}` })] })) : null, _jsx("nav", { "aria-label": labels.primaryNav, className: `tamga-rail-scroll flex w-full flex-1 flex-col gap-2 py-1 ${wide ? "" : "items-center"}`, children: nav.map((entry) => (_jsx(RailLink, { href: entry.href, label: entry.label, active: isCurrent(entry.href), showLabel: wide, linkComponent: Link, "data-nav": entry.key, children: _jsx(Icon, { icon: entry.icon, size: "base" }) }, entry.key))) }), railFooter ? _jsx("div", { className: "shrink-0", children: railFooter }) : null] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("header", { className: "tamga-gutter flex items-center gap-4 py-4", children: topbar }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden pr-4 pb-4", children: _jsx("main", { className: "tamga-surface h-full min-h-0 overflow-y-auto", children: children }) })] })] }));
|
|
23
13
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type SwatchOption, type ThemeChoice } from "../components/appearance.js";
|
|
3
|
+
/**
|
|
4
|
+
* The Appearance screen: logo, mark, brand colour, theme, sidebar.
|
|
5
|
+
*
|
|
6
|
+
* WHY A WHOLE SCREEN AND NOT FIVE PARTS. Every panel grows an appearance
|
|
7
|
+
* setting, and the kit not giving one is why two products drew their own hex
|
|
8
|
+
* box. Handing over the parts would not have fixed it: each product arranges
|
|
9
|
+
* them differently and they drift apart again, which is the thing this layer
|
|
10
|
+
* exists to prevent (ADR-0004).
|
|
11
|
+
*
|
|
12
|
+
* IT STORES NOTHING. `value` comes in, `onChange` goes out, `onSave` belongs
|
|
13
|
+
* to the caller. Where the preference lives (session, account, browser) is a
|
|
14
|
+
* product decision and a template that learns it stops being a template.
|
|
15
|
+
*
|
|
16
|
+
* THE PALETTE IS NOT APPLIED HERE EITHER. This screen reports the chosen
|
|
17
|
+
* colour; turning it into tokens is `makePalette` plus `paletteVars`, and the
|
|
18
|
+
* product decides when that happens, because the product owns the root
|
|
19
|
+
* element.
|
|
20
|
+
*/
|
|
21
|
+
export type Appearance = {
|
|
22
|
+
brand: string;
|
|
23
|
+
theme: ThemeChoice;
|
|
24
|
+
/** `free` means each user decides. TR: `free`, her kullanıcı kendi seçer demek. */
|
|
25
|
+
rail: "narrow" | "wide" | "free";
|
|
26
|
+
logo: string | null;
|
|
27
|
+
mark: string | null;
|
|
28
|
+
};
|
|
29
|
+
export type AppearanceLabels = {
|
|
30
|
+
title: string;
|
|
31
|
+
description: string;
|
|
32
|
+
logo: {
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
field: ImageLabels;
|
|
36
|
+
pickMark: string;
|
|
37
|
+
};
|
|
38
|
+
mark: {
|
|
39
|
+
title: string;
|
|
40
|
+
description: string;
|
|
41
|
+
field: ImageLabels;
|
|
42
|
+
fallbackNote: string;
|
|
43
|
+
};
|
|
44
|
+
brand: {
|
|
45
|
+
title: string;
|
|
46
|
+
description: string;
|
|
47
|
+
swatches: readonly SwatchOption[];
|
|
48
|
+
custom: string;
|
|
49
|
+
};
|
|
50
|
+
theme: {
|
|
51
|
+
title: string;
|
|
52
|
+
description: string;
|
|
53
|
+
light: string;
|
|
54
|
+
dark: string;
|
|
55
|
+
system: string;
|
|
56
|
+
group: string;
|
|
57
|
+
};
|
|
58
|
+
rail: {
|
|
59
|
+
title: string;
|
|
60
|
+
description: string;
|
|
61
|
+
narrow: string;
|
|
62
|
+
wide: string;
|
|
63
|
+
free: string;
|
|
64
|
+
group: string;
|
|
65
|
+
};
|
|
66
|
+
picker: {
|
|
67
|
+
title: string;
|
|
68
|
+
hint: string;
|
|
69
|
+
size: string;
|
|
70
|
+
cancel: string;
|
|
71
|
+
confirm: string;
|
|
72
|
+
close: string;
|
|
73
|
+
};
|
|
74
|
+
save: string;
|
|
75
|
+
saving: string;
|
|
76
|
+
reset: string;
|
|
77
|
+
clean: string;
|
|
78
|
+
dirty: string;
|
|
79
|
+
};
|
|
80
|
+
type ImageLabels = {
|
|
81
|
+
name: string;
|
|
82
|
+
upload: string;
|
|
83
|
+
replace: string;
|
|
84
|
+
remove: string;
|
|
85
|
+
empty: string;
|
|
86
|
+
errorType: string;
|
|
87
|
+
errorSize: string;
|
|
88
|
+
errorUnreadable: string;
|
|
89
|
+
};
|
|
90
|
+
export declare function AppearanceTemplate({ value, onChange, saved, onSave, onReset, saving, fallbackName, labels, extra, }: {
|
|
91
|
+
value: Appearance;
|
|
92
|
+
onChange: (next: Appearance) => void;
|
|
93
|
+
/** The stored version; "is anything unsaved" is answered by comparing with it. TR: Kayıtlı hâli; "kaydedilmemiş bir şey var mı" sorusu bununla cevaplanıyor. */
|
|
94
|
+
saved: Appearance;
|
|
95
|
+
onSave: () => void;
|
|
96
|
+
onReset: () => void;
|
|
97
|
+
saving?: boolean;
|
|
98
|
+
/** The name a tile is drawn from when there is no mark. TR: Amblem yokken karonun çizildiği ad. */
|
|
99
|
+
fallbackName: string;
|
|
100
|
+
labels: AppearanceLabels;
|
|
101
|
+
/** Extra panels the product adds, after the built-in ones. TR: Ürünün eklediği ek bölümler, hazır olanlardan sonra. */
|
|
102
|
+
extra?: ReactNode;
|
|
103
|
+
}): import("react").JSX.Element;
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=appearance-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance-template.d.ts","sourceRoot":"","sources":["../../src/patterns/appearance-template.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { ColorSwatches, ImageField, ThemeCards } from "../components/appearance.js";
|
|
5
|
+
import { LogoTile } from "../components/chrome.js";
|
|
6
|
+
import { Segmented } from "../components/segmented.js";
|
|
7
|
+
import { SquarePicker } from "../components/square-picker.js";
|
|
8
|
+
import { Button } from "../components/button.js";
|
|
9
|
+
import { Label } from "../components/surface.js";
|
|
10
|
+
import { SettingsPanel } from "./settings-template.js";
|
|
11
|
+
export function AppearanceTemplate({ value, onChange, saved, onSave, onReset, saving = false, fallbackName, labels, extra, }) {
|
|
12
|
+
const [picking, setPicking] = useState(false);
|
|
13
|
+
const dirty = JSON.stringify(value) !== JSON.stringify(saved);
|
|
14
|
+
return (_jsxs("form", { className: "flex flex-col gap-5", onSubmit: (e) => {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
if (dirty && !saving)
|
|
17
|
+
onSave();
|
|
18
|
+
}, children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h1", { className: "text-display-sm font-semibold text-ink", children: labels.title }), _jsx("p", { className: "w-full text-control leading-relaxed text-ink-soft", children: labels.description })] }), _jsx(SettingsPanel, { accent: true, title: labels.logo.title, description: labels.logo.description, children: _jsx(ImageField, { value: value.logo, onChange: (v) => onChange({ ...value, logo: v }), maxEdge: 512, labels: labels.logo.field, preview: (src) => (_jsx("img", { src: src, alt: "", className: "max-h-full w-auto max-w-56 object-contain" })), extra: value.logo ? (_jsx(Button, { type: "button", onClick: () => setPicking(true), children: labels.logo.pickMark })) : undefined }) }), _jsxs(SettingsPanel, { accent: true, title: labels.mark.title, description: labels.mark.description, children: [_jsx(ImageField, { value: value.mark, onChange: (v) => onChange({ ...value, mark: v }), maxEdge: 256, labels: labels.mark.field, preview: (src) => (_jsx("img", { src: src, alt: "", className: "size-12 object-contain" })) }), !value.mark ? (_jsxs("span", { className: "mt-4 flex items-center gap-3", children: [_jsx(LogoTile, { name: fallbackName }), _jsx(Label, { children: labels.mark.fallbackNote })] })) : null] }), _jsx(SettingsPanel, { accent: true, title: labels.brand.title, description: labels.brand.description, children: _jsx(ColorSwatches, { options: labels.brand.swatches, value: value.brand, onChange: (hex) => onChange({ ...value, brand: hex }), customLabel: labels.brand.custom }) }), _jsx(SettingsPanel, { accent: true, title: labels.theme.title, description: labels.theme.description, children: _jsx(ThemeCards, { value: value.theme, onChange: (t) => onChange({ ...value, theme: t }), labels: labels.theme }) }), _jsx(SettingsPanel, { accent: true, title: labels.rail.title, description: labels.rail.description, children: _jsx(Segmented, { label: labels.rail.group, value: value.rail, onChange: (r) => onChange({ ...value, rail: r }), options: [
|
|
19
|
+
{ value: "narrow", label: labels.rail.narrow },
|
|
20
|
+
{ value: "wide", label: labels.rail.wide },
|
|
21
|
+
{ value: "free", label: labels.rail.free },
|
|
22
|
+
] }) }), extra, value.logo ? (_jsx(SquarePicker, { open: picking, source: value.logo, onClose: () => setPicking(false), onPick: (square) => onChange({ ...value, mark: square }), labels: labels.picker })) : null, _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("span", { className: "flex flex-wrap items-center gap-3", children: [_jsx(Button, { variant: "primary", type: "submit", disabled: !dirty, busy: saving, busyLabel: labels.saving, children: labels.save }), _jsx(Button, { type: "button", disabled: saving, onClick: onReset, children: labels.reset })] }), _jsx(Label, { children: dirty ? labels.dirty : labels.clean })] })] }));
|
|
23
|
+
}
|
package/dist/patterns/index.d.ts
CHANGED
|
@@ -15,4 +15,5 @@ export { AuthTemplate, AuthProviders } from "./auth-template.js";
|
|
|
15
15
|
export { WizardTemplate, type WizardStep, type WizardTemplateProps } from "./wizard-template.js";
|
|
16
16
|
export { PublicTemplate, type PublicState, type PublicTemplateProps } from "./public-template.js";
|
|
17
17
|
export { AppShell, type NavEntry, type AppShellProps } from "./app-shell.js";
|
|
18
|
+
export { AppearanceTemplate, type Appearance, type AppearanceLabels, } from "./appearance-template.js";
|
|
18
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/patterns/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EACL,SAAS,EACT,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EACL,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/patterns/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EACL,SAAS,EACT,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EACL,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,0BAA0B,CAAC"}
|
package/dist/patterns/index.js
CHANGED
|
@@ -19,3 +19,4 @@ export { AuthTemplate, AuthProviders } from "./auth-template.js";
|
|
|
19
19
|
export { WizardTemplate } from "./wizard-template.js";
|
|
20
20
|
export { PublicTemplate } from "./public-template.js";
|
|
21
21
|
export { AppShell } from "./app-shell.js";
|
|
22
|
+
export { AppearanceTemplate, } from "./appearance-template.js";
|
|
@@ -40,9 +40,20 @@ export declare function SettingsTemplate({ sections, activeSection, title, subti
|
|
|
40
40
|
* Bir değer üst bir düzeyden miras alınıyorsa, bunu bilmeden değiştirmek üç
|
|
41
41
|
* ekran açıp "bu alan neden böyle yazıyor" diye aramak demek.
|
|
42
42
|
*/
|
|
43
|
-
export declare function SettingsPanel({ title, description, inheritedFrom, children, }: {
|
|
43
|
+
export declare function SettingsPanel({ title, description, inheritedFrom, accent, children, }: {
|
|
44
44
|
title: string;
|
|
45
45
|
description?: string;
|
|
46
|
+
/**
|
|
47
|
+
* A brand-coloured edge down the left of the panel. TR: Panelin sol kenarında marka renginde
|
|
48
|
+
* bir şerit.
|
|
49
|
+
*
|
|
50
|
+
* For screens whose SUBJECT is the brand: on an appearance screen the blocks have to separate
|
|
51
|
+
* from one another, and the thing that separates them should be the colour itself. Elsewhere
|
|
52
|
+
* it would be decoration. TR: Konusu MARKANIN KENDİSİ olan ekranlar için: bir görünüm
|
|
53
|
+
* ekranında bloklar birbirinden ayrılmalı ve ayıran şey rengin kendisi olmalı. Başka yerde
|
|
54
|
+
* süs olurdu.
|
|
55
|
+
*/
|
|
56
|
+
accent?: boolean;
|
|
46
57
|
/** Like "inherited from the workspace": a full sentence written by the caller. TR: "çalışma alanından geliyor" gibi, çağıran tarafından yazılmış tam cümle. */
|
|
47
58
|
inheritedFrom?: string;
|
|
48
59
|
children: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-template.d.ts","sourceRoot":"","sources":["../../src/patterns/settings-template.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1D,kCAAkC;AAClC,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gHAAgH;IAChH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;KACtC,GAAG,WAAW,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,aAAa,EACb,KAAK,EACL,QAAQ,EACR,OAAO,EACP,KAAe,EACf,KAAK,EACL,aAAa,EAAE,IAAgB,EAC/B,MAAM,EACN,QAAQ,GACT,EAAE,qBAAqB,+BAsDvB;AA0BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,WAAW,EACX,aAAa,EACb,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+JAA+J;IAC/J,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;CACrB,+
|
|
1
|
+
{"version":3,"file":"settings-template.d.ts","sourceRoot":"","sources":["../../src/patterns/settings-template.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1D,kCAAkC;AAClC,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gHAAgH;IAChH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;KACtC,GAAG,WAAW,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,aAAa,EACb,KAAK,EACL,QAAQ,EACR,OAAO,EACP,KAAe,EACf,KAAK,EACL,aAAa,EAAE,IAAgB,EAC/B,MAAM,EACN,QAAQ,GACT,EAAE,qBAAqB,+BAsDvB;AA0BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,WAAW,EACX,aAAa,EACb,MAAc,EACd,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+JAA+J;IAC/J,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BA6BA"}
|
|
@@ -28,6 +28,6 @@ function ScopeMark({ scope, label }) {
|
|
|
28
28
|
* Bir değer üst bir düzeyden miras alınıyorsa, bunu bilmeden değiştirmek üç
|
|
29
29
|
* ekran açıp "bu alan neden böyle yazıyor" diye aramak demek.
|
|
30
30
|
*/
|
|
31
|
-
export function SettingsPanel({ title, description, inheritedFrom, children, }) {
|
|
32
|
-
return (_jsxs("section", { className:
|
|
31
|
+
export function SettingsPanel({ title, description, inheritedFrom, accent = false, children, }) {
|
|
32
|
+
return (_jsxs("section", { className: `tamga-card tamga-card-open tamga-gutter py-4${accent ? " tamga-settings-panel-accent" : ""}`, "data-panel": title, children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-2", children: [_jsx("h3", { className: "text-subhead font-semibold text-ink", children: title }), inheritedFrom ? (_jsx("span", { className: "text-caption text-ink-faint", "data-inherited-from": true, children: inheritedFrom })) : null] }), description ? (_jsx("p", { className: "mt-1 text-small leading-relaxed text-ink-soft", children: description })) : null, _jsx("div", { className: "mt-4", children: children })] }));
|
|
33
33
|
}
|