Skip to content

Commit

Permalink
feat: expose *ref props to access underlying elements (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte authored Dec 11, 2024
1 parent a9cce37 commit 7caef35
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 114 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-numbers-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"barqode": patch
---

expose `*ref` props for access to the underlying elements
39 changes: 11 additions & 28 deletions docs/src/lib/components/demos/barqode-dropzone.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { BarqodeDropzone, type DetectedBarcode } from "barqode";
import { DemoContainer } from "@svecodocs/kit";
import { cn, DemoContainer } from "@svecodocs/kit";
let result = $state("");
let dragover = $state(false);
Expand All @@ -14,40 +14,23 @@
}
</script>

<DemoContainer>
<div class="title">Detecting QR-codes</div>
<DemoContainer class="flex flex-col gap-5">
<div>Detecting QR-codes</div>

<div class:dragover style="width: 100%; aspect-ratio: 4/3; border: 2px solid #2563eb;">
<div
class={cn(
"border-foreground/15 aspect-[4/3] w-full rounded-lg border-[2px] border-dashed",
dragover && "border-brand"
)}
>
<BarqodeDropzone {onDetect} {onDragover}>
<div class="instructions">
<div class="flex h-full w-full items-center justify-center">
<p>Click to upload or drop an image here</p>
</div>
</BarqodeDropzone>
</div>

<div class="result">
<div>
Last detected: {result}
</div>
</DemoContainer>

