proteum 1.0.0-1
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/.dockerignore +10 -0
- package/Rte.zip +0 -0
- package/cli/app/config.ts +54 -0
- package/cli/app/index.ts +195 -0
- package/cli/bin.js +11 -0
- package/cli/commands/build.ts +34 -0
- package/cli/commands/deploy/app.ts +29 -0
- package/cli/commands/deploy/web.ts +60 -0
- package/cli/commands/dev.ts +109 -0
- package/cli/commands/init.ts +85 -0
- package/cli/compiler/client/identite.ts +72 -0
- package/cli/compiler/client/index.ts +334 -0
- package/cli/compiler/common/babel/index.ts +170 -0
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +579 -0
- package/cli/compiler/common/babel/routes/imports.ts +127 -0
- package/cli/compiler/common/babel/routes/routes.ts +1130 -0
- package/cli/compiler/common/files/autres.ts +39 -0
- package/cli/compiler/common/files/images.ts +35 -0
- package/cli/compiler/common/files/style.ts +78 -0
- package/cli/compiler/common/index.ts +154 -0
- package/cli/compiler/index.ts +532 -0
- package/cli/compiler/server/index.ts +211 -0
- package/cli/index.ts +189 -0
- package/cli/paths.ts +165 -0
- package/cli/print.ts +12 -0
- package/cli/tsconfig.json +38 -0
- package/cli/utils/index.ts +22 -0
- package/cli/utils/keyboard.ts +78 -0
- package/client/app/component.tsx +54 -0
- package/client/app/index.ts +142 -0
- package/client/app/service.ts +34 -0
- package/client/app.tsconfig.json +28 -0
- package/client/components/Button.tsx +298 -0
- package/client/components/Dialog/Manager.tsx +309 -0
- package/client/components/Dialog/card.tsx +208 -0
- package/client/components/Dialog/index.less +151 -0
- package/client/components/Dialog/status.less +176 -0
- package/client/components/Dialog/status.tsx +48 -0
- package/client/components/index.ts +2 -0
- package/client/components/types.d.ts +3 -0
- package/client/data/input.ts +32 -0
- package/client/global.d.ts +5 -0
- package/client/hooks.ts +22 -0
- package/client/index.ts +6 -0
- package/client/pages/_layout/index.less +6 -0
- package/client/pages/_layout/index.tsx +43 -0
- package/client/pages/bug.tsx.old +60 -0
- package/client/pages/useHeader.tsx +50 -0
- package/client/services/captcha/index.ts +67 -0
- package/client/services/router/components/Link.tsx +46 -0
- package/client/services/router/components/Page.tsx +55 -0
- package/client/services/router/components/router.tsx +218 -0
- package/client/services/router/index.tsx +521 -0
- package/client/services/router/request/api.ts +267 -0
- package/client/services/router/request/history.ts +5 -0
- package/client/services/router/request/index.ts +53 -0
- package/client/services/router/request/multipart.ts +147 -0
- package/client/services/router/response/index.tsx +128 -0
- package/client/services/router/response/page.ts +86 -0
- package/client/services/socket/index.ts +147 -0
- package/client/utils/dom.ts +77 -0
- package/common/app/index.ts +9 -0
- package/common/data/chaines/index.ts +54 -0
- package/common/data/dates.ts +179 -0
- package/common/data/markdown.ts +73 -0
- package/common/data/rte/nodes.ts +83 -0
- package/common/data/stats.ts +90 -0
- package/common/errors/index.tsx +326 -0
- package/common/router/index.ts +213 -0
- package/common/router/layouts.ts +93 -0
- package/common/router/register.ts +55 -0
- package/common/router/request/api.ts +77 -0
- package/common/router/request/index.ts +35 -0
- package/common/router/response/index.ts +45 -0
- package/common/router/response/page.ts +128 -0
- package/common/utils/rte.ts +183 -0
- package/common/utils.ts +7 -0
- package/doc/TODO.md +71 -0
- package/doc/front/router.md +27 -0
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/package.json +171 -0
- package/server/app/commands.ts +141 -0
- package/server/app/container/config.ts +203 -0
- package/server/app/container/console/index.ts +550 -0
- package/server/app/container/index.ts +137 -0
- package/server/app/index.ts +273 -0
- package/server/app/service/container.ts +88 -0
- package/server/app/service/index.ts +235 -0
- package/server/app.tsconfig.json +28 -0
- package/server/context.ts +4 -0
- package/server/index.ts +4 -0
- package/server/services/auth/index.ts +250 -0
- package/server/services/auth/old.ts +277 -0
- package/server/services/auth/router/index.ts +95 -0
- package/server/services/auth/router/request.ts +54 -0
- package/server/services/auth/router/service.json +6 -0
- package/server/services/auth/service.json +6 -0
- package/server/services/cache/commands.ts +41 -0
- package/server/services/cache/index.ts +297 -0
- package/server/services/cache/service.json +6 -0
- package/server/services/cron/CronTask.ts +86 -0
- package/server/services/cron/index.ts +112 -0
- package/server/services/cron/service.json +6 -0
- package/server/services/disks/driver.ts +103 -0
- package/server/services/disks/drivers/local/index.ts +188 -0
- package/server/services/disks/drivers/local/service.json +6 -0
- package/server/services/disks/drivers/s3/index.ts +301 -0
- package/server/services/disks/drivers/s3/service.json +6 -0
- package/server/services/disks/index.ts +90 -0
- package/server/services/disks/service.json +6 -0
- package/server/services/email/index.ts +188 -0
- package/server/services/email/utils.ts +53 -0
- package/server/services/fetch/index.ts +201 -0
- package/server/services/fetch/service.json +7 -0
- package/server/services/models.7z +0 -0
- package/server/services/prisma/Facet.ts +142 -0
- package/server/services/prisma/index.ts +201 -0
- package/server/services/prisma/service.json +6 -0
- package/server/services/router/http/index.ts +217 -0
- package/server/services/router/http/multipart.ts +102 -0
- package/server/services/router/http/session.ts.old +40 -0
- package/server/services/router/index.ts +801 -0
- package/server/services/router/request/api.ts +87 -0
- package/server/services/router/request/index.ts +184 -0
- package/server/services/router/request/service.ts +21 -0
- package/server/services/router/request/validation/zod.ts +180 -0
- package/server/services/router/response/index.ts +338 -0
- package/server/services/router/response/mask/Filter.ts +323 -0
- package/server/services/router/response/mask/index.ts +60 -0
- package/server/services/router/response/mask/selecteurs.ts +92 -0
- package/server/services/router/response/page/document.tsx +160 -0
- package/server/services/router/response/page/index.tsx +196 -0
- package/server/services/router/service.json +6 -0
- package/server/services/router/service.ts +36 -0
- package/server/services/schema/index.ts +44 -0
- package/server/services/schema/request.ts +49 -0
- package/server/services/schema/router/index.ts +28 -0
- package/server/services/schema/router/service.json +6 -0
- package/server/services/schema/service.json +6 -0
- package/server/services/security/encrypt/aes/index.ts +85 -0
- package/server/services/security/encrypt/aes/service.json +6 -0
- package/server/services/socket/index.ts +162 -0
- package/server/services/socket/scope.ts +226 -0
- package/server/services/socket/service.json +6 -0
- package/server/services_old/SocketClient.ts +92 -0
- package/server/services_old/Token.old.ts +97 -0
- package/server/utils/slug.ts +79 -0
- package/tsconfig.common.json +45 -0
- package/tsconfig.json +3 -0
- package/types/aliases.d.ts +54 -0
- package/types/global/modules.d.ts +49 -0
- package/types/global/utils.d.ts +103 -0
- package/types/icons.d.ts +1 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
import type { ClientContext } from '../../context';
|
|
6
|
+
|
|
7
|
+
import type { TDialogControls } from '@client/components/Dialog/Manager';
|
|
8
|
+
|
|
9
|
+
/*----------------------------------
|
|
10
|
+
- TYPE
|
|
11
|
+
----------------------------------*/
|
|
12
|
+
|
|
13
|
+
type TEventsList = {[name: string]: TEventCallback[]};
|
|
14
|
+
|
|
15
|
+
type TEventCallback = (data: any) => void
|
|
16
|
+
|
|
17
|
+
let netwokrStatusToast: TDialogControls;
|
|
18
|
+
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- SERVICE
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
class SocketScope {
|
|
23
|
+
|
|
24
|
+
public ws?: WebSocket;
|
|
25
|
+
public events: TEventsList = {}
|
|
26
|
+
|
|
27
|
+
public constructor(
|
|
28
|
+
public path: string,
|
|
29
|
+
public context: ClientContext,
|
|
30
|
+
events: {[name: string]: TEventCallback} = {}
|
|
31
|
+
) {
|
|
32
|
+
this.connect();
|
|
33
|
+
|
|
34
|
+
for (const event in events)
|
|
35
|
+
this.events[event] = [events[event]];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private connect() {
|
|
39
|
+
|
|
40
|
+
const protocol = window.location.protocol === 'http:' ? 'ws:' : 'wss:';
|
|
41
|
+
const url = protocol + '//' + this.context.request.host + this.path;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
this.ws = new WebSocket(url);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.warn(`[socket] Connection failed for ${url}`, error);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.ws.onopen = () => {
|
|
51
|
+
console.log(`[socket] Connected to the live data provider:`, url);
|
|
52
|
+
|
|
53
|
+
if (netwokrStatusToast) {
|
|
54
|
+
netwokrStatusToast.close(true);
|
|
55
|
+
this.context.toast.success("Your connection has been restored", null, null, {
|
|
56
|
+
autohide: 3
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.ws.onmessage = (event: MessageEvent<any>) => {
|
|
62
|
+
|
|
63
|
+
const [name, rawData] = event.data.split('>');
|
|
64
|
+
if (this.events[name] === undefined) {
|
|
65
|
+
console.warn("Unknown command: " + name + `. Raw command:`, event.data);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let data;
|
|
70
|
+
try {
|
|
71
|
+
data = JSON.parse(rawData);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.warn(`[socket] Error decoding data`, rawData, error);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const callback of this.events[name])
|
|
78
|
+
callback(data);
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.ws.onerror = (event) => {
|
|
83
|
+
console.log(`[socket] Network error for ${url}`, event);
|
|
84
|
+
this.close();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.ws.onclose = (event) => {
|
|
88
|
+
|
|
89
|
+
// Fermeture volontaire = on ne retente pas de se connecter
|
|
90
|
+
if (event.wasClean) {
|
|
91
|
+
console.log(`[socket] Disconnected from ${url}. Reason: `, event.reason);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!netwokrStatusToast)
|
|
96
|
+
netwokrStatusToast = this.context.toast.error("You're offline", "Please check your connection", null, {
|
|
97
|
+
autohide: false,
|
|
98
|
+
prison: true
|
|
99
|
+
});
|
|
100
|
+
console.log(`[socket] Disconnected from ${url}. Retry in 5 seconds ...`, event);
|
|
101
|
+
setTimeout(() => this.connect(), 5000);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public close() {
|
|
107
|
+
if (this.ws)
|
|
108
|
+
this.ws.close();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public on(event: string, callback: TEventCallback) {
|
|
112
|
+
|
|
113
|
+
if (this.events[event] === undefined)
|
|
114
|
+
this.events[event] = [callback]
|
|
115
|
+
else
|
|
116
|
+
this.events[event].push(callback)
|
|
117
|
+
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public off(event: string) {
|
|
122
|
+
|
|
123
|
+
delete this.events[event];
|
|
124
|
+
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default class SocketClient {
|
|
131
|
+
|
|
132
|
+
public scopes: { [name: string]: SocketScope } = {}
|
|
133
|
+
|
|
134
|
+
public constructor( public context: ClientContext ) {
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public open(path: string, commands: {[name: string]: TEventCallback} = {}) {
|
|
140
|
+
|
|
141
|
+
if (!(path in this.scopes))
|
|
142
|
+
this.scopes[path] = new SocketScope(path, this.context, commands);
|
|
143
|
+
|
|
144
|
+
return this.scopes[path]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { RefObject } from 'preact';
|
|
2
|
+
import { history } from '@client/services/router/request/history';
|
|
3
|
+
|
|
4
|
+
type ElementOrSelector = HTMLElement | string;
|
|
5
|
+
export const deepContains = (
|
|
6
|
+
parents: ElementOrSelector | ElementOrSelector[],
|
|
7
|
+
children: HTMLElement
|
|
8
|
+
): boolean => {
|
|
9
|
+
|
|
10
|
+
if (!Array.isArray(parents))
|
|
11
|
+
parents = [parents];
|
|
12
|
+
|
|
13
|
+
let node: HTMLElement | null = children;
|
|
14
|
+
while (node) {
|
|
15
|
+
for (const parent of parents) {
|
|
16
|
+
//console.log('Checking if', parent, 'matches with', node);
|
|
17
|
+
if (
|
|
18
|
+
// HTML Element
|
|
19
|
+
node === parent
|
|
20
|
+
||
|
|
21
|
+
// CSS Selector
|
|
22
|
+
(typeof parent === 'string' && node.matches && node.matches(parent))
|
|
23
|
+
)
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
node = node.parentNode as HTMLElement | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Usage: React.useEffect( blurable([ <element>, <function> ]) );
|
|
34
|
+
export const blurable = (...args: [HTMLElement, Function][]) => {
|
|
35
|
+
|
|
36
|
+
if (!history)
|
|
37
|
+
return;
|
|
38
|
+
|
|
39
|
+
const blur = (e: MouseEvent) => {
|
|
40
|
+
|
|
41
|
+
if (e.target === null)
|
|
42
|
+
return;
|
|
43
|
+
|
|
44
|
+
for (const [refElement, masquer] of args) {
|
|
45
|
+
|
|
46
|
+
//console.log("refElement", refElement, e.target?.matches);
|
|
47
|
+
|
|
48
|
+
if (!deepContains([refElement], e.target))
|
|
49
|
+
masquer();
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
window.addEventListener('mousedown', blur);
|
|
55
|
+
|
|
56
|
+
const unlisten = history.listen(() => {
|
|
57
|
+
for (const [, masquer] of args) {
|
|
58
|
+
|
|
59
|
+
masquer();
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
window.removeEventListener('mousedown', blur);
|
|
66
|
+
unlisten();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const focusContent = ( container: HTMLElement ) => {
|
|
71
|
+
|
|
72
|
+
const toFocus = container.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLButtonElement>(
|
|
73
|
+
'input, textarea, button.btn.primary, footer > button.btn'
|
|
74
|
+
) || container; // Is it useful ? Creating unwanted scroll issue on showing popover
|
|
75
|
+
|
|
76
|
+
toFocus?.focus();
|
|
77
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type ClientApplication from '@client/app';
|
|
2
|
+
import type ServerApplication from '@server/app';
|
|
3
|
+
|
|
4
|
+
export type ClientOrServerApplication = ClientApplication | ServerApplication;
|
|
5
|
+
|
|
6
|
+
export type TAppArrowFunction<
|
|
7
|
+
TRegisteredData = void,
|
|
8
|
+
TApplication extends ClientOrServerApplication = ClientOrServerApplication
|
|
9
|
+
> = (app: TApplication) => TRegisteredData
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- TRANSFORM
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a string into an ID
|
|
7
|
+
* @param name The string to normalize
|
|
8
|
+
* @returns A ID string
|
|
9
|
+
*/
|
|
10
|
+
export const nameToID = (name: string) => name.toLowerCase().replace(/[^a-z1-9]/gi, '');
|
|
11
|
+
|
|
12
|
+
export const ucfirst = (chaine: string): string => {
|
|
13
|
+
return chaine.charAt(0).toUpperCase() + chaine.slice(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const linkify = (texte: string): string => {
|
|
17
|
+
const regex = /((http|https)\:\/\/([a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?))/gi;
|
|
18
|
+
return texte.replace(regex, '<a href="$1" target="_blank">$3</a>');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const trim = (s: string, c: string) => {
|
|
22
|
+
if (c === "]") c = "\\]";
|
|
23
|
+
if (c === "\\") c = "\\\\";
|
|
24
|
+
return s.replace(new RegExp(
|
|
25
|
+
"^[" + c + "]+|[" + c + "]+$", "g"
|
|
26
|
+
), "");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const trimLeft = (chaine: string, toTrim: string) => chaine.startsWith(toTrim)
|
|
30
|
+
? chaine.substring(toTrim.length) : chaine;
|
|
31
|
+
|
|
32
|
+
export const trimRight = (chaine: string, toTrim: string) => chaine.endsWith(toTrim)
|
|
33
|
+
? chaine.substring(0, -toTrim.length) : chaine;
|
|
34
|
+
|
|
35
|
+
export const escapeRegExp = (string: string) =>
|
|
36
|
+
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
37
|
+
|
|
38
|
+
/*----------------------------------
|
|
39
|
+
- EXTRACT
|
|
40
|
+
----------------------------------*/
|
|
41
|
+
export const getKeywords = (str: string, delimiter: string = ' ') => str
|
|
42
|
+
|
|
43
|
+
// Minuscule
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
|
|
46
|
+
// condenseWhitespace ( https://github.com/sindresorhus/condense-whitespace/blob/main/index.js )
|
|
47
|
+
.trim().replace(/\s{2,}/gu, ' ')
|
|
48
|
+
|
|
49
|
+
// Ne garde que les caractères alĥanumériques, ainsi que .
|
|
50
|
+
// https://stackoverflow.com/questions/20690499/concrete-javascript-regex-for-accented-characters-diacritics
|
|
51
|
+
.replace(/[^\.0-9A-Za-zÀ-ÖØ-öø-ÿ]/ig, ' ')
|
|
52
|
+
|
|
53
|
+
// TODO: remove stopwords: https://github.com/fergiemcdowall/stopword
|
|
54
|
+
export const getSlug = (str: string) => getKeywords(str, '-')
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
/*
|
|
3
|
+
// Function to calculate the difference in the specified unit
|
|
4
|
+
function dateDiffInUnits(date1, date2) {
|
|
5
|
+
const msPerSecond = 1000;
|
|
6
|
+
const msPerMinute = msPerSecond * 60;
|
|
7
|
+
const msPerHour = msPerMinute * 60;
|
|
8
|
+
const msPerDay = msPerHour * 24;
|
|
9
|
+
const msPerMonth = msPerDay * 30; // Approximation
|
|
10
|
+
const msPerYear = msPerDay * 365; // Approximation
|
|
11
|
+
|
|
12
|
+
const diffInMs = date2 - date1;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
years: diffInMs / msPerYear,
|
|
16
|
+
months: diffInMs / msPerMonth,
|
|
17
|
+
days: diffInMs / msPerDay,
|
|
18
|
+
hours: diffInMs / msPerHour,
|
|
19
|
+
minutes: diffInMs / msPerMinute,
|
|
20
|
+
seconds: diffInMs / msPerSecond,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Function to determine the best unit based on the differences
|
|
25
|
+
function chooseBestUnit(diffs) {
|
|
26
|
+
if (Math.abs(diffs.years) >= 1) return 'year';
|
|
27
|
+
if (Math.abs(diffs.months) >= 1) return 'month';
|
|
28
|
+
if (Math.abs(diffs.days) >= 1) return 'day';
|
|
29
|
+
if (Math.abs(diffs.hours) >= 1) return 'hour';
|
|
30
|
+
if (Math.abs(diffs.minutes) >= 1) return 'minute';
|
|
31
|
+
return 'second';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Function to format the relative time between two dates
|
|
35
|
+
function formatRelativeTime(date1, date2, locale = 'en') {
|
|
36
|
+
const diffs = dateDiffInUnits(date1, date2);
|
|
37
|
+
const bestUnit = chooseBestUnit(diffs);
|
|
38
|
+
const diff = diffs[bestUnit];
|
|
39
|
+
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
|
|
40
|
+
return rtf.format(Math.round(diff), bestUnit);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Example dates
|
|
44
|
+
const date1 = new Date('2023-07-01');
|
|
45
|
+
const date2 = new Date('2024-07-13');
|
|
46
|
+
|
|
47
|
+
// Format the relative time automatically choosing the best unit
|
|
48
|
+
console.log(formatRelativeTime(date1, date2)); // Output: "in 1 year"
|
|
49
|
+
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
export type TDateInfo = {
|
|
53
|
+
isPast: boolean,
|
|
54
|
+
delta: number,
|
|
55
|
+
text: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const timeSince = (date: Date | number | string): TDateInfo | null => {
|
|
59
|
+
|
|
60
|
+
if (date === undefined)
|
|
61
|
+
return null;
|
|
62
|
+
|
|
63
|
+
// Timeago ne prend que des dates et des timestamp
|
|
64
|
+
if (typeof date === 'string') {
|
|
65
|
+
date = Date.parse(date);
|
|
66
|
+
if (isNaN(date))
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get metas
|
|
71
|
+
const now = Date.now()
|
|
72
|
+
const timestamp = date instanceof Date ? date.getTime() : date;
|
|
73
|
+
const deltaSeconds = Math.abs( Math.round( (now - timestamp) / 1000 ));
|
|
74
|
+
const isPast = now > timestamp;
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
text: date,//timeAgo.format(date),
|
|
78
|
+
isPast,
|
|
79
|
+
delta: deltaSeconds
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const tempsRelatif = (time: number, nbChiffresInit?: number) => {
|
|
84
|
+
|
|
85
|
+
const nbChiffres = nbChiffresInit === undefined ? 2 : nbChiffresInit;
|
|
86
|
+
|
|
87
|
+
const jours = Math.floor(time / (60 * 60 * 24));
|
|
88
|
+
|
|
89
|
+
if (jours >= 1) {
|
|
90
|
+
|
|
91
|
+
return jours + (jours === 1 ? ' day' : ' days')
|
|
92
|
+
|
|
93
|
+
} else {
|
|
94
|
+
|
|
95
|
+
const heures = Math.floor((time % (60 * 60 * 24)) / (60 * 60));
|
|
96
|
+
const minutes = Math.floor((time % (60 * 60)) / (60));
|
|
97
|
+
const secondes = Math.floor(time % (60));
|
|
98
|
+
|
|
99
|
+
return [heures, minutes, secondes].filter(
|
|
100
|
+
(nb: number | false, i: number) => nb > 0 || 4 - i <= nbChiffres
|
|
101
|
+
).map(
|
|
102
|
+
(nb: number) => nb < 10 ? '0' + nb : nb
|
|
103
|
+
).join(':');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const chaineDate = (chaine: string): boolean => {
|
|
108
|
+
// 2019-09-09T11:28:21.778Z
|
|
109
|
+
const regexDate = /[0-9]{4}\-[0-9]{2}\-[0-9]{2}T[0-9]{2}\:[0-9]{2}\:[0-9]{2}\.[0-9]{3}Z/;
|
|
110
|
+
return regexDate.test( chaine );
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// Based on https://github.com/sebastiansandqvist/s-ago/blob/master/index.ts
|
|
118
|
+
type TUnit = {
|
|
119
|
+
max: number,
|
|
120
|
+
divisor?: number,
|
|
121
|
+
past: string,
|
|
122
|
+
future: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const units: {[name: string]: TUnit} = {
|
|
126
|
+
seconds: { max: 60000, past: 'just now', future: 'now' },
|
|
127
|
+
minute: { max: 2760000, divisor: 60000, past: 'a minute ago', future: 'in a minute' }, // max: 46 minutes
|
|
128
|
+
hour: { max: 72000000, divisor: 3600000, past: 'an hour ago', future: 'in an hour' }, // max: 20 hours
|
|
129
|
+
day: { max: 518400000, divisor: 86400000, past: 'yesterday', future: 'tomorrow' }, // max: 6 days
|
|
130
|
+
week: { max: 2419200000, divisor: 604800000, past: 'last week', future: 'in a week' }, // max: 28 days
|
|
131
|
+
month: { max: 28512000000, divisor: 2592000000, past: 'last month', future: 'in a month' }, // max: 11 months
|
|
132
|
+
year: { max: Infinity, divisor: 31536000000, past: 'last year', future: 'in a year' },
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export function ago(date: Date | string, { min, max }: { min?: string, max?: string } = {}): string {
|
|
136
|
+
|
|
137
|
+
if (!date)
|
|
138
|
+
return '-';
|
|
139
|
+
|
|
140
|
+
if (typeof date === 'string')
|
|
141
|
+
date = new Date(date);
|
|
142
|
+
|
|
143
|
+
const minUnit = min ? units[min].max : 0;
|
|
144
|
+
const diff = Date.now() - date.getTime();
|
|
145
|
+
const delta = Math.abs(diff);
|
|
146
|
+
|
|
147
|
+
let unitName!: string;
|
|
148
|
+
let unit!: TUnit;
|
|
149
|
+
for (unitName in units) {
|
|
150
|
+
unit = units[unitName];
|
|
151
|
+
if (unit.max >= minUnit && (delta < unit.max || unitName === max))
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (unit.divisor === undefined)
|
|
156
|
+
return diff < 0 ? unit.future : unit.past;
|
|
157
|
+
|
|
158
|
+
var val = Math.round(delta / unit.divisor);
|
|
159
|
+
if (diff < 0)
|
|
160
|
+
return val <= 1 ? unit.future : 'in ' + val + ' ' + unitName + 's';
|
|
161
|
+
else
|
|
162
|
+
return val <= 1 ? unit.past : val + ' ' + unitName + 's ago';
|
|
163
|
+
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const daysAgo = (date: string) => {
|
|
167
|
+
|
|
168
|
+
const days = dayjs().diff(date, 'days')
|
|
169
|
+
|
|
170
|
+
if (days === 0)
|
|
171
|
+
return 'Today';
|
|
172
|
+
else if (days === 1)
|
|
173
|
+
return 'Yesterday';
|
|
174
|
+
else if (days <= 7)
|
|
175
|
+
return days + ' days ago'
|
|
176
|
+
else
|
|
177
|
+
return dayjs(date).format('DD/MM');
|
|
178
|
+
|
|
179
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import markdownIt from 'markdown-it';
|
|
2
|
+
const md = markdownIt({
|
|
3
|
+
html: false, // Enable HTML tags in source
|
|
4
|
+
xhtmlOut: false, // Use '/' to close single tags (<br />). This is only for full CommonMark compatibility.
|
|
5
|
+
breaks: true, // Convert '\n' in paragraphs into <br>
|
|
6
|
+
langPrefix: 'language-', // CSS language prefix for fenced blocks. Can be useful for external highlighters.
|
|
7
|
+
linkify: false, // Autoconvert URL-like text to links
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const rules = md.renderer.rules;
|
|
11
|
+
|
|
12
|
+
// ------------------------
|
|
13
|
+
|
|
14
|
+
var link_open_default = rules.link_open || function (tokens, idx, options, env, self) {
|
|
15
|
+
return self.renderToken(tokens, idx, options);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Target = _blank
|
|
19
|
+
rules.link_open = function (tokens, idx, options, env, self) {
|
|
20
|
+
|
|
21
|
+
const aIndex = tokens[idx].attrIndex('target');
|
|
22
|
+
if (aIndex < 0) {
|
|
23
|
+
tokens[idx].attrPush(['target', '_blank']);
|
|
24
|
+
} else {
|
|
25
|
+
tokens[idx].attrs[ aIndex ][1] = '_blank';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return link_open_default(tokens, idx, options, env, self);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// ------------------------
|
|
32
|
+
|
|
33
|
+
var image_default = rules.image || function (tokens, idx, options, env, self) {
|
|
34
|
+
return self.renderToken(tokens, idx, options);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// img => figure
|
|
38
|
+
rules.image = function (tokens, idx, options, env, self) {
|
|
39
|
+
|
|
40
|
+
const rendu = image_default(tokens, idx, options, env, self);
|
|
41
|
+
|
|
42
|
+
return `<figure>${rendu}</figure>`
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ------------------------
|
|
46
|
+
|
|
47
|
+
md.block.ruler.after('list', 'test', (state, startLine, endLine, silent) => {
|
|
48
|
+
|
|
49
|
+
for (const token of state.tokens) {
|
|
50
|
+
if (token.type === 'bullet_list_open') {
|
|
51
|
+
|
|
52
|
+
const aIndex = token.attrIndex('class');
|
|
53
|
+
if (aIndex < 0) {
|
|
54
|
+
token.attrPush(['class', 'liste']); // add new attribute
|
|
55
|
+
} else {
|
|
56
|
+
token.attrs[ aIndex ][1] = 'liste'; // replace value of existing attr
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
} else if (token.type === 'ordered_list_open') {
|
|
60
|
+
|
|
61
|
+
const aIndex = token.attrIndex('class');
|
|
62
|
+
if (aIndex < 0) {
|
|
63
|
+
token.attrPush(['class', 'steps']); // add new attribute
|
|
64
|
+
} else {
|
|
65
|
+
token.attrs[ aIndex ][1] = 'steps'; // replace value of existing attr
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}, { alt: ['paragraph', 'reference', 'blockquote'] })
|
|
72
|
+
|
|
73
|
+
export default md;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Klass, LexicalNode } from 'lexical';
|
|
10
|
+
|
|
11
|
+
import { CodeHighlightNode, CodeNode } from '@lexical/code';
|
|
12
|
+
import { HashtagNode } from '@lexical/hashtag';
|
|
13
|
+
import { AutoLinkNode, LinkNode } from '@lexical/link';
|
|
14
|
+
import { ListItemNode, ListNode } from '@lexical/list';
|
|
15
|
+
import { MarkNode } from '@lexical/mark';
|
|
16
|
+
import { OverflowNode } from '@lexical/overflow';
|
|
17
|
+
import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
18
|
+
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
|
|
19
|
+
import { TableCellNode, TableNode, TableRowNode } from '@lexical/table';
|
|
20
|
+
|
|
21
|
+
import { CollapsibleContainerNode } from '@client/components/Rte/plugins/CollapsiblePlugin/CollapsibleContainerNode';
|
|
22
|
+
import { CollapsibleContentNode } from '@client/components/Rte/plugins/CollapsiblePlugin/CollapsibleContentNode';
|
|
23
|
+
import { CollapsibleTitleNode } from '@client/components/Rte/plugins/CollapsiblePlugin/CollapsibleTitleNode';
|
|
24
|
+
import { AutocompleteNode } from '@client/components/Rte/nodes/AutocompleteNode';
|
|
25
|
+
import { EmojiNode } from '@client/components/Rte/nodes/EmojiNode';
|
|
26
|
+
import { ImageNode } from '@client/components/Rte/nodes/ImageNode';
|
|
27
|
+
import { InlineImageNode } from '@client/components/Rte/nodes/InlineImageNode/InlineImageNode';
|
|
28
|
+
import { KeywordNode } from '@client/components/Rte/nodes/KeywordNode';
|
|
29
|
+
import { LayoutContainerNode } from '@client/components/Rte/nodes/LayoutContainerNode';
|
|
30
|
+
import { LayoutItemNode } from '@client/components/Rte/nodes/LayoutItemNode';
|
|
31
|
+
import { MentionNode } from '@client/components/Rte/nodes/MentionNode';
|
|
32
|
+
import { PageBreakNode } from '@client/components/Rte/nodes/PageBreakNode';
|
|
33
|
+
import { PollNode } from '@client/components/Rte/nodes/PollNode';
|
|
34
|
+
import { StickyNode } from '@client/components/Rte/nodes/StickyNode';
|
|
35
|
+
import { TweetNode } from '@client/components/Rte/nodes/TweetNode';
|
|
36
|
+
import { YouTubeNode } from '@client/components/Rte/nodes/YouTubeNode';
|
|
37
|
+
|
|
38
|
+
import HeadingWithAnchorNode from '@client/components/Rte/nodes/HeadingNode';
|
|
39
|
+
import ReferenceLinkNode from '@client/components/Rte/nodes/ReferenceLinkNode';
|
|
40
|
+
|
|
41
|
+
const PlaygroundNodes: Array<Klass<LexicalNode>> = [
|
|
42
|
+
/*HeadingNode, */HeadingWithAnchorNode,
|
|
43
|
+
{
|
|
44
|
+
replace: HeadingNode,
|
|
45
|
+
with: (node) => {
|
|
46
|
+
return new HeadingWithAnchorNode( node.getTag() );
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
ListNode,
|
|
50
|
+
ListItemNode,
|
|
51
|
+
QuoteNode,
|
|
52
|
+
CodeNode,
|
|
53
|
+
TableNode,
|
|
54
|
+
TableCellNode,
|
|
55
|
+
TableRowNode,
|
|
56
|
+
HashtagNode,
|
|
57
|
+
CodeHighlightNode,
|
|
58
|
+
AutoLinkNode,
|
|
59
|
+
LinkNode,
|
|
60
|
+
OverflowNode,
|
|
61
|
+
PollNode,
|
|
62
|
+
StickyNode,
|
|
63
|
+
ImageNode, InlineImageNode,
|
|
64
|
+
MentionNode,
|
|
65
|
+
EmojiNode,
|
|
66
|
+
AutocompleteNode,
|
|
67
|
+
KeywordNode,
|
|
68
|
+
HorizontalRuleNode,
|
|
69
|
+
TweetNode,
|
|
70
|
+
YouTubeNode,
|
|
71
|
+
MarkNode,
|
|
72
|
+
CollapsibleContainerNode,
|
|
73
|
+
CollapsibleContentNode,
|
|
74
|
+
CollapsibleTitleNode,
|
|
75
|
+
PageBreakNode,
|
|
76
|
+
LayoutContainerNode,
|
|
77
|
+
LayoutItemNode,
|
|
78
|
+
|
|
79
|
+
// Custom
|
|
80
|
+
ReferenceLinkNode
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export default PlaygroundNodes;
|