aberdeen 1.4.1 → 1.5.0
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/README.md +19 -112
- package/dist/aberdeen.d.ts +23 -36
- package/dist/aberdeen.js +25 -3
- package/dist/aberdeen.js.map +3 -3
- package/dist-min/aberdeen.js +6 -4
- package/dist-min/aberdeen.js.map +3 -3
- package/package.json +1 -1
- package/skill/SKILL.md +13 -8
- package/src/aberdeen.ts +50 -38
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -30,7 +30,7 @@ $('input placeholder="Something containing spaces" value=', userInput);
|
|
|
30
30
|
$('button text=', `Count: ${state.count}`);
|
|
31
31
|
|
|
32
32
|
// Event handlers
|
|
33
|
-
$('button
|
|
33
|
+
$('button text=Click click=', () => console.log('clicked'));
|
|
34
34
|
|
|
35
35
|
// Nested content via function (creates reactive scope)
|
|
36
36
|
$('ul', () => {
|
|
@@ -78,18 +78,23 @@ $('button', { click: handler, '.active': isActive });
|
|
|
78
78
|
| `r` | borderRadius | | |
|
|
79
79
|
|
|
80
80
|
### CSS Variables (`@`)
|
|
81
|
-
Values starting with `@`
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
| `@
|
|
81
|
+
Values starting with `@` expand to native CSS custom properties via `var(--name)`. Numeric keys are prefixed with `m` (e.g., `@3` → `var(--m3)`).
|
|
82
|
+
|
|
83
|
+
Predefined spacing scale:
|
|
84
|
+
| Var | CSS Output | Value |
|
|
85
|
+
|-----|------------|-------|
|
|
86
|
+
| `@1` | `var(--m1)` | 0.25rem |
|
|
87
|
+
| `@2` | `var(--m2)` | 0.5rem |
|
|
88
|
+
| `@3` | `var(--m3)` | 1rem |
|
|
89
|
+
| `@4` | `var(--m4)` | 2rem |
|
|
90
|
+
| `@5` | `var(--m5)` | 4rem |
|
|
91
|
+
| `@n` | `var(--mn)` | 2^(n-3) rem |
|
|
87
92
|
|
|
88
93
|
**Best practice:** Use `@3` and `@4` for most margins/paddings. For new projects, define color variables:
|
|
89
94
|
```typescript
|
|
90
95
|
cssVars.primary = '#3b82f6';
|
|
91
96
|
cssVars.danger = '#ef4444';
|
|
92
|
-
$('button bg:@primary fg:white#Save');
|
|
97
|
+
$('button bg:@primary fg:white#Save'); // outputs: background: var(--primary); color: white
|
|
93
98
|
```
|
|
94
99
|
|
|
95
100
|
## Reactive State: `proxy()`
|
package/src/aberdeen.ts
CHANGED
|
@@ -53,7 +53,7 @@ function queue(runner: QueueRunner) {
|
|
|
53
53
|
* ```typescript
|
|
54
54
|
* const data = proxy("before");
|
|
55
55
|
*
|
|
56
|
-
* $(
|
|
56
|
+
* $('#'+data);
|
|
57
57
|
* console.log(1, document.body.innerHTML); // before
|
|
58
58
|
*
|
|
59
59
|
* // Make an update that should cause the DOM to change.
|
|
@@ -940,9 +940,9 @@ const EMPTY = Symbol("empty");
|
|
|
940
940
|
* // Reactively display a message if the items array is empty
|
|
941
941
|
* $('div', () => {
|
|
942
942
|
* if (isEmpty(items)) {
|
|
943
|
-
* $('p
|
|
943
|
+
* $('p i#No items yet!');
|
|
944
944
|
* } else {
|
|
945
|
-
*
|
|
945
|
+
* onEach(items, item => $('p#'+item));
|
|
946
946
|
* }
|
|
947
947
|
* });
|
|
948
948
|
*
|
|
@@ -995,7 +995,7 @@ export interface ValueRef<T> {
|
|
|
995
995
|
* const cnt = count(items);
|
|
996
996
|
*
|
|
997
997
|
* // Create a DOM text node for the count:
|
|
998
|
-
* $('div',
|
|
998
|
+
* $('div text=', cnt);
|
|
999
999
|
* // <div>2</div>
|
|
1000
1000
|
|
|
1001
1001
|
* // Or we can use it in an {@link derive} function:
|
|
@@ -1704,17 +1704,20 @@ export const NO_COPY = Symbol("NO_COPY");
|
|
|
1704
1704
|
(Promise.prototype as any)[NO_COPY] = true;
|
|
1705
1705
|
|
|
1706
1706
|
/**
|
|
1707
|
-
* CSS
|
|
1707
|
+
* CSS variables that are output as native CSS custom properties.
|
|
1708
1708
|
*
|
|
1709
|
-
* When a CSS value starts with `@`,
|
|
1709
|
+
* When a CSS value starts with `@`, it becomes `var(--name)` (or `var(--mN)` for numeric keys).
|
|
1710
1710
|
* Pre-initialized with keys '1'-'12' mapping to an exponential rem scale (e.g., @1=0.25rem, @3=1rem).
|
|
1711
1711
|
*
|
|
1712
|
+
* Changes to cssVars are automatically reflected in a `<style>` tag in `<head>`, making updates
|
|
1713
|
+
* reactive across all elements using those variables.
|
|
1714
|
+
*
|
|
1712
1715
|
* @example
|
|
1713
1716
|
* ```typescript
|
|
1714
1717
|
* cssVars.primary = '#3b82f6';
|
|
1715
1718
|
* cssVars[3] = '16px'; // Override @3 to be 16px instead of 1rem
|
|
1716
|
-
* $('p color:@primary'); // Sets color to
|
|
1717
|
-
* $('div mt:@3'); // Sets margin-top to
|
|
1719
|
+
* $('p color:@primary'); // Sets color to var(--primary)
|
|
1720
|
+
* $('div mt:@3'); // Sets margin-top to var(--m3)
|
|
1718
1721
|
* ```
|
|
1719
1722
|
*/
|
|
1720
1723
|
export const cssVars: Record<string, string> = optProxy({});
|
|
@@ -1723,6 +1726,13 @@ for (let i = 1; i <= 12; i++) {
|
|
|
1723
1726
|
cssVars[i] = 2 ** (i - 3) + "rem";
|
|
1724
1727
|
}
|
|
1725
1728
|
|
|
1729
|
+
const DIGIT_FIRST = /^\d/;
|
|
1730
|
+
function cssVarRef(name: string): string {
|
|
1731
|
+
// Prefix numeric keys with 'm' (CSS custom property names can't start with a digit)
|
|
1732
|
+
const varName = DIGIT_FIRST.test(name) ? `m${name}` : name;
|
|
1733
|
+
return `var(--${varName})`;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1726
1736
|
/**
|
|
1727
1737
|
* Clone an (optionally proxied) object or array.
|
|
1728
1738
|
*
|
|
@@ -1782,17 +1792,10 @@ const refHandler: ProxyHandler<RefTarget> = {
|
|
|
1782
1792
|
* const formData = proxy({ color: 'orange', velocity: 42 });
|
|
1783
1793
|
*
|
|
1784
1794
|
* // Usage with `bind`
|
|
1785
|
-
* $('input',
|
|
1786
|
-
* type: 'text',
|
|
1787
|
-
* // Creates a two-way binding between the input's value and formData.username
|
|
1788
|
-
* bind: ref(formData, 'color')
|
|
1789
|
-
* });
|
|
1795
|
+
* $('input type=text bind=', ref(formData, 'color'));
|
|
1790
1796
|
*
|
|
1791
1797
|
* // Usage as a dynamic property, causes a TextNode with just the name to be created and live-updated
|
|
1792
|
-
* $('p
|
|
1793
|
-
* text: ref(formData, 'color'),
|
|
1794
|
-
* $color: ref(formData, 'color')
|
|
1795
|
-
* });
|
|
1798
|
+
* $('p text="Selected color: " text=', ref(formData, 'color'), 'color:', ref(formData, 'color'));
|
|
1796
1799
|
*
|
|
1797
1800
|
* // Changes are actually stored in formData - this causes logs like `{color: "Blue", velocity 42}`
|
|
1798
1801
|
* $(() => console.log(formData))
|
|
@@ -1926,16 +1929,7 @@ const SPECIAL_PROPS: { [key: string]: (el: Element, value: any) => void } = {
|
|
|
1926
1929
|
*
|
|
1927
1930
|
* @example Create Element
|
|
1928
1931
|
* ```typescript
|
|
1929
|
-
* $('button.secondary.outline
|
|
1930
|
-
* disabled: false,
|
|
1931
|
-
* click: () => console.log('Clicked!'),
|
|
1932
|
-
* $color: 'red'
|
|
1933
|
-
* });
|
|
1934
|
-
* ```
|
|
1935
|
-
*
|
|
1936
|
-
* Which can also be written as:
|
|
1937
|
-
* ```typescript
|
|
1938
|
-
* $('button.secondary.outline text=Submit $color=red disabled=', false, 'click=', () => console.log('Clicked!'));
|
|
1932
|
+
* $('button.secondary.outline text=Submit color:red disabled=', false, 'click=', () => console.log('Clicked!'));
|
|
1939
1933
|
* ```
|
|
1940
1934
|
*
|
|
1941
1935
|
* We want to set `disabled` as a property instead of an attribute, so we must use the `key=` syntax in order to provide
|
|
@@ -1943,7 +1937,7 @@ const SPECIAL_PROPS: { [key: string]: (el: Element, value: any) => void } = {
|
|
|
1943
1937
|
*
|
|
1944
1938
|
* @example Create Nested Elements
|
|
1945
1939
|
* ```typescript
|
|
1946
|
-
* let inputElement: Element = $('label
|
|
1940
|
+
* let inputElement: Element = $('label text="Click me" input type=checkbox');
|
|
1947
1941
|
* // You should usually not touch raw DOM elements, unless when integrating
|
|
1948
1942
|
* // with non-Aberdeen code.
|
|
1949
1943
|
* console.log('DOM element:', inputElement);
|
|
@@ -1955,14 +1949,14 @@ const SPECIAL_PROPS: { [key: string]: (el: Element, value: any) => void } = {
|
|
|
1955
1949
|
* $('div', () => { // Outer element
|
|
1956
1950
|
* // This scope re-renders when state.count changes
|
|
1957
1951
|
* $(`p#Count is ${state.count}`);
|
|
1958
|
-
* $('button
|
|
1952
|
+
* $('button text=Increment click=', () => state.count++);
|
|
1959
1953
|
* });
|
|
1960
1954
|
* ```
|
|
1961
1955
|
*
|
|
1962
1956
|
* @example Two-way Binding
|
|
1963
1957
|
* ```typescript
|
|
1964
1958
|
* const user = proxy({ name: '' });
|
|
1965
|
-
* $('input
|
|
1959
|
+
* $('input placeholder=Name bind=', ref(user, 'name'));
|
|
1966
1960
|
* $('h3', () => { // Reactive scope
|
|
1967
1961
|
* $(`#Hello ${user.name || 'stranger'}`);
|
|
1968
1962
|
* });
|
|
@@ -1971,7 +1965,7 @@ const SPECIAL_PROPS: { [key: string]: (el: Element, value: any) => void } = {
|
|
|
1971
1965
|
* @example Conditional Rendering
|
|
1972
1966
|
* ```typescript
|
|
1973
1967
|
* const show = proxy(false);
|
|
1974
|
-
* $('button',
|
|
1968
|
+
* $('button click=', () => show.value = !show.value, () => $(show.value ? '#Hide' : '#Show'));
|
|
1975
1969
|
* $(() => { // Reactive scope
|
|
1976
1970
|
* if (show.value) {
|
|
1977
1971
|
* $('p#Details are visible!');
|
|
@@ -2095,7 +2089,7 @@ let cssCount = 0;
|
|
|
2095
2089
|
* - In case a selector contains a `&`, that character will be replaced by the parent selector.
|
|
2096
2090
|
* - Selectors will be split on `,` characters, each combining with the parent selector with *or* semantics.
|
|
2097
2091
|
* - Selector starting with `'@'` define at-rules like media queries. They may be nested within regular selectors.
|
|
2098
|
-
* @param global -
|
|
2092
|
+
* @param global - Deprecated! Use {@link insertGlobalCss} instead.
|
|
2099
2093
|
* @returns The unique class name prefix used for scoping (e.g., `.AbdStl1`). Use this
|
|
2100
2094
|
* prefix with {@link $} to apply the styles.
|
|
2101
2095
|
*
|
|
@@ -2194,7 +2188,7 @@ function styleToCss(style: object, prefix: string): string {
|
|
|
2194
2188
|
);
|
|
2195
2189
|
}
|
|
2196
2190
|
} else {
|
|
2197
|
-
const val = v == null || v === false ? "" : typeof v === 'string' ? (v[0] === '@' ? (
|
|
2191
|
+
const val = v == null || v === false ? "" : typeof v === 'string' ? (v[0] === '@' ? cssVarRef(v.substring(1)) : v) : String(v);
|
|
2198
2192
|
const expanded = CSS_SHORT[k] || k;
|
|
2199
2193
|
for (const prop of (Array.isArray(expanded) ? expanded : [expanded])) {
|
|
2200
2194
|
props += `${prop.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}:${val};`;
|
|
@@ -2223,7 +2217,7 @@ function applyArg(el: Element, key: string, value: any) {
|
|
|
2223
2217
|
} else if (key[0] === "$") {
|
|
2224
2218
|
// Style (with shortcuts)
|
|
2225
2219
|
key = key.substring(1);
|
|
2226
|
-
const val = value == null || value === false ? "" : typeof value === 'string' ? (value[0] === '@' ? (
|
|
2220
|
+
const val = value == null || value === false ? "" : typeof value === 'string' ? (value[0] === '@' ? cssVarRef(value.substring(1)) : value) : String(value);
|
|
2227
2221
|
const expanded = CSS_SHORT[key] || key;
|
|
2228
2222
|
if (typeof expanded === "string") {
|
|
2229
2223
|
(el as any).style[expanded] = val;
|
|
@@ -2374,7 +2368,7 @@ export function getParentElement(): Element {
|
|
|
2374
2368
|
* })
|
|
2375
2369
|
*
|
|
2376
2370
|
* // Show the sum
|
|
2377
|
-
* $('h1',
|
|
2371
|
+
* $('h1 text=', sum);
|
|
2378
2372
|
*
|
|
2379
2373
|
* // Make random changes to the array
|
|
2380
2374
|
* const rnd = () => 0|(Math.random()*20);
|
|
@@ -2412,8 +2406,8 @@ export function clean(cleaner: () => void) {
|
|
|
2412
2406
|
* // When data.notifications changes, only this inner scope reruns,
|
|
2413
2407
|
* // leaving the `<p>Welcome, ..</p>` untouched.
|
|
2414
2408
|
* console.log('Notifications');
|
|
2415
|
-
* $('code.notification-badge
|
|
2416
|
-
* $('a
|
|
2409
|
+
* $('code.notification-badge text=', data.notifications);
|
|
2410
|
+
* $('a text=Notify! click=', () => data.notifications++);
|
|
2417
2411
|
* });
|
|
2418
2412
|
* });
|
|
2419
2413
|
* ```
|
|
@@ -2522,7 +2516,7 @@ export function unmountAll() {
|
|
|
2522
2516
|
*
|
|
2523
2517
|
*/
|
|
2524
2518
|
|
|
2525
|
-
export function peek<T extends object>(target: T, key:
|
|
2519
|
+
export function peek<T extends object, K extends keyof T>(target: T, key: K): T[K];
|
|
2526
2520
|
export function peek<K,V>(target: Map<K,V>, key: K): V | undefined;
|
|
2527
2521
|
export function peek<T>(target: T[], key: number): T | undefined;
|
|
2528
2522
|
export function peek<T>(target: () => T): T;
|
|
@@ -2919,3 +2913,21 @@ export function withEmitHandler(
|
|
|
2919
2913
|
emit = oldEmitHandler;
|
|
2920
2914
|
}
|
|
2921
2915
|
}
|
|
2916
|
+
|
|
2917
|
+
// Initialize the cssVars style tag in document.head
|
|
2918
|
+
// This runs at module load time, after all functions are defined
|
|
2919
|
+
if (typeof document !== "undefined") {
|
|
2920
|
+
leakScope(() => {
|
|
2921
|
+
mount(document.head, () => {
|
|
2922
|
+
$('style', () => {
|
|
2923
|
+
let css = ":root {\n";
|
|
2924
|
+
for(const [key, value] of Object.entries(cssVars)) {
|
|
2925
|
+
const varName = DIGIT_FIRST.test(String(key)) ? `m${key}` : key;
|
|
2926
|
+
css += ` --${varName}: ${value};\n`;
|
|
2927
|
+
}
|
|
2928
|
+
css += "}";
|
|
2929
|
+
$(`#${css}`);
|
|
2930
|
+
})
|
|
2931
|
+
});
|
|
2932
|
+
});
|
|
2933
|
+
}
|