<style>
.title {
margin-bottom: 1.25rem;
}
.dragover {
border-color: white;
}
.instructions {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.result {
margin-top: 1.25rem;
}
</style>
35 changes: 9 additions & 26 deletions docs/src/lib/components/demos/barqode-stream.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script lang="ts">
import { BarqodeStream, type DetectedBarcode } from "barqode";
import { DemoContainer } from "@svecodocs/kit";
import SpinnerBall from "phosphor-svelte/lib/SpinnerBall";
let loading = $state(true);
let result: string | null = $state(null);
let result = $state<string | null>(null);
function onCameraOn() {
loading = false;
Expand Down Expand Up @@ -33,38 +34,20 @@
}
</script>

<DemoContainer>
<div class="title">Detecting QR-codes</div>
<DemoContainer class="flex flex-col gap-5">
<div>Detecting QR-codes</div>

<div style="width: 100%; aspect-ratio: 4/3;">
<div class="aspect-[4/3] w-full">
<BarqodeStream {onCameraOn} {onDetect} {track}>
{#if loading}
<div class="loading-indicator">Loading...</div>
<div class="flex h-full w-full items-center justify-center text-4xl font-semibold">
<SpinnerBall class="size-20 animate-spin " />
</div>
{/if}
</BarqodeStream>
</div>

<div class="result">
<div>
Last detected: {result}
</div>
</DemoContainer>

<style>
.title {
margin-bottom: 1.25rem;
}
.loading-indicator {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
font-size: 2rem;
}
.result {
margin-top: 1.25rem;
}
</style>
54 changes: 23 additions & 31 deletions docs/src/lib/components/demos/full-demo.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { BarqodeStream, type BarcodeFormat, type DetectedBarcode } from "barqode";
import { DemoContainer } from "@svecodocs/kit";
import { DemoContainer, NativeSelect, Label, Checkbox } from "@svecodocs/kit";
let result = $state("");
let error = $state("");
Expand Down Expand Up @@ -36,7 +36,7 @@
});
// computed value for selected formats
let selectedBarcodeFormats: BarcodeFormat[] = $derived(
const selectedBarcodeFormats: BarcodeFormat[] = $derived(
Object.keys(barcodeFormats).filter(
// @ts-expect-error fix this later :)
(format: string) => barcodeFormats[format]
Expand Down Expand Up @@ -183,10 +183,10 @@
}
</script>

<DemoContainer>
<div class="flex flex-col gap-1">
<label for="camera-contraints">Camera constraints:</label>
<select
<DemoContainer class="flex flex-col gap-6">
<div class="flex flex-col gap-2.5">
<Label for="camera-contraints">Camera constraints:</Label>
<NativeSelect
id="camera-contraints"
class="bg-accent rounded-sm"
bind:value={selectedConstraints}
Expand All @@ -196,40 +196,39 @@
{option.label}
</option>
{/each}
</select>
</NativeSelect>
</div>

<div class="mt-4 flex flex-col gap-1">
<label for="track-function">Track function:</label>
<select id="track-function" class="bg-accent rounded-sm" bind:value={trackFunctionSelected}>
<div class="flex flex-col gap-2.5">
<Label for="track-function">Track function:</Label>
<NativeSelect
id="track-function"
class="bg-accent rounded-sm"
bind:value={trackFunctionSelected}
>
{#each trackFunctionOptions as option}
<option value={option}>
{option.text}
</option>
{/each}
</select>
</NativeSelect>
</div>

<!-- svelte-ignore a11y_label_has_associated_control -->
<div class="mt-4 flex flex-col gap-1">
<label>Barcode formats:</label>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<fieldset class="flex flex-col gap-1">
<legend class="mb-2.5 select-none">Barcode formats:</legend>
<div class="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3">
{#each Object.keys(barcodeFormats) as option}
{@const barcodeOption = option as BarcodeFormat}
<div class="flex gap-2">
<input
type="checkbox"
id={option}
bind:checked={barcodeFormats[barcodeOption]}
/>
<label for={option}>{option}</label>
<div class="flex items-center">
<Checkbox id={option} bind:checked={barcodeFormats[barcodeOption]} />
<Label for={option} class="pl-2">{option}</Label>
</div>
{/each}
</div>
</div>
</fieldset>

{#if error}
<div class="error">{error}</div>
<div class="text-destructive font-semibold">{error}</div>
{/if}

<div class="my-4" style="width: 100%; aspect-ratio: 4/3;">
Expand All @@ -247,10 +246,3 @@
Last detected: {result}
</div>
</DemoContainer>

<style>
.error {
font-weight: bold;
color: red;
}
</style>
9 changes: 5 additions & 4 deletions packages/barqode/src/lib/components/barqode-dropzone.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
onDragover,
onError,
children,
inputRef = $bindable(null),
wrapperRef = $bindable(null),
...restProps
}: DropzoneProps = $props();
let input: HTMLInputElement = $state()!;
async function onDetectFile(promise: Promise<DetectedBarcode[]>) {
try {
const detectedCodes = await promise;
Expand Down Expand Up @@ -76,7 +76,7 @@
}
function onClick() {
input.click();
inputRef?.click();
}
</script>

Expand All @@ -91,6 +91,7 @@
ondragover={handleDragOver}
onclick={onClick}
onkeydown={onClick}
bind:this={wrapperRef}
>
<input
class="hidden"
Expand All @@ -101,7 +102,7 @@
capture="environment"
multiple
{...restProps}
bind:this={input}
bind:this={inputRef}
/>

{@render children?.()}
Expand Down
55 changes: 32 additions & 23 deletions packages/barqode/src/lib/components/barqode-stream.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
onCameraOff,
onDetect,
children,
videoRef = $bindable(null),
pauseRef = $bindable(null),
trackingRef = $bindable(null),
wrapperRef = $bindable(null),
}: StreamProps = $props();
// state
let video: HTMLVideoElement = $state()!;
let pauseFrame: HTMLCanvasElement = $state()!;
let trackingLayer: HTMLCanvasElement = $state()!;
let cameraActive = $state(false);
let isMounted = $state(false);
Expand All @@ -37,12 +38,13 @@
() => cameraSettings,
() => {
const settings = cameraSettings;
const ctx = pauseFrame.getContext("2d")!;
const ctx = pauseRef!.getContext("2d")!;
if (!settings.shouldStream) {
pauseFrame.width = video.videoWidth;
pauseFrame.height = video.videoHeight;
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
pauseRef!.width = videoRef!.videoWidth;
pauseRef!.height = videoRef!.videoHeight;
ctx.drawImage(videoRef!, 0, 0, videoRef!.videoWidth, videoRef!.videoHeight);
cameraController.stop();
cameraActive = false;
Expand All @@ -55,7 +57,7 @@
try {
cameraController
.start(video, settings)
.start(videoRef!, settings)
.then((capabilities) => {
if (isMounted) {
cameraActive = true;
Expand Down Expand Up @@ -88,12 +90,12 @@
() => shouldScan,
() => {
if (shouldScan) {
clearCanvas(pauseFrame);
clearCanvas(trackingLayer);
clearCanvas(pauseRef!);
clearCanvas(trackingRef!);
const scanInterval = track === undefined ? 500 : 40;
keepScanning(video, {
keepScanning(videoRef!, {
detectHandler: (detectedCodes: DetectedBarcode[]) => onDetect?.(detectedCodes),
formats: formats,
locateHandler: onLocate,
Expand Down Expand Up @@ -124,12 +126,12 @@
*/
function onLocate(detectedCodes: DetectedBarcode[]) {
if (detectedCodes.length === 0 || track === undefined) {
clearCanvas(trackingLayer);
clearCanvas(trackingRef!);
} else {
const displayWidth = video.offsetWidth;
const displayHeight = video.offsetHeight;
const resolutionWidth = video.videoWidth;
const resolutionHeight = video.videoHeight;
const displayWidth = videoRef!.offsetWidth;
const displayHeight = videoRef!.offsetHeight;
const resolutionWidth = videoRef!.videoWidth;
const resolutionHeight = videoRef!.videoHeight;
const largerRatio = Math.max(
displayWidth / resolutionWidth,
Expand Down Expand Up @@ -173,9 +175,9 @@
};
});
trackingLayer.width = video.offsetWidth;
trackingLayer.height = video.offsetHeight;
const ctx = trackingLayer.getContext("2d") as CanvasRenderingContext2D;
trackingRef!.width = videoRef!.offsetWidth;
trackingRef!.height = videoRef!.offsetHeight;
const ctx = trackingRef!.getContext("2d") as CanvasRenderingContext2D;
track?.(adjustedCodes, ctx);
}
}
Expand All @@ -189,19 +191,26 @@
});
</script>

<div class="wrapper">
<video bind:this={video} class="camera" class:hidden={!shouldScan} autoplay muted playsinline>
<div class="wrapper" bind:this={wrapperRef}>
<video
bind:this={videoRef}
class="camera"
class:hidden={!shouldScan}
autoplay
muted
playsinline
>
</video>

<canvas
id="qrcode-stream-pause-frame"
bind:this={pauseFrame}
bind:this={pauseRef}
class="camera"
class:hidden={shouldScan}
>
</canvas>

<canvas id="qrcode-stream-tracking-layer" bind:this={trackingLayer} class="overlay"></canvas>
<canvas id="qrcode-stream-tracking-layer" bind:this={trackingRef} class="overlay"></canvas>

<div class="overlay">
{@render children?.()}
Expand Down
Loading

0 comments on commit 7caef35

Please sign in to comment.