hamzus-ui 0.0.12 → 0.0.14

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/index.d.ts CHANGED
@@ -15,13 +15,21 @@ export { default as Kbd } from "./src/components/hamzus-ui/Kbd/Kbd.svelte"
15
15
  export { default as Dialog } from "./src/components/hamzus-ui/Dialog/Dialog.svelte"
16
16
 
17
17
  // form
18
+ export { default as Form } from "./src/components/hamzus-ui/Form/Form.svelte"
18
19
  export { default as Input } from "./src/components/hamzus-ui/Input/Input.svelte"
19
20
  export { default as DatePicker } from "./src/components/hamzus-ui/DatePicker/DatePicker.svelte"
20
21
  export { default as TextArea } from "./src/components/hamzus-ui/TextArea/TextArea.svelte"
21
22
  export { default as Checkbox } from "./src/components/hamzus-ui/Checkboxes/Checkbox/Checkbox.svelte"
22
23
  export { default as Switch } from "./src/components/hamzus-ui/Swicth/Swicth.svelte"
23
24
 
25
+ // data
26
+ export { default as ProgressCircle } from "./src/components/hamzus-ui/ProgressCircle/ProgressCircle.svelte"
27
+
24
28
 
25
29
  export * as DropdownMenu from "./src/components/hamzus-ui/DropdownMenu";
26
30
  export * as Popover from "./src/components/hamzus-ui/Popover";
27
31
  export * as Tooltip from "./src/components/hamzus-ui/AdvancedTooltip";
32
+
33
+ // utils
34
+ export { getCookie as getCookie } from "./src/utils/clientCookie.js"
35
+ export { sendRequest as sendRequest } from "./src/utils/request.js"
package/index.js CHANGED
@@ -11,13 +11,19 @@ export { default as Kbd } from "./src/components/hamzus-ui/KBD/KBD.svelte"
11
11
  // dialog
12
12
  export { default as Dialog } from "./src/components/hamzus-ui/Dialog/Dialog.svelte"
13
13
  // form
14
+ export { default as Form } from "./src/components/hamzus-ui/Form/Form.svelte"
14
15
  export { default as Input } from "./src/components/hamzus-ui/Input/Input.svelte"
15
16
  export { default as DatePicker } from "./src/components/hamzus-ui/DatePicker/DatePicker.svelte"
16
17
  export { default as TextArea } from "./src/components/hamzus-ui/TextArea/TextArea.svelte"
17
18
  export { default as Checkbox } from "./src/components/hamzus-ui/Checkboxes/Checkbox/Checkbox.svelte"
18
19
  export { default as Switch } from "./src/components/hamzus-ui/Swicth/Swicth.svelte"
19
-
20
+ // data
21
+ export { default as ProgressCircle } from "./src/components/hamzus-ui/ProgressCircle/ProgressCircle.svelte"
20
22
 
21
23
  export * as DropdownMenu from "./src/components/hamzus-ui/DropdownMenu"
22
24
  export * as Popover from "./src/components/hamzus-ui/Popover"
23
- export * as Tooltip from "./src/components/hamzus-ui/AdvancedTooltip"
25
+ export * as Tooltip from "./src/components/hamzus-ui/AdvancedTooltip"
26
+
27
+ // utils
28
+ export { getCookie as getCookie } from "./src/utils/clientCookie.js"
29
+ export { sendRequest as sendRequest } from "./src/utils/request.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hamzus-ui",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "svelte": "index.js",
@@ -4,6 +4,7 @@
4
4
  export let label = '';
5
5
  export let disabled = false;
6
6
  export let fullWidth = false;
7
+ export let spaceBetween = false;
7
8
  export let checked = false;
8
9
  export let direction = 'ltr';
9
10
  export let onChange = null;
@@ -22,7 +23,7 @@
22
23
  }
23
24
  </script>
24
25
 
25
- <label class="checkbox-label {disabled ? 'disabled' : ''} {fullWidth ? 'full-width' : ''}">
26
+ <label class="checkbox-label {disabled ? 'disabled' : ''} {fullWidth ? 'full-width' : ''} {spaceBetween ? 'space-between' : ''}">
26
27
  {#if label && direction === 'ltr'}
27
28
  <h4>{label}</h4>
28
29
  {/if}
@@ -54,6 +55,8 @@
54
55
  }
