hlmnhkl-ui 0.35.0 → 0.35.2
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
CHANGED
|
@@ -9,6 +9,10 @@ React components, measured down to the part you never see: the 44px tap area, th
|
|
|
9
9
|
|
|
10
10
|
**[Docs](https://ui.hlmnhkl.my)** · [Installation](https://ui.hlmnhkl.my/installation) · [Components](https://ui.hlmnhkl.my/button) · [What's new](https://ui.hlmnhkl.my/changelog)
|
|
11
11
|
|
|
12
|
+
<img alt="Four hlmnhkl/ui controls in use: a Dial turned to a new oven temperature, a RulerScrubber dragged and landing on a mark, a SwipeDeck card thrown right and the next thrown left, and a SplitFlap board flipping to new times." src="https://raw.githubusercontent.com/hilmanhaikal/hlmnhkl-ui/main/.github/assets/signature.gif" width="600">
|
|
13
|
+
|
|
14
|
+
Controls that feel like real ones: Dial, RulerScrubber, SwipeDeck, SplitFlap and more. Plus the everyday parts every app needs: buttons, inputs, sheets, tables, menus and 60 more.
|
|
15
|
+
|
|
12
16
|
## Install
|
|
13
17
|
|
|
14
18
|
From your app's folder, set it up once, then copy the components you need:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hlmnhkl-ui",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.2",
|
|
4
4
|
"description": "React components in black, white and grey with one blue accent. Copy them into your app with npx hlmnhkl-ui add <name>.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://ui.hlmnhkl.my",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
],
|
|
26
26
|
"scripts": {
|
|
27
27
|
"typecheck": "tsc -p tsconfig.json",
|
|
28
|
-
"add": "node scripts/add.js"
|
|
28
|
+
"add": "node scripts/add.js",
|
|
29
|
+
"prepublishOnly": "node scripts/check-publish.mjs"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/react": "^19.3.0",
|
package/scripts/add.js
CHANGED
|
@@ -41,6 +41,17 @@ const KINDS = [
|
|
|
41
41
|
{ key: 'theme', plural: 'Themes', folder: 'themes', pro: true, single: true },
|
|
42
42
|
].map((k) => ({ ...k, dir: join(SRC, k.folder) }))
|
|
43
43
|
|
|
44
|
+
// The import lines as they read from the app's entry file. A folder inside src/ is written
|
|
45
|
+
// relative to src/ (Vite, Next and most setups keep the entry there), so it can be pasted as is.
|
|
46
|
+
function fromEntry(folder, both = true) {
|
|
47
|
+
const clean = folder.replace(/^\.\//, '').replace(/\/$/, '')
|
|
48
|
+
const inSrc = clean.startsWith('src/')
|
|
49
|
+
const rel = inSrc ? clean.slice(4) : clean
|
|
50
|
+
const where = inSrc ? 'in your entry file inside src/ (for example src/main.tsx):' : 'in a file at your project root:'
|
|
51
|
+
const lines = [`import './${rel}/tokens.css'`, ...(both ? [`import './${rel}/base.css'`] : [])]
|
|
52
|
+
return `${where}\n${lines.map((l) => ` ${l}`).join('\n')}`
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
function fail(message, usage = true) {
|
|
45
56
|
console.error(`Error: ${message}`)
|
|
46
57
|
if (usage) console.error(`\n${USAGE}`)
|
|
@@ -352,7 +363,7 @@ function runInit(opts) {
|
|
|
352
363
|
if (!opts.dryRun) writeFileSync(config, `${JSON.stringify({ to: folder }, null, 2)}\n`)
|
|
353
364
|
console.log(` ${CONFIG}: added, so later commands put files in ${folder}`)
|
|
354
365
|
}
|
|
355
|
-
console.log(`\nNext:\n 1. Import both once
|
|
366
|
+
console.log(`\nNext:\n 1. Import both once, tokens first, ${fromEntry(folder)}`)
|
|
356
367
|
console.log(` 2. Add your first component: npx hlmnhkl-ui add button`)
|
|
357
368
|
console.log(` See everything there is: npx hlmnhkl-ui list, or ${SITE}`)
|
|
358
369
|
}
|
|
@@ -417,7 +428,7 @@ function runAdd(opts) {
|
|
|
417
428
|
for (const s of skipped) console.log(` ${s}`)
|
|
418
429
|
if (skipped.some((s) => /^(tokens|base)\.css /.test(s))) console.log(' tokens.css and base.css are never replaced, so your theme edits stay.')
|
|
419
430
|
}
|
|
420
|
-
if (tokensNew) console.log(`Next: import ${
|
|
431
|
+
if (tokensNew) console.log(`Next: import tokens.css once ${fromEntry(opts.to || readConfig()?.to || DEFAULT_TO, false)} (or run init).`)
|
|
421
432
|
if (items.some((i) => i.kind === 'theme')) console.log('Themes: import each one after tokens.css, then set data-theme-brand or data-density on <html>.')
|
|
422
433
|
}
|
|
423
434
|
|
|
@@ -147,8 +147,16 @@
|
|
|
147
147
|
margin-inline: auto;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/* Registered so the fill can ease back after a throw instead of snapping off in one frame. */
|
|
151
|
+
@property --fill {
|
|
152
|
+
syntax: '<number>';
|
|
153
|
+
inherits: true;
|
|
154
|
+
initial-value: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
.choice {
|
|
151
158
|
--fill: 0;
|
|
159
|
+
transition: --fill var(--hl-motion-base) var(--hl-ease-out);
|
|
152
160
|
position: relative;
|
|
153
161
|
display: grid;
|
|
154
162
|
min-block-size: var(--hl-control-h-lg);
|
|
@@ -172,6 +180,10 @@
|
|
|
172
180
|
white-space: nowrap;
|
|
173
181
|
}
|
|
174
182
|
|
|
183
|
+
.root:has(.deck[data-dragging]) .choice {
|
|
184
|
+
transition: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
175
187
|
/* The fill pours in from the button's inner edge toward the side the card goes, and the word under it turns to its ink. */
|
|
176
188
|
.fill {
|
|
177
189
|
position: absolute;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useEffect, useLayoutEffect, useRef, useState, type ComponentPropsWithRef, type KeyboardEvent, type PointerEvent, type ReactNode } from 'react'
|
|
4
|
+
import { flushSync } from 'react-dom'
|
|
4
5
|
import { cx } from '../../internal/cx'
|
|
5
6
|
import { clamp, magnetTo, pull, speedTracker, springTo } from '../../internal/feel'
|
|
6
7
|
import { UndoIcon } from '../../internal/icons'
|
|
@@ -133,8 +134,10 @@ export function SwipeDeck<T>({
|
|
|
133
134
|
|
|
134
135
|
const finish = (side: Side) => {
|
|
135
136
|
stop.current = () => {}
|
|
137
|
+
// The next card must be in place before the top card's transform resets, or the thrown card
|
|
138
|
+
// shows in the middle for a frame.
|
|
139
|
+
flushSync(() => commit(side))
|
|
136
140
|
paint(0)
|
|
137
|
-
commit(side)
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
const fly = (side: Side, velocity: number) => {
|