tamga-ui 0.1.1 → 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 +128 -0
- package/README.md +83 -91
- package/dist/blocks/count-row.d.ts +3 -3
- package/dist/blocks/count-row.d.ts.map +1 -1
- package/dist/blocks/filter-bar.d.ts +13 -13
- package/dist/blocks/filter-bar.d.ts.map +1 -1
- package/dist/blocks/save-bar.d.ts +3 -3
- package/dist/blocks/save-bar.d.ts.map +1 -1
- 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/chrome.d.ts +54 -3
- package/dist/components/chrome.d.ts.map +1 -1
- package/dist/components/chrome.js +43 -2
- package/dist/components/primitives.d.ts +32 -2
- package/dist/components/primitives.d.ts.map +1 -1
- package/dist/components/primitives.js +11 -4
- 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 +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/kit.css +257 -4
- package/dist/lib/color.d.ts +31 -11
- package/dist/lib/color.d.ts.map +1 -1
- package/dist/lib/color.js +40 -18
- 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/lib/palette.d.ts +38 -19
- package/dist/lib/palette.d.ts.map +1 -1
- package/dist/lib/palette.js +45 -33
- package/dist/patterns/app-shell.d.ts +22 -6
- package/dist/patterns/app-shell.d.ts.map +1 -1
- package/dist/patterns/app-shell.js +4 -5
- 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/auth-template.d.ts +9 -6
- package/dist/patterns/auth-template.d.ts.map +1 -1
- package/dist/patterns/detail-template.d.ts +5 -5
- package/dist/patterns/detail-template.d.ts.map +1 -1
- package/dist/patterns/index.d.ts +2 -1
- package/dist/patterns/index.d.ts.map +1 -1
- package/dist/patterns/index.js +6 -1
- package/dist/patterns/list-template.d.ts +6 -6
- package/dist/patterns/list-template.d.ts.map +1 -1
- package/dist/patterns/overview-template.d.ts +3 -3
- package/dist/patterns/overview-template.d.ts.map +1 -1
- package/dist/patterns/public-template.d.ts +3 -3
- package/dist/patterns/public-template.d.ts.map +1 -1
- package/dist/patterns/settings-template.d.ts +15 -4
- package/dist/patterns/settings-template.d.ts.map +1 -1
- package/dist/patterns/settings-template.js +2 -2
- package/dist/patterns/shared.d.ts +3 -3
- package/dist/patterns/shared.d.ts.map +1 -1
- package/dist/patterns/wizard-template.d.ts +1 -1
- package/dist/patterns/wizard-template.d.ts.map +1 -1
- package/dist/patterns/wizard-template.js +2 -9
- package/package.json +3 -2
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 {
|
|
@@ -1659,6 +1664,14 @@
|
|
|
1659
1664
|
.tamga-table tbody tr[data-selected="true"] td:first-child {
|
|
1660
1665
|
box-shadow: inset 2px 0 0 var(--color-accent-line);
|
|
1661
1666
|
}
|
|
1667
|
+
/* SEÇİLİ SATIR İKİ ADLA GELEBİLİYOR, VE BİRİ BOYANMIYORDU.
|
|
1668
|
+
`SettingsTemplate` menüsündeki açık bölümü `data-active` ile işaretliyor
|
|
1669
|
+
(bir GEZİNME satırı: hangi sayfadasın), liste satırları ise `data-selected`
|
|
1670
|
+
ile (bir SEÇİM: hangi kayıtları işaretledin). İkisi farklı anlam ama aynı
|
|
1671
|
+
görünüm, ve kural yalnız ikincisi için yazılmıştı: ayarlar menüsünde
|
|
1672
|
+
hangi bölümde olduğun hiç görünmüyordu. Hiçbir şey patlamadı, çünkü
|
|
1673
|
+
tarayıcı bilmediği bir nitelik seçicisini sessizce atlıyor. */
|
|
1674
|
+
.tamga-list-row[data-active="true"],
|
|
1662
1675
|
.tamga-list-row[data-selected="true"] {
|
|
1663
1676
|
background: var(--color-accent-bg);
|
|
1664
1677
|
box-shadow: inset 2px 0 0 var(--color-accent-line);
|
|
@@ -1668,6 +1681,10 @@
|
|
|
1668
1681
|
color: var(--color-accent-line);
|
|
1669
1682
|
font-weight: 500;
|
|
1670
1683
|
text-underline-offset: 3px;
|
|
1684
|
+
/* `<a href>` pointer'ı tarayıcıdan alır; `href`siz bir `<a>`, bir `<span>`
|
|
1685
|
+
ya da link gibi görünen bir `<button>` almaz ve imleç `auto` kalır. Sınıf
|
|
1686
|
+
kendi imlecini söylemeli. */
|
|
1687
|
+
cursor: pointer;
|
|
1671
1688
|
}
|
|
1672
1689
|
.tamga-link:hover {
|
|
1673
1690
|
text-decoration: underline;
|
|
@@ -1791,6 +1808,218 @@
|
|
|
1791
1808
|
}
|
|
1792
1809
|
}
|
|
1793
1810
|
|
|
1811
|
+
/* Çekmece kenardan kayarak girer; yön `side` ile aynı olmak zorunda, yoksa
|
|
1812
|
+
kapatma hareketi ters öğretilir. Gerekçe: /docs/sheet */
|
|
1813
|
+
.tamga-sheet-start {
|
|
1814
|
+
animation: tamga-sheet-start var(--duration-base) var(--ease-standard);
|
|
1815
|
+
}
|
|
1816
|
+
.tamga-sheet-end {
|
|
1817
|
+
animation: tamga-sheet-end var(--duration-base) var(--ease-standard);
|
|
1818
|
+
}
|
|
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
|
+
|
|
1794
2023
|
.tamga-label {
|
|
1795
2024
|
font-size: var(--text-caption);
|
|
1796
2025
|
font-weight: 500;
|
|
@@ -2105,6 +2334,24 @@
|
|
|
2105
2334
|
}
|
|
2106
2335
|
}
|
|
2107
2336
|
|
|
2337
|
+
@keyframes tamga-sheet-start {
|
|
2338
|
+
from {
|
|
2339
|
+
transform: translateX(-100%);
|
|
2340
|
+
}
|
|
2341
|
+
to {
|
|
2342
|
+
transform: none;
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
@keyframes tamga-sheet-end {
|
|
2347
|
+
from {
|
|
2348
|
+
transform: translateX(100%);
|
|
2349
|
+
}
|
|
2350
|
+
to {
|
|
2351
|
+
transform: none;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2108
2355
|
@keyframes tamga-bar {
|
|
2109
2356
|
0%,
|
|
2110
2357
|
100% {
|
|
@@ -2185,6 +2432,12 @@
|
|
|
2185
2432
|
.tamga-toast {
|
|
2186
2433
|
animation: none;
|
|
2187
2434
|
}
|
|
2435
|
+
/* Duran bir çekmece yine de AÇIK olmalı: kayma gidiyor, panel yerinde
|
|
2436
|
+
beliriyor. */
|
|
2437
|
+
.tamga-sheet-start,
|
|
2438
|
+
.tamga-sheet-end {
|
|
2439
|
+
animation: none;
|
|
2440
|
+
}
|
|
2188
2441
|
/* a stopped spinner must still be a visible mark, never an empty box */
|
|
2189
2442
|
.tamga-spin-part {
|
|
2190
2443
|
animation: none !important;
|
package/dist/lib/color.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type Oklch = {
|
|
|
18
18
|
h: number;
|
|
19
19
|
};
|
|
20
20
|
/** `#abc` ya da `#aabbcc`. Kısa biçim de sayılıyor, boşluk sayılmıyor. */
|
|
21
|
-
export declare const
|
|
21
|
+
export declare const isHex: (v: string) => boolean;
|
|
22
22
|
/**
|
|
23
23
|
* Hex → RGB, GEÇERSİZ GİRDİDE FIRLATARAK.
|
|
24
24
|
*
|
|
@@ -28,23 +28,23 @@ export declare const gecerliHex: (v: string) => boolean;
|
|
|
28
28
|
* sebebi rengin kendisinde aranıyor.
|
|
29
29
|
*
|
|
30
30
|
* Kit KATI, çağıran toleranslı: yarım yazılmış bir kodun ne anlama geldiği
|
|
31
|
-
* ekranın kararı (bkz. `
|
|
31
|
+
* ekranın kararı (bkz. `isHex`).
|
|
32
32
|
*/
|
|
33
|
-
export declare function
|
|
34
|
-
export declare const
|
|
33
|
+
export declare function hexToRgb(hex: string): [number, number, number];
|
|
34
|
+
export declare const rgbToHex: (r: number, g: number, b: number) => string;
|
|
35
35
|
/** WCAG bağıl parlaklık. Metin kontrastı bununla ölçülüyor. */
|
|
36
|
-
export declare function
|
|
36
|
+
export declare function luminance(hex: string): number;
|
|
37
37
|
/** WCAG kontrast oranı. AA metin eşiği 4.5. */
|
|
38
|
-
export declare function
|
|
38
|
+
export declare function contrast(a: string, b: string): number;
|
|
39
39
|
/**
|
|
40
40
|
* CIE L* · algısal açıklık.
|
|
41
41
|
*
|
|
42
42
|
* İki YÜZEYİN ayrı okunup okunmadığı bununla ölçülüyor; kontrast oranı metin
|
|
43
43
|
* içindir, yüzey için değil. Bir kartın kenarı sayfadan ΔL* ≥ 10 ayrı olmalı.
|
|
44
44
|
*/
|
|
45
|
-
export declare function
|
|
46
|
-
export declare const
|
|
47
|
-
export declare function
|
|
45
|
+
export declare function lightness(hex: string): number;
|
|
46
|
+
export declare const deltaL: (a: string, b: string) => number;
|
|
47
|
+
export declare function hexToOklch(hex: string): Oklch;
|
|
48
48
|
/**
|
|
49
49
|
* OKLCH → hex, GAMUT DIŞINA TAŞMADAN.
|
|
50
50
|
*
|
|
@@ -53,7 +53,7 @@ export declare function hexOklch(hex: string): Oklch;
|
|
|
53
53
|
* doyum (C) ikili aramayla sığana kadar düşürülüyor; ton ve açıklık korunuyor,
|
|
54
54
|
* yalnız canlılık gerektiği kadar azalıyor.
|
|
55
55
|
*/
|
|
56
|
-
export declare function
|
|
56
|
+
export declare function oklchToHex({ l, c, h }: Oklch): string;
|
|
57
57
|
/**
|
|
58
58
|
* Verilen ton ve doyumda, HEDEF ORANI tutturan açıklığı arar.
|
|
59
59
|
*
|
|
@@ -64,5 +64,25 @@ export declare function oklchHex({ l, c, h }: Oklch): string;
|
|
|
64
64
|
* `yon` "koyulaş" (-1) ya da "açıl" (+1): koyu temada aynı hedef ters yönden
|
|
65
65
|
* yakalanıyor.
|
|
66
66
|
*/
|
|
67
|
-
export declare function
|
|
67
|
+
export declare function lightnessForContrast(taban: Oklch, karsi: string, hedef: number, yon: -1 | 1): Oklch;
|
|
68
|
+
/** @deprecated `isHex` kullan. 0.3.0'da kaldırılacak. */
|
|
69
|
+
export declare const gecerliHex: typeof isHex;
|
|
70
|
+
/** @deprecated `hexToRgb` kullan. 0.3.0'da kaldırılacak. */
|
|
71
|
+
export declare const hexRgb: typeof hexToRgb;
|
|
72
|
+
/** @deprecated `rgbToHex` kullan. 0.3.0'da kaldırılacak. */
|
|
73
|
+
export declare const rgbHex: typeof rgbToHex;
|
|
74
|
+
/** @deprecated `luminance` kullan. 0.3.0'da kaldırılacak. */
|
|
75
|
+
export declare const parlaklik: typeof luminance;
|
|
76
|
+
/** @deprecated `contrast` kullan. 0.3.0'da kaldırılacak. */
|
|
77
|
+
export declare const oran: typeof contrast;
|
|
78
|
+
/** @deprecated `lightness` kullan. 0.3.0'da kaldırılacak. */
|
|
79
|
+
export declare const acikligi: typeof lightness;
|
|
80
|
+
/** @deprecated `deltaL` kullan. 0.3.0'da kaldırılacak. */
|
|
81
|
+
export declare const dL: typeof deltaL;
|
|
82
|
+
/** @deprecated `hexToOklch` kullan. 0.3.0'da kaldırılacak. */
|
|
83
|
+
export declare const hexOklch: typeof hexToOklch;
|
|
84
|
+
/** @deprecated `oklchToHex` kullan. 0.3.0'da kaldırılacak. */
|
|
85
|
+
export declare const oklchHex: typeof oklchToHex;
|
|
86
|
+
/** @deprecated `lightnessForContrast` kullan. 0.3.0'da kaldırılacak. */
|
|
87
|
+
export declare const oranaGoreAcikligi: typeof lightnessForContrast;
|
|
68
88
|
//# sourceMappingURL=color.d.ts.map
|
package/dist/lib/color.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAKxD,0EAA0E;AAC1E,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAKxD,0EAA0E;AAC1E,eAAO,MAAM,KAAK,MAAO,MAAM,KAAG,OAA0D,CAAC;AAE7F;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAM9D;AAGD,eAAO,MAAM,QAAQ,MAAO,MAAM,KAAK,MAAM,KAAK,MAAM,WACqB,CAAC;AAE9E,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,+CAA+C;AAC/C,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,eAAO,MAAM,MAAM,MAAO,MAAM,KAAK,MAAM,WAA0C,CAAC;AAItF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAiB7C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,CA6BrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,GACV,KAAK,CAQP;AAKD,yDAAyD;AACzD,eAAO,MAAM,UAAU,cAAQ,CAAC;AAChC,4DAA4D;AAC5D,eAAO,MAAM,MAAM,iBAAW,CAAC;AAC/B,4DAA4D;AAC5D,eAAO,MAAM,MAAM,iBAAW,CAAC;AAC/B,6DAA6D;AAC7D,eAAO,MAAM,SAAS,kBAAY,CAAC;AACnC,4DAA4D;AAC5D,eAAO,MAAM,IAAI,iBAAW,CAAC;AAC7B,6DAA6D;AAC7D,eAAO,MAAM,QAAQ,kBAAY,CAAC;AAClC,0DAA0D;AAC1D,eAAO,MAAM,EAAE,eAAS,CAAC;AACzB,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,mBAAa,CAAC;AACnC,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,mBAAa,CAAC;AACnC,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,6BAAuB,CAAC"}
|
package/dist/lib/color.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
const srgbDoğrusal = (v) => (v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
|
|
16
16
|
const doğrusalSrgb = (v) => (v <= 0.0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055);
|
|
17
17
|
/** `#abc` ya da `#aabbcc`. Kısa biçim de sayılıyor, boşluk sayılmıyor. */
|
|
18
|
-
export const
|
|
18
|
+
export const isHex = (v) => /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v.trim());
|
|
19
19
|
/**
|
|
20
20
|
* Hex → RGB, GEÇERSİZ GİRDİDE FIRLATARAK.
|
|
21
21
|
*
|
|
@@ -25,10 +25,10 @@ export const gecerliHex = (v) => /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v.trim())
|
|
|
25
25
|
* sebebi rengin kendisinde aranıyor.
|
|
26
26
|
*
|
|
27
27
|
* Kit KATI, çağıran toleranslı: yarım yazılmış bir kodun ne anlama geldiği
|
|
28
|
-
* ekranın kararı (bkz. `
|
|
28
|
+
* ekranın kararı (bkz. `isHex`).
|
|
29
29
|
*/
|
|
30
|
-
export function
|
|
31
|
-
if (!
|
|
30
|
+
export function hexToRgb(hex) {
|
|
31
|
+
if (!isHex(hex))
|
|
32
32
|
throw new Error(`Geçersiz hex: ${JSON.stringify(hex)}`);
|
|
33
33
|
const s = hex.trim().replace("#", "");
|
|
34
34
|
const t = s.length === 3 ? [...s].map((c) => c + c).join("") : s;
|
|
@@ -36,15 +36,15 @@ export function hexRgb(hex) {
|
|
|
36
36
|
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
37
37
|
}
|
|
38
38
|
const kanal = (v) => Math.max(0, Math.min(255, Math.round(v * 255)));
|
|
39
|
-
export const
|
|
39
|
+
export const rgbToHex = (r, g, b) => "#" + [r, g, b].map((v) => kanal(v).toString(16).padStart(2, "0")).join("");
|
|
40
40
|
/** WCAG bağıl parlaklık. Metin kontrastı bununla ölçülüyor. */
|
|
41
|
-
export function
|
|
42
|
-
const [r, g, b] =
|
|
41
|
+
export function luminance(hex) {
|
|
42
|
+
const [r, g, b] = hexToRgb(hex).map((v) => srgbDoğrusal(v / 255));
|
|
43
43
|
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
44
44
|
}
|
|
45
45
|
/** WCAG kontrast oranı. AA metin eşiği 4.5. */
|
|
46
|
-
export function
|
|
47
|
-
const [ust, alt] = [
|
|
46
|
+
export function contrast(a, b) {
|
|
47
|
+
const [ust, alt] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
48
48
|
return (ust + 0.05) / (alt + 0.05);
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
@@ -53,14 +53,14 @@ export function oran(a, b) {
|
|
|
53
53
|
* İki YÜZEYİN ayrı okunup okunmadığı bununla ölçülüyor; kontrast oranı metin
|
|
54
54
|
* içindir, yüzey için değil. Bir kartın kenarı sayfadan ΔL* ≥ 10 ayrı olmalı.
|
|
55
55
|
*/
|
|
56
|
-
export function
|
|
57
|
-
const y =
|
|
56
|
+
export function lightness(hex) {
|
|
57
|
+
const y = luminance(hex);
|
|
58
58
|
return y > 216 / 24389 ? 116 * Math.cbrt(y) - 16 : (y * 24389) / 27;
|
|
59
59
|
}
|
|
60
|
-
export const
|
|
60
|
+
export const deltaL = (a, b) => Math.abs(lightness(a) - lightness(b));
|
|
61
61
|
/* ---- OKLab ---- */
|
|
62
|
-
export function
|
|
63
|
-
const [r8, g8, b8] =
|
|
62
|
+
export function hexToOklch(hex) {
|
|
63
|
+
const [r8, g8, b8] = hexToRgb(hex);
|
|
64
64
|
const r = srgbDoğrusal(r8 / 255);
|
|
65
65
|
const g = srgbDoğrusal(g8 / 255);
|
|
66
66
|
const b = srgbDoğrusal(b8 / 255);
|
|
@@ -82,7 +82,7 @@ export function hexOklch(hex) {
|
|
|
82
82
|
* doyum (C) ikili aramayla sığana kadar düşürülüyor; ton ve açıklık korunuyor,
|
|
83
83
|
* yalnız canlılık gerektiği kadar azalıyor.
|
|
84
84
|
*/
|
|
85
|
-
export function
|
|
85
|
+
export function oklchToHex({ l, c, h }) {
|
|
86
86
|
const dene = (kroma) => {
|
|
87
87
|
const hr = (h * Math.PI) / 180;
|
|
88
88
|
const A = Math.cos(hr) * kroma;
|
|
@@ -110,7 +110,7 @@ export function oklchHex({ l, c, h }) {
|
|
|
110
110
|
}
|
|
111
111
|
rgb = dene(alt);
|
|
112
112
|
}
|
|
113
|
-
return
|
|
113
|
+
return rgbToHex(...rgb);
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
116
|
* Verilen ton ve doyumda, HEDEF ORANI tutturan açıklığı arar.
|
|
@@ -122,13 +122,35 @@ export function oklchHex({ l, c, h }) {
|
|
|
122
122
|
* `yon` "koyulaş" (-1) ya da "açıl" (+1): koyu temada aynı hedef ters yönden
|
|
123
123
|
* yakalanıyor.
|
|
124
124
|
*/
|
|
125
|
-
export function
|
|
125
|
+
export function lightnessForContrast(taban, karsi, hedef, yon) {
|
|
126
126
|
let en = { ...taban };
|
|
127
127
|
for (let i = 0; i < 60; i++) {
|
|
128
128
|
const aday = { ...taban, l: Math.max(0.02, Math.min(0.99, taban.l + yon * 0.01 * i)) };
|
|
129
129
|
en = aday;
|
|
130
|
-
if (
|
|
130
|
+
if (contrast(oklchToHex(aday), karsi) >= hedef)
|
|
131
131
|
break;
|
|
132
132
|
}
|
|
133
133
|
return en;
|
|
134
134
|
}
|
|
135
|
+
/* Eski Türkçe adlar, 0.3.0'a kadar. SİLME: 0.1.1 npm'de yayında ve bu adları
|
|
136
|
+
kullanan bir tüketici var. Gerekçe: ADR-0001. */
|
|
137
|
+
/** @deprecated `isHex` kullan. 0.3.0'da kaldırılacak. */
|
|
138
|
+
export const gecerliHex = isHex;
|
|
139
|
+
/** @deprecated `hexToRgb` kullan. 0.3.0'da kaldırılacak. */
|
|
140
|
+
export const hexRgb = hexToRgb;
|
|
141
|
+
/** @deprecated `rgbToHex` kullan. 0.3.0'da kaldırılacak. */
|
|
142
|
+
export const rgbHex = rgbToHex;
|
|
143
|
+
/** @deprecated `luminance` kullan. 0.3.0'da kaldırılacak. */
|
|
144
|
+
export const parlaklik = luminance;
|
|
145
|
+
/** @deprecated `contrast` kullan. 0.3.0'da kaldırılacak. */
|
|
146
|
+
export const oran = contrast;
|
|
147
|
+
/** @deprecated `lightness` kullan. 0.3.0'da kaldırılacak. */
|
|
148
|
+
export const acikligi = lightness;
|
|
149
|
+
/** @deprecated `deltaL` kullan. 0.3.0'da kaldırılacak. */
|
|
150
|
+
export const dL = deltaL;
|
|
151
|
+
/** @deprecated `hexToOklch` kullan. 0.3.0'da kaldırılacak. */
|
|
152
|
+
export const hexOklch = hexToOklch;
|
|
153
|
+
/** @deprecated `oklchToHex` kullan. 0.3.0'da kaldırılacak. */
|
|
154
|
+
export const oklchHex = oklchToHex;
|
|
155
|
+
/** @deprecated `lightnessForContrast` kullan. 0.3.0'da kaldırılacak. */
|
|
156
|
+
export const oranaGoreAcikligi = lightnessForContrast;
|
|
@@ -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
|
+
}
|