-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: move frontend-lib functions here
frontend-lib will be deprecated after this move. Why? There's not much reason to have frontend-lib functions separate. Both libs are ~0-dependency.
- Loading branch information
1 parent
9bc7539
commit 9920416
Showing
28 changed files
with
1,171 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// | ||
// @naturalcycles/js-lib/cfg/frontend/tsconfig.json | ||
// | ||
// Shared tsconfig for Frontend applications | ||
// | ||
{ | ||
"compilerOptions": { | ||
// Target/module | ||
"target": "es2020", // es2020+ browsers, adjust to your requirements! | ||
"lib": ["esnext", "dom", "dom.iterable"], | ||
"module": "esnext", | ||
"moduleResolution": "node", | ||
"moduleDetection": "force", | ||
// specifying these explicitly for better IDE compatibility (but they're on by default with module=nodenext) | ||
"esModuleInterop": true, | ||
"allowSyntheticDefaultImports": true, | ||
// Faster compilation in general | ||
// Support for external compilers (e.g esbuild) | ||
// Speedup in Jest by using "isolatedModules" in 'ts-jest' config | ||
"isolatedModules": true, | ||
|
||
// Emit | ||
"sourceMap": false, | ||
"declaration": false, | ||
// Otherwise since es2022 it defaults to true | ||
// and starts to produce different/unexpected behavior | ||
// https://angular.schule/blog/2022-11-use-define-for-class-fields | ||
"useDefineForClassFields": false, | ||
"importHelpers": true, | ||
|
||
// Strictness | ||
"strict": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"resolveJsonModule": true, | ||
"suppressImplicitAnyIndexErrors": false, | ||
"noUncheckedIndexedAccess": true, | ||
"noPropertyAccessFromIndexSignature": true, | ||
"noImplicitOverride": true, | ||
|
||
// Enabled should be faster, but will catch less errors | ||
// "skipLibCheck": true, | ||
|
||
// Disabled because of https://github.com/Microsoft/TypeScript/issues/29172 | ||
// Need to be specified in the project tsconfig | ||
// "outDir": "dist", | ||
// "rootDir": "./src", | ||
// "baseUrl": "./", | ||
// "paths": { | ||
// "@src/*": ["src/*"] | ||
// }, | ||
// "typeRoots": [ | ||
// "node_modules/@types", | ||
// "src/@types" | ||
// ], | ||
|
||
// Other | ||
"jsx": "preserve", | ||
"pretty": true, | ||
"newLine": "lf", | ||
"experimentalDecorators": true, | ||
// "emitDecoratorMetadata": true // use if needed | ||
}, | ||
// Need to be specified in the project tsconfig | ||
// "include": ["src"], | ||
// "exclude": ["**/__exclude", "**/@linked"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# AdminService <Badge text="experimental" type="warning"/> | ||
|
||
## Admin Mode | ||
|
||
Admin mode is activated by a combination of keys on a keyboard (no mobile support), visualized with | ||
the RedDot™ on the page. | ||
|
||
Function `startListening()` enables listening for a key combination (Ctrl+Shift+L by default). | ||
|
||
Example: | ||
|
||
```ts | ||
const admin = new AdminService({ | ||
onEnter: () => console.log('Entered Admin mode'), | ||
onExit: () => console.log('Exited Admin mode'), | ||
onRedDotClick: () => alert('RedDot clicked'), | ||
}) | ||
|
||
admin.startListening() | ||
``` | ||
|
||
Try pressing `Ctrl+Shift+L` on the keyboard to see the RedDot™ in action. | ||
|
||
<script setup> | ||
import AdminModeDemo from './components/AdminModeDemo.vue' | ||
</script> | ||
|
||
<AdminModeDemo/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Analytics | ||
|
||
## loadGTag | ||
|
||
Example: | ||
|
||
```ts | ||
await loadGTag('UA-634xxxx-xx') | ||
// gtag is loaded now | ||
``` | ||
|
||
## loadGTM | ||
|
||
Example: | ||
|
||
```ts | ||
await loadGTM('GTM-WJ6xxx') | ||
// GTM is loaded now | ||
``` | ||
|
||
## loadHotjar | ||
|
||
Example: | ||
|
||
```ts | ||
await loadHotjar('19xxxxx') | ||
// Hotjar is loaded now | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# BotDetectionService <Badge text="experimental" type="warning"/> | ||
|
||
Allows to detect simple (non-sophisticated) bots. | ||
|
||
Example usage: | ||
|
||
```ts | ||
import { BotDetectionService } from '@naturalcycles/js-lib' | ||
|
||
const botDetectionService = new BotDetectionService() | ||
|
||
botDetectionService.isBot() // true/false | ||
botDetectionService.isCDP() // true/false | ||
botDetectionService.getBotReason() // BotReason enum | ||
``` | ||
|
||
## Demo | ||
|
||
<script setup> | ||
import {BotDetectionService} from "../src"; | ||
const botDetectionService = new BotDetectionService() | ||
</script> | ||
|
||
<pre> | ||
isBot: {{ botDetectionService.isBot() }} | ||
isCDP: {{ botDetectionService.isCDP() }} | ||
isBotOrCDP: {{ botDetectionService.isBotOrCDP() }} | ||
botReason: {{ botDetectionService.getBotReason() || 'null' }} | ||
</pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<script setup lang="ts"> | ||
import { onMounted } from 'vue' | ||
import { AdminService } from '../../src' | ||
const adminService = new AdminService({ | ||
onChange: adminMode => console.log({ adminMode }), | ||
onRedDotClick: () => alert('RedDot clicked'), | ||
}) | ||
onMounted(() => { | ||
adminService.startListening() | ||
}) | ||
</script> | ||
<template></template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
<script setup lang="ts"> | ||
import { onMounted, onUnmounted, ref, watch } from 'vue' | ||
import { ImageFitter, FitImage, _deepCopy, AnyFunction, StringMap } from '../../src' | ||
const imageIds = [ | ||
'a8ZYS21_Toc', | ||
'rpxnS9CtEDw', | ||
'Ck-qAr0qbAI', | ||
'h5UOgcq1Dkw', | ||
'Jwhzumwgq9Q', | ||
'2aLB0aQI5v4', | ||
'0Q_XPEm2oVQ', | ||
'bz0H2d753_U', | ||
'oSIl84tpYYY', | ||
'cX0Yxw38cx8', | ||
'Y6Oi_1aGKPg', | ||
'AqGhEk1khbE', | ||
'XDvvt_IEH_w', | ||
'leSvrOiu-nE', | ||
'lkeBDBTwjWQ', | ||
'6tJ50mdoyY4', | ||
'wqJW5B9Q05I', | ||
'Q2xGYGSu0Qo', | ||
'Ai-AnKx5bSM', | ||
'O4TA_kXW9as', | ||
'aV31XuctrM8', | ||
'zwoYd0ZiBmc', | ||
'vMGM9Y48eIY', | ||
] | ||
const maxHeight = ref(300) | ||
const margin = ref(8) | ||
const images = ref<FitImage[]>([]) | ||
const fitter = ref<ImageFitter | undefined>() | ||
onMounted(async () => { | ||
// Preload images | ||
const map: StringMap<FitImage> = {} | ||
await new Promise(resolve => { | ||
imageIds.forEach(id => { | ||
const img = new Image() | ||
img.onload = () => { | ||
const { width, height, src } = img | ||
map[id] = { | ||
src, | ||
aspectRatio: width / height, | ||
} | ||
if (Object.keys(map).length === imageIds.length) resolve() | ||
} | ||
img.src = `https://source.unsplash.com/${id}` | ||
}) | ||
}) | ||
images.value = imageIds.map(id => map[id]!) | ||
update() | ||
}) | ||
onUnmounted(() => { | ||
fitter.value?.stop() | ||
}) | ||
watch(() => margin.value + maxHeight.value, update) | ||
function update() { | ||
// console.log('update!') | ||
const containerElement = document.getElementById('imagesContainer')! | ||
fitter.value?.stop() | ||
fitter.value = new ImageFitter({ | ||
containerElement, | ||
images: images.value, | ||
maxHeight: maxHeight.value, | ||
margin: margin.value, | ||
onChange: newImages => { | ||
images.value = _deepCopy(newImages) | ||
}, | ||
}) | ||
} | ||
</script> | ||
|
||
<template> | ||
<div class="app-content"> | ||
<p> | ||
<span class="label">maxHeight: {{ maxHeight }}</span> | ||
<input type="range" min="10" max="400" step="10" v-model="maxHeight" /><br /> | ||
<span class="label">margin: {{ margin }}</span> | ||
<input type="range" min="0" max="20" v-model="margin" /><br /> | ||
</p> | ||
|
||
<p v-if="!images.length">Loading images...</p> | ||
|
||
<div id="imagesContainer" :style="{ margin: `-${margin / 2}px` }"> | ||
<img | ||
v-for="im in images" | ||
:style="{ | ||
width: `${im.fitWidth}px`, | ||
height: `${im.fitHeight}px`, | ||
margin: `${margin / 2}px`, | ||
}" | ||
:src="im.src" | ||
alt="img" | ||
/> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style> | ||
#imagesContainer { | ||
line-height: 0; | ||
border: 1px solid #888; | ||
} | ||
#imagesContainer img { | ||
margin: 4px; | ||
display: inline-block; | ||
} | ||
.label { | ||
display: inline-block; | ||
width: 160px; | ||
} | ||
input { | ||
width: 300px; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<script setup lang="ts"> | ||
import { ref } from 'vue' | ||
import { _stringify, loadCSS, loadScript } from '../../src' | ||
const loading = ref(false) | ||
async function loadGood() { | ||
await load(`https://unpkg.com/[email protected]/dist/jquery.js`) | ||
} | ||
async function loadBad() { | ||
await load(`https://unpkg.com/jqueryNON_EXISTING`) | ||
} | ||
async function loadGoodCSS() { | ||
await loadStylesheet(`https://cdn.simplecss.org/simple.min.css`) | ||
} | ||
async function loadBadCSS() { | ||
await loadStylesheet(`https://cdn.simplecss.org/simpleNOTFOUND.min.css`) | ||
} | ||
async function load(src: string) { | ||
loading.value = true | ||
try { | ||
await loadScript(src) | ||
alert('loaded ok') | ||
} catch (err) { | ||
alert(_stringify(err)) | ||
} finally { | ||
loading.value = false | ||
} | ||
} | ||
async function loadStylesheet(src: string) { | ||
loading.value = true | ||
try { | ||
await loadCSS(src) | ||
alert('loaded ok') | ||
} catch (err) { | ||
alert(_stringify(err)) | ||
} finally { | ||
loading.value = false | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<div class="app-content"> | ||
<button @click="loadGood">Load good script</button> | ||
<button @click="loadBad">Load bad script</button> | ||
<br /><br /> | ||
<button @click="loadGoodCSS">Load good CSS</button> | ||
<button @click="loadBadCSS">Load bad CSS</button> | ||
<p v-if="loading">loading...</p> | ||
</div> | ||
</template> | ||
|
||
<style> | ||
@import '/custom.css'; | ||
</style> |
Oops, something went wrong.