55
56
  .checkbox-label.full-width {
56
57
  width: 100%;
58
+ }
59
+ .checkbox-label.space-between {
57
60
  justify-content: space-between;
58
61
  }
59
62
  .checkbox-box {
@@ -13,8 +13,8 @@
13
13
 
14
14
  function lintCode(text) {
15
15
  // text = text.replace(/ /g, '\t');
16
- text = text.replace(/"([^"]*)":/g, '<span>"$1"</span> <span class="syntax">:</span>');
17
- text = text.replace(/(,)\n/g, '<span class="syntax">$1</span>\n');
16
+ // text = text.replace(/"([^"]*)":/g, '<span>"$1"</span> <span class="syntax">:</span>');
17
+ // text = text.replace(/(,)\n/g, '<span class="syntax">$1</span>\n');
18
18
 
19
19
  let tempText = text.split('\n');
20
20
  lines = tempText;
@@ -32,7 +32,7 @@
32
32
  </svg>
33
33
  {/if}
34
34
  </IconButton>
35
- <TinyScrollArea>
35
+ <TinyScrollArea style="justify-content:center;">
36
36
  <h4 class="code" style="text-align: left;">{displayedText}</h4>
37
37
  </TinyScrollArea>
38
38
  </div>
@@ -0,0 +1,217 @@
1
+ <script>
2
+ import { onMount } from 'svelte';
3
+
4
+ export let afterSubmit = () => {};
5
+ export let url = undefined;
6
+ export let path = '/';
7
+ export let sending = false;
8
+ export let style = '';
9
+ export let avoidAutoMessage = false;
10
+ export let successMessage = null;
11
+ export let errorMessage = null;
12
+ export let includeToken = true;
13
+ export let appendData = undefined
14
+ export let alterData = undefined
15
+ export let preventQuit = true
16
+
17
+ import { getCookie } from './clientCookie';
18
+ import { beforeNavigate } from '$app/navigation';
19
+ import { config } from '../../../hamzus.config';
20
+
21
+ let form;
22
+ let isFormDirty = false;
23
+
24
+ onMount(() => {
25
+ window.addEventListener('beforeunload', handlePreventExit);
26
+
27
+ return () => {
28
+ window.removeEventListener('beforeunload', handlePreventExit);
29
+ };
30
+ });
31
+ function handleLinkClick(event) {
32
+ if (isFormDirty && preventQuit) {
33
+ const confirmLeave = confirm(
34
+ 'Vos modifications pourraient être perdues. Êtes-vous sûr de vouloir quitter cette page ?'
35
+ );
36
+ if (!confirmLeave) {
37
+ }
38
+ }
39
+ }
40
+
41
+ function handleSetFormDirty() {
42
+ isFormDirty = true;
43
+ }
44
+
45
+ function handlePreventExit(event) {
46
+ if (isFormDirty && preventQuit) {
47
+ event.preventDefault();
48
+ // Certains navigateurs exigent que la propriété returnValue soit définie
49
+ event.returnValue =
50
+ 'Vos modifications pourraient être perdues. Êtes-vous sûr de vouloir quitter cette page ?';
51
+ return 'Vos modifications pourraient être perdues. Êtes-vous sûr de vouloir quitter cette page ?';
52
+ }
53
+ }
54
+
55
+ beforeNavigate(({ cancel }) => {
56
+ if (isFormDirty && preventQuit) {
57
+ let confiramtion = confirm("Vos modifications pourraient être perdues. Êtes-vous sûr de vouloir quitter cette page ?")
58
+ if (!confiramtion) {
59
+ cancel();
60
+ }
61
+ }
62
+ });
63
+
64
+ function handleSubmit(event) {
65
+ if (sending) {
66
+ return
67
+ }
68
+
69
+ sending = true;
70
+
71
+ let formData = new FormData(event.target);
72
+
73
+ // inclure le token
74
+ if (includeToken) {
75
+ const token = getCookie(config['sessionTokenName']);
76
+ formData.append(config['sessionTokenName'], token);
77
+ }
78
+
79
+ // inclure les données
80
+ if (appendData !== undefined) {
81
+ let moreData = appendData();
82
+
83
+ for (const key in moreData) {
84
+ formData.append(key, moreData[key])
85
+ }
86
+ }
87
+
88
+ // alterer si possible les donné
89
+ if (alterData !== undefined) {
90
+ const alteredData = alterData(Object.fromEntries(formData.entries())) // passer les données du formulaire acutelle en parametre
91
+
92
+ formData = new FormData();
93
+
94
+ for (const key in alteredData) {
95
+ formData.append(key, alteredData[key])
96
+ }
97
+ }
98
+
99
+
100
+ // Création de la requête XHR
101
+ var scriptPath = (url || config['url']) + path;
102
+ const xhr = new XMLHttpRequest();
103
+ xhr.open('POST', scriptPath, true);
104
+
105
+ xhr.onload = function () {
106
+ sending = false;
107
+
108
+ if (form.querySelectorAll(`input.invalid`).length > 0) {
109
+ const inputs = form.querySelectorAll(`input.invalid`);
110
+ for (const input of inputs) {
111
+ input.classList.remove('invalid');
112
+ }
113
+ }
114
+
115
+ try {
116
+ const response = JSON.parse(xhr.responseText);
117
+ if (response.statusType == 'success') {
118
+ // desaactiver le invcalid
119
+ isFormDirty = false
120
+ errorMessage = null;
121
+ if (!avoidAutoMessage) {
122
+ successMessage = response.message;
123
+ setInterval(() => {
124
+ successMessage = null;
125
+ }, 5000);
126
+ }
127
+ } else {
128
+ successMessage = null;
129
+ if (!avoidAutoMessage) {
130
+ errorMessage = response.message;
131
+ }
132
+
133
+ // verifier si il y a des input manquant
134
+ if (response.requiredField && response.requiredField.length > 0) {
135
+ for (const inputName of response.requiredField) {
136
+ if (form.querySelector(`input[name="${inputName}"]`)) {
137
+ const input = form.querySelector(`input[name="${inputName}"]`);
138
+ input.classList.add('invalid');
139
+ }
140
+ }
141
+ }
142
+ if (response.invalidField && response.invalidField.length > 0) {
143
+ for (const inputName of response.invalidField) {
144
+ if (form.querySelector(`input[name="${inputName}"]`)) {
145
+ const input = form.querySelector(`input[name="${inputName}"]`);
146
+ input.classList.add('invalid');
147
+ }
148
+ }
149
+ }
150
+ }
151
+ } catch (error) {
152
+ console.error(error);
153
+ return;
154
+ }
155
+
156
+ if (afterSubmit) {
157
+ afterSubmit(JSON.parse(xhr.responseText));
158
+ }
159
+ };
160
+
161
+ xhr.onerror = function () {
162
+ sending = false
163
+ };
164
+
165
+ // Envoi des données sous forme JSON
166
+ xhr.send(formData);
167
+ }
168
+ </script>
169
+
170
+ <form
171
+ {style}
172
+ on:input={handleSetFormDirty}
173
+ bind:this={form}
174
+ method="POST"
175
+ on:submit|preventDefault={handleSubmit}
176
+ >
177
+ <slot></slot>
178
+ <div class="buttons">
179
+ <slot name="button" />
180
+ </div>
181
+ <slot name="down-form" />
182
+ {#if successMessage}
183
+ <div class="success p">{successMessage}</div>
184
+ {/if}
185
+ {#if errorMessage}
186
+ <div class="error p">{errorMessage}</div>
187
+ {/if}
188
+ </form>
189
+
190
+ <style>
191
+ form {
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: 1rem;
195
+ }
196
+ .success {
197
+ width: 100%;
198
+ padding: 12px;
199
+ background-color: var(--green-b);
200
+ color: var(--green);
201
+ border-radius: 12px;
202
+ white-space: pre-wrap;
203
+ }
204
+ .error {
205
+ width: 100%;
206
+ padding: 12px;
207
+ background-color: var(--red-b);
208
+ color: var(--red);
209
+ border-radius: 12px;
210
+ white-space: pre-wrap;
211
+ }
212
+ .buttons {
213
+ display: flex;
214
+ width: 100%;
215
+ justify-content: end;
216
+ }
217
+ </style>
@@ -0,0 +1,18 @@
1
+
2
+ export function getCookie(cookieName) {
3
+
4
+ var cookies = document.cookie.split(';');
5
+
6
+ for (var i = 0; i < cookies.length; i++) {
7
+ var cookie = cookies[i].trim();
8
+
9
+ // Vérifie si le nom du cookie correspond à celui recherché
10
+ if (cookie.indexOf(cookieName + '=') === 0) {
11
+ // Retourne la valeur du cookie
12
+ return cookie.substring(cookieName.length + 1);
13
+ }
14
+ }
15
+
16
+ // Retourne null si le cookie n'est pas trouvé
17
+ return '';
18
+ }
@@ -6,6 +6,7 @@
6
6
  export let desabled = false
7
7
  export let onClick = undefined
8
8
  export let avoidRipple = false;
9
+ export let type = "button"
9
10
  // local var
10
11
  let button
11
12
  // functions
@@ -40,7 +41,7 @@
40
41
  }
41
42
  </script>
42
43
 
43
- <button bind:this={button} on:click={handleClick} class="button h4" class:desabled class:loading {...$$restProps}>
44
+ <button {type} bind:this={button} on:click={handleClick} class="button h4" class:desabled class:loading {...$$restProps}>
44
45
  <slot/>
45
46
  {#if loading}
46
47
  <div class="loader">
@@ -16,6 +16,7 @@
16
16
  export let hasChanged = false;
17
17
  export let disabled = false;
18
18
  export let required = false;
19
+ export let fullWidth = false;
19
20
  export let type = '';
20
21
  export let style = '';
21
22
  export let placeholder = '';
@@ -88,7 +89,7 @@
88
89
 
89
90
  <div
90
91
  class="input-container {variant == 'default' ? variant : ''} {className}"
91
- {style}
92
+ style={(fullWidth ? "width:100%;" : "") + style}
92
93
  {...$$restProps}
93
94
  >
94
95
  <label
@@ -0,0 +1,167 @@
1
+ <script>
2
+ import { onMount } from 'svelte';
3
+
4
+ export let size = 100;
5
+ export let thickness = 10;
6
+ export let progress = 100; // 0–100
7
+ export let gapPixels = 4; // Écart visuel minimal
8
+ export let color = null;
9
+ export let colorPercentBased = {
10
+ 0: 'var(--red)',
11
+ 40: 'var(--orange)',
12
+ 60: 'var(--green)'
13
+ };
14
+ export let bgColor = 'var(--bg-2)';
15
+ export let showLabel = false;
16
+ export let animated = true;
17
+ export let linecap = 'round';
18
+
19
+ const r = (size - thickness) / 2;
20
+ const cx = size / 2;
21
+ const cy = size / 2;
22
+ const circumference = 2 * Math.PI * r;
23
+
24
+ let dynamicColor = color === null;
25
+ let copyColor = dynamicColor
26
+ ? animated
27
+ ? colorPercentBased[0]
28
+ : getCorrectColor(progress)
29
+ : color;
30
+ let copyProgress = animated ? 0 : progress === 100 ? 99.99 : progress;
31
+
32
+ // Fonction ease-out (courbe cubic)
33
+ function easeOutCubic(t) {
34
+ return 1 - Math.pow(1 - t, 3);
35
+ }
36
+
37
+ onMount(() => {
38
+ if (!animated) return;
39
+
40
+ const duration = 800; // en ms
41
+ const start = performance.now();
42
+
43
+ function animate(now) {
44
+ const elapsed = now - start;
45
+ const t = Math.min(elapsed / duration, 1); // clamp entre 0 et 1
46
+
47
+ const eased = easeOutCubic(t);
48
+
49
+ let newValue = Math.round(progress * eased);
50
+
51
+ copyProgress = newValue === 100 ? 99.99 : newValue;
52
+
53
+ // determiner la couleur si il y a dynamic color
54
+ if (dynamicColor) {
55
+ let finalColor = colorPercentBased[0];
56
+ for (const step in colorPercentBased) {
57
+ const color = colorPercentBased[step];
58
+
59
+ if (copyProgress > step) {
60
+ finalColor = color;
61
+ continue;
62
+ }
63
+
64
+ break;
65
+ }
66
+
67
+ copyColor = finalColor;
68
+ }
69
+
70
+ if (t < 1) {
71
+ requestAnimationFrame(animate);
72
+ }
73
+ }
74
+
75
+ requestAnimationFrame(animate);
76
+ });
77
+
78
+ function getCorrectColor(progress) {
79
+ let finalColor = colorPercentBased[0];
80
+ for (const step in colorPercentBased) {
81
+ const color = colorPercentBased[step];
82
+
83
+ if (progress > step) {
84
+ finalColor = color;
85
+ continue;
86
+ }
87
+
88
+ break;
89
+ }
90
+
91
+ return finalColor;
92
+ }
93
+
94
+ // Progrès en angle
95
+ $: progressAngle = (copyProgress / 100) * 360;
96
+
97
+ // ➕ Compensation du stroke-linecap (ajoute une demi-thickness de chaque côté de l'arc)
98
+ const capVisualOverlap = thickness; // total débordement = thickness (2 * thickness/2)
99
+
100
+ $: totalGapPx = gapPixels + capVisualOverlap;
101
+ $: gapAngle = (totalGapPx / circumference) * 360;
102
+
103
+ $: startAngleProgress = -90;
104
+ $: endAngleProgress = startAngleProgress + progressAngle;
105
+
106
+ $: startAngleBg = endAngleProgress + gapAngle;
107
+ $: endAngleBg = 270 - gapAngle;
108
+
109
+ function polarToCartesian(cx, cy, r, angleDeg) {
110
+ const angleRad = (angleDeg * Math.PI) / 180;
111
+ return {
112
+ x: cx + r * Math.cos(angleRad),
113
+ y: cy + r * Math.sin(angleRad)
114
+ };
115
+ }
116
+
117
+ function describeArc(cx, cy, r, startAngle, endAngle) {
118
+ const start = polarToCartesian(cx, cy, r, endAngle);
119
+ const end = polarToCartesian(cx, cy, r, startAngle);
120
+ const largeArcFlag = endAngle - startAngle > 180 ? 1 : 0;
121
+
122
+ return ['M', start.x, start.y, 'A', r, r, 0, largeArcFlag, 0, end.x, end.y].join(' ');
123
+ }
124
+ </script>
125
+
126
+ <svg class="progress-circle" width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
127
+ <!-- Arc de progression -->
128
+ <path
129
+ d={describeArc(cx, cy, r, startAngleProgress, endAngleProgress)}
130
+ stroke={copyColor}
131
+ stroke-width={thickness}
132
+ fill="none"
133
+ stroke-linecap={linecap}
134
+ />
135
+
136
+ {#if showLabel}
137
+ <text
138
+ x="50%"
139
+ y="50%"
140
+ text-anchor="middle"
141
+ dominant-baseline="central"
142
+ fill={copyColor}
143
+ style="font-size: {size / 3.5}px;"
144
+ class="h4">{Math.round(copyProgress)}%</text
145
+ >
146
+ {/if}
147
+
148
+ <!-- Arc de fond, après le progress + gap -->
149
+ {#if copyProgress < 96}
150
+ <path
151
+ d={describeArc(cx, cy, r, startAngleBg, endAngleBg)}
152
+ stroke={bgColor}
153
+ stroke-width={thickness}
154
+ fill="none"
155
+ stroke-linecap={linecap}
156
+ />
157
+ {/if}
158
+ </svg>
159
+
160
+ <style>
161
+ .progress-circle path {
162
+ transition: stroke 0.3s ease-out;
163
+ }
164
+ .progress-circle text {
165
+ transition: color 0.3s ease-out;
166
+ }
167
+ </style>
@@ -101,8 +101,8 @@
101
101
  }
102
102
 
103
103
  html {
104
- width: 100vw;
105
- height: 100vh;
104
+ width: 100dvw;
105
+ height: 100dvh;
106
106
  overflow: hidden;
107
107
  scroll-behavior: smooth;
108
108
  background: var(--bg-1);