14 | );
15 | }
16 | }
17 |
18 | fritz.define('some-thing', MyThing);
--------------------------------------------------------------------------------
/core/test/typescript/jsx-runtime/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "jsxImportSource": "fritz"
5 | },
6 | "extends": "../../../tsconfig.json",
7 | "include": ["."]
8 | }
--------------------------------------------------------------------------------
/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "moduleResolution": "node",
5 | "target": "ESNext",
6 | "strict": true,
7 | "declaration": true,
8 | "noEmit": true,
9 | "skipLibCheck": true
10 | },
11 | "files": ["src/window/mods.d.ts"],
12 | "include": ["src"]
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fritz-repo",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "A library for rendering custom elements in a web worker.",
6 | "scripts": {
7 | "build": "wireit",
8 | "build:site": "wireit",
9 | "check": "wireit",
10 | "changeset": "changeset"
11 | },
12 | "wireit": {
13 | "build": {
14 | "dependencies": [
15 | "./astro-fritz:build",
16 | "./core:build"
17 | ]
18 | },
19 | "check": {
20 | "dependencies": [
21 | "./astro-fritz:check",
22 | "./core:check"
23 | ]
24 | },
25 | "build:site": {
26 | "dependencies": [
27 | "./site:build"
28 | ]
29 | }
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "git+https://github.com/matthewp/fritz.git"
34 | },
35 | "keywords": [],
36 | "author": "",
37 | "license": "Unlicensed",
38 | "bugs": {
39 | "url": "https://github.com/matthewp/fritz/issues"
40 | },
41 | "homepage": "https://github.com/matthewp/fritz#readme",
42 | "workspaces": [
43 | "core",
44 | "astro-fritz",
45 | "site"
46 | ],
47 | "devDependencies": {
48 | "@changesets/cli": "^2.26.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/site/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | .astro/
--------------------------------------------------------------------------------
/site/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # fritz-site
2 |
3 | ## 1.0.1-alpha.0
4 |
5 | ### Patch Changes
6 |
7 | - Updated dependencies [d1aa720]
8 | - astro-fritz@2.0.0-alpha.0
9 |
--------------------------------------------------------------------------------
/site/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'astro/config';
2 | import fritz from 'astro-fritz';
3 | import rehypeHighlight from 'rehype-highlight';
4 |
5 | export default defineConfig({
6 | integrations: [
7 | fritz()
8 | ],
9 | markdown: {
10 | syntaxHighlight: false,
11 | rehypePlugins: [rehypeHighlight]
12 | }
13 | });
--------------------------------------------------------------------------------
/site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fritz-site",
3 | "version": "1.0.1-alpha.0",
4 | "description": "Fritz site and documentation",
5 | "main": "dist/index.html",
6 | "private": true,
7 | "scripts": {
8 | "dev": "wireit",
9 | "build": "wireit",
10 | "preview": "wireit",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "wireit": {
14 | "dev": {
15 | "command": "astro dev",
16 | "dependencies": [
17 | "../astro-fritz:build",
18 | "../core:build"
19 | ],
20 | "service": true,
21 | "readyWhen": {
22 | "lineMatches": "started in"
23 | }
24 | },
25 | "build": {
26 | "command": "astro build",
27 | "dependencies": [
28 | "../astro-fritz:build",
29 | "../core:build"
30 | ],
31 | "files": [
32 | "astro.config.mjs",
33 | "src/**/*"
34 | ],
35 | "output": [
36 | "dist/**/*"
37 | ]
38 | },
39 | "preview": {
40 | "command": "astro preview",
41 | "dependencies": [
42 | "build"
43 | ],
44 | "service": true,
45 | "readyWhen": {
46 | "lineMatches": "started in"
47 | }
48 | }
49 | },
50 | "repository": {
51 | "type": "git",
52 | "url": "git+https://github.com/matthewp/fritz-template.git"
53 | },
54 | "keywords": [],
55 | "author": "",
56 | "license": "BSD-2-Clause",
57 | "bugs": {
58 | "url": "https://github.com/matthewp/fritz-template/issues"
59 | },
60 | "homepage": "https://github.com/matthewp/fritz-template#readme",
61 | "devDependencies": {
62 | "astro": "^2.0.7",
63 | "astro-fritz": "^2.0.0-alpha.0",
64 | "fritz": "^5.0.0-alpha.6",
65 | "highlight.js": "^9.15.8",
66 | "lowlight": "^1.12.1",
67 | "postcss-preset-env": "^8.0.1",
68 | "rehype-highlight": "^6.0.0",
69 | "rollup": "^1.16.6"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/site/postcss.config.js:
--------------------------------------------------------------------------------
1 | const postcssPresetEnv = require('postcss-preset-env');
2 |
3 | module.exports = {
4 | plugins: [
5 | postcssPresetEnv(/* pluginOptions */)
6 | ]
7 | };
--------------------------------------------------------------------------------
/site/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matthewp/fritz/d2128919ba984735df26ee4cd473ad8cf0200b73/site/public/favicon.ico
--------------------------------------------------------------------------------
/site/public/images/logo-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matthewp/fritz/d2128919ba984735df26ee4cd473ad8cf0200b73/site/public/images/logo-144.png
--------------------------------------------------------------------------------
/site/public/images/logo-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matthewp/fritz/d2128919ba984735df26ee4cd473ad8cf0200b73/site/public/images/logo-192.png
--------------------------------------------------------------------------------
/site/public/images/logo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matthewp/fritz/d2128919ba984735df26ee4cd473ad8cf0200b73/site/public/images/logo-512.png
--------------------------------------------------------------------------------
/site/public/images/logo-96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matthewp/fritz/d2128919ba984735df26ee4cd473ad8cf0200b73/site/public/images/logo-96.png
--------------------------------------------------------------------------------
/site/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Fritz",
3 | "name": "Fritz",
4 | "icons": [
5 | {
6 | "src": "images/logo-96.png",
7 | "sizes": "96x96",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "images/logo-144.png",
12 | "sizes": "144x144",
13 | "type": "image/png"
14 | },
15 | {
16 | "src": "images/logo-192.png",
17 | "sizes": "192x192",
18 | "type": "image/png"
19 | },
20 | {
21 | "src": "images/logo-512.png",
22 | "sizes": "512x512",
23 | "type": "image/png"
24 | }
25 | ],
26 | "start_url": "/",
27 | "display": "standalone",
28 | "orientation": "portrait",
29 | "theme_color": "#58A4B0",
30 | "background_color": "#BAC1B8"
31 | }
32 |
--------------------------------------------------------------------------------
/site/public/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // DO NOT EDIT THIS GENERATED OUTPUT DIRECTLY!
18 | // This file should be overwritten as part of your build process.
19 | // If you need to extend the behavior of the generated service worker, the best approach is to write
20 | // additional code and include it using the importScripts option:
21 | // https://github.com/GoogleChrome/sw-precache#importscripts-arraystring
22 | //
23 | // Alternatively, it's possible to make changes to the underlying template file and then use that as the
24 | // new base for generating output, via the templateFilePath option:
25 | // https://github.com/GoogleChrome/sw-precache#templatefilepath-string
26 | //
27 | // If you go that route, make sure that whenever you update your sw-precache dependency, you reconcile any
28 | // changes made to this original template file with your modified copy.
29 |
30 | // This generated service worker JavaScript will precache your site's resources.
31 | // The code needs to be saved in a .js file at the top-level of your site, and registered
32 | // from your pages in order to be used. See
33 | // https://github.com/googlechrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
34 | // for an example of how you can register this script and handle various service worker events.
35 |
36 | /* eslint-env worker, serviceworker */
37 | /* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren, quotes, comma-spacing */
38 | 'use strict';
39 |
40 | var precacheConfig = [["app.js","db111f5e8f770efffa1214c473b34549"],["frankenstein-fritz-flame.png","d382c041fa5e0564cf780e5ec57dfb7f"],["frankenstein-fritz-flame.webp","64a0d9e360f13b66a5b9c7fe82802cd1"],["index.html","8ea58980dcff6895e0c91d5e385b2d29"],["main.js","728721710b63f80eadafc6f027fdf6fd"],["service-worker-registration.js","854cb02fab00d5e4b382a00771ba8774"]];
41 | var cacheName = 'sw-precache-v3-sw-precache-' + (self.registration ? self.registration.scope : '');
42 |
43 |
44 | var ignoreUrlParametersMatching = [/^utm_/];
45 |
46 |
47 |
48 | var addDirectoryIndex = function(originalUrl, index) {
49 | var url = new URL(originalUrl);
50 | if (url.pathname.slice(-1) === '/') {
51 | url.pathname += index;
52 | }
53 | return url.toString();
54 | };
55 |
56 | var cleanResponse = function(originalResponse) {
57 | // If this is not a redirected response, then we don't have to do anything.
58 | if (!originalResponse.redirected) {
59 | return Promise.resolve(originalResponse);
60 | }
61 |
62 | // Firefox 50 and below doesn't support the Response.body stream, so we may
63 | // need to read the entire body to memory as a Blob.
64 | var bodyPromise = 'body' in originalResponse ?
65 | Promise.resolve(originalResponse.body) :
66 | originalResponse.blob();
67 |
68 | return bodyPromise.then(function(body) {
69 | // new Response() is happy when passed either a stream or a Blob.
70 | return new Response(body, {
71 | headers: originalResponse.headers,
72 | status: originalResponse.status,
73 | statusText: originalResponse.statusText
74 | });
75 | });
76 | };
77 |
78 | var createCacheKey = function(originalUrl, paramName, paramValue,
79 | dontCacheBustUrlsMatching) {
80 | // Create a new URL object to avoid modifying originalUrl.
81 | var url = new URL(originalUrl);
82 |
83 | // If dontCacheBustUrlsMatching is not set, or if we don't have a match,
84 | // then add in the extra cache-busting URL parameter.
85 | if (!dontCacheBustUrlsMatching ||
86 | !(url.pathname.match(dontCacheBustUrlsMatching))) {
87 | url.search += (url.search ? '&' : '') +
88 | encodeURIComponent(paramName) + '=' + encodeURIComponent(paramValue);
89 | }
90 |
91 | return url.toString();
92 | };
93 |
94 | var isPathWhitelisted = function(whitelist, absoluteUrlString) {
95 | // If the whitelist is empty, then consider all URLs to be whitelisted.
96 | if (whitelist.length === 0) {
97 | return true;
98 | }
99 |
100 | // Otherwise compare each path regex to the path of the URL passed in.
101 | var path = (new URL(absoluteUrlString)).pathname;
102 | return whitelist.some(function(whitelistedPathRegex) {
103 | return path.match(whitelistedPathRegex);
104 | });
105 | };
106 |
107 | var stripIgnoredUrlParameters = function(originalUrl,
108 | ignoreUrlParametersMatching) {
109 | var url = new URL(originalUrl);
110 | // Remove the hash; see https://github.com/GoogleChrome/sw-precache/issues/290
111 | url.hash = '';
112 |
113 | url.search = url.search.slice(1) // Exclude initial '?'
114 | .split('&') // Split into an array of 'key=value' strings
115 | .map(function(kv) {
116 | return kv.split('='); // Split each 'key=value' string into a [key, value] array
117 | })
118 | .filter(function(kv) {
119 | return ignoreUrlParametersMatching.every(function(ignoredRegex) {
120 | return !ignoredRegex.test(kv[0]); // Return true iff the key doesn't match any of the regexes.
121 | });
122 | })
123 | .map(function(kv) {
124 | return kv.join('='); // Join each [key, value] array into a 'key=value' string
125 | })
126 | .join('&'); // Join the array of 'key=value' strings into a string with '&' in between each
127 |
128 | return url.toString();
129 | };
130 |
131 |
132 | var hashParamName = '_sw-precache';
133 | var urlsToCacheKeys = new Map(
134 | precacheConfig.map(function(item) {
135 | var relativeUrl = item[0];
136 | var hash = item[1];
137 | var absoluteUrl = new URL(relativeUrl, self.location);
138 | var cacheKey = createCacheKey(absoluteUrl, hashParamName, hash, false);
139 | return [absoluteUrl.toString(), cacheKey];
140 | })
141 | );
142 |
143 | function setOfCachedUrls(cache) {
144 | return cache.keys().then(function(requests) {
145 | return requests.map(function(request) {
146 | return request.url;
147 | });
148 | }).then(function(urls) {
149 | return new Set(urls);
150 | });
151 | }
152 |
153 | self.addEventListener('install', function(event) {
154 | event.waitUntil(
155 | caches.open(cacheName).then(function(cache) {
156 | return setOfCachedUrls(cache).then(function(cachedUrls) {
157 | return Promise.all(
158 | Array.from(urlsToCacheKeys.values()).map(function(cacheKey) {
159 | // If we don't have a key matching url in the cache already, add it.
160 | if (!cachedUrls.has(cacheKey)) {
161 | var request = new Request(cacheKey, {credentials: 'same-origin'});
162 | return fetch(request).then(function(response) {
163 | // Bail out of installation unless we get back a 200 OK for
164 | // every request.
165 | if (!response.ok) {
166 | throw new Error('Request for ' + cacheKey + ' returned a ' +
167 | 'response with status ' + response.status);
168 | }
169 |
170 | return cleanResponse(response).then(function(responseToCache) {
171 | return cache.put(cacheKey, responseToCache);
172 | });
173 | });
174 | }
175 | })
176 | );
177 | });
178 | }).then(function() {
179 |
180 | // Force the SW to transition from installing -> active state
181 | return self.skipWaiting();
182 |
183 | })
184 | );
185 | });
186 |
187 | self.addEventListener('activate', function(event) {
188 | var setOfExpectedUrls = new Set(urlsToCacheKeys.values());
189 |
190 | event.waitUntil(
191 | caches.open(cacheName).then(function(cache) {
192 | return cache.keys().then(function(existingRequests) {
193 | return Promise.all(
194 | existingRequests.map(function(existingRequest) {
195 | if (!setOfExpectedUrls.has(existingRequest.url)) {
196 | return cache.delete(existingRequest);
197 | }
198 | })
199 | );
200 | });
201 | }).then(function() {
202 |
203 | return self.clients.claim();
204 |
205 | })
206 | );
207 | });
208 |
209 |
210 | self.addEventListener('fetch', function(event) {
211 | if (event.request.method === 'GET') {
212 | // Should we call event.respondWith() inside this fetch event handler?
213 | // This needs to be determined synchronously, which will give other fetch
214 | // handlers a chance to handle the request if need be.
215 | var shouldRespond;
216 |
217 | // First, remove all the ignored parameters and hash fragment, and see if we
218 | // have that URL in our cache. If so, great! shouldRespond will be true.
219 | var url = stripIgnoredUrlParameters(event.request.url, ignoreUrlParametersMatching);
220 | shouldRespond = urlsToCacheKeys.has(url);
221 |
222 | // If shouldRespond is false, check again, this time with 'index.html'
223 | // (or whatever the directoryIndex option is set to) at the end.
224 | var directoryIndex = 'index.html';
225 | if (!shouldRespond && directoryIndex) {
226 | url = addDirectoryIndex(url, directoryIndex);
227 | shouldRespond = urlsToCacheKeys.has(url);
228 | }
229 |
230 | // If shouldRespond is still false, check to see if this is a navigation
231 | // request, and if so, whether the URL matches navigateFallbackWhitelist.
232 | var navigateFallback = '';
233 | if (!shouldRespond &&
234 | navigateFallback &&
235 | (event.request.mode === 'navigate') &&
236 | isPathWhitelisted([], event.request.url)) {
237 | url = new URL(navigateFallback, self.location).toString();
238 | shouldRespond = urlsToCacheKeys.has(url);
239 | }
240 |
241 | // If shouldRespond was set to true at any point, then call
242 | // event.respondWith(), using the appropriate cache key.
243 | if (shouldRespond) {
244 | event.respondWith(
245 | caches.open(cacheName).then(function(cache) {
246 | return cache.match(urlsToCacheKeys.get(url)).then(function(response) {
247 | if (response) {
248 | return response;
249 | }
250 | throw Error('The cached response that was expected is missing.');
251 | });
252 | }).catch(function(e) {
253 | // Fall back to just fetch()ing the request if some unexpected error
254 | // prevented the cached response from being valid.
255 | console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
256 | return fetch(event.request);
257 | })
258 | );
259 | }
260 | }
261 | });
262 |
263 |
264 |
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------
/site/public/sw.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.2.0/workbox-sw.js");
15 |
16 | self.addEventListener('message', (event) => {
17 | if (event.data && event.data.type === 'SKIP_WAITING') {
18 | self.skipWaiting();
19 | }
20 | });
21 |
22 | /**
23 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
24 | * requests for URLs in the manifest.
25 | * See https://goo.gl/S9QRab
26 | */
27 | self.__precacheManifest = [
28 | {
29 | "url": "app.js",
30 | "revision": "2983a4b1bf8763e5c3018d73bd9385f4"
31 | },
32 | {
33 | "url": "favicon.ico",
34 | "revision": "7522e123750db337a8fbb15ecb24bb77"
35 | },
36 | {
37 | "url": "frankenstein-fritz-flame.png",
38 | "revision": "d382c041fa5e0564cf780e5ec57dfb7f"
39 | },
40 | {
41 | "url": "frankenstein-fritz-flame.webp",
42 | "revision": "64a0d9e360f13b66a5b9c7fe82802cd1"
43 | },
44 | {
45 | "url": "index.html",
46 | "revision": "432275db1d0c0afa19380fb2aa5596ca"
47 | },
48 | {
49 | "url": "main.js",
50 | "revision": "2c45122305f0c7391f9cad97dfafeabf"
51 | },
52 | {
53 | "url": "manifest.json",
54 | "revision": "4c5a863777c53de7143a363767de0ad8"
55 | },
56 | {
57 | "url": "service-worker.js",
58 | "revision": "1a74471f1f9522c25f49eb818b44853c"
59 | },
60 | {
61 | "url": "images/logo-144.png",
62 | "revision": "df8c7d604b90c1e2e4fd5dfa4fc1239d"
63 | },
64 | {
65 | "url": "images/logo-192.png",
66 | "revision": "db6fcdf854262a09bf49066cfd28db6f"
67 | },
68 | {
69 | "url": "images/logo-512.png",
70 | "revision": "15fda8d80d083e8c46c0226f7bffafcf"
71 | },
72 | {
73 | "url": "images/logo-96.png",
74 | "revision": "14e97738e405176ea97e08fa5d4bbd91"
75 | }
76 | ].concat(self.__precacheManifest || []);
77 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
78 |
--------------------------------------------------------------------------------
/site/src/components/About.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import CodeFile from "./CodeFile.astro";
3 | import CodeSnippet from "./CodeSnippet.astro";
4 | const { class: className } = Astro.props;
5 |
6 | const npmInstall = `
7 | npm install fritz@next --save
8 | `;
9 |
10 | const yarnAdd = `
11 | yarn add fritz@next
12 | `;
13 | ---
14 |
53 |
54 |
What is Fritz?
55 |
Fritz is a UI library that allows you to define components that run inside of a Web Worker. By running your application logic inside of a Worker, you can ensure that the main thread and scrolling are never blocked by expensive work you are doing. Fritz makes jank-free apps possible.
56 |
57 |
Fritz plays nicely with frameworks. Since it is built on web components you can use Fritz just by adding a tag. Use Fritz within your React, Vue.js, Angular, or any other framework. If you have an expensive component that operates on a large dataset, this is a good candidate to turn into a Fritz component. Although you can create your entire app using Fritz (this page is), you don't have to.
58 |
59 |
If you've heard of React's new version, Fiber, Fritz is in some ways an alternative. Fiber enables React to smartly schedule updates. Fritz allows for parallel updates. You're app can launch as many workers as you want and Fritz will use them all. The main thread only ever needs to apply changes. Due to this design, Fritz's scheduler is dead simple; it only needs to ensure that it applies only 16ms of work per frame. It can completely ignore the cost of user-code; that's free with Fritz.
60 |
61 |
Getting Started
62 |
Installation
63 |
64 |
Install Fritz with npm:
65 |
66 |
67 |
Or with Yarn:
68 |
69 |
70 |
Using Fritz
71 |
Fritz lets you define web components inside of a Web Worker. So, the first step to using Fritz is to create a Worker. Use new Worker to do so:
72 |
73 |
74 |
75 |
And then define a component inside of that worker. We'll assume you know how to configure your bundler tool and skip that part. But we should point out that you want to change your Babel config so that it renders JSX to Fritz h() calls.
76 |
77 |
82 |
83 |
Then import all of the needed things and create a basic component:
Cool, now that we have created a component we need to actually use it. Create another bundle named main.js, this will be a script we add to our page which will sync up the DOM to our component:
104 |
105 |
111 |
112 |
Now we just need to add this script to our page and use the component.
Using Fritz components within a React application is simple. First step is to update your .babelrc to use h as the pragma:
128 |
135 |
136 |
This will allow you to transform JSX both for the React and Fritz sides of your application. As before, we won't explain how to configure your bundler, but know that you will need to create a worker bundle (that contains Fritz code) and a bundle for your React code.
137 |
138 |
React doesn't properly handle passing data to web components, but luckily there is a helper library that fixes the issue for us. Install skatejs/val like so:
139 |
140 |
141 |
Then create the module that will act as our wrapper:
Note that this imports our implementation of h, which is just a small wrapper around React.createElement. Since we are using h in both the React app and the worker, our babel config remains the same.
176 |
177 |
Now we just need to implement ${'<'}worker-component${'>'}.
We define props that we expect to receive with a static props getter (of course you can use class properties here if using the Babel plugin).
212 |
render receives props and state as its arguments, so you can destruct.
213 |
Unlike in React and Preact, you can directly pass your class methods as event handlers. Fritz will know to call your component as the this value when calling that function.
214 |
As always, we finish out component by calling fritz.define to define the custom element tag name.
215 |
216 |
217 |
And that's it! Now you can seemlessly use any Fritz components within your React application.