canvasframework 0.3.13 → 0.3.15
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/README.md +721 -2
- package/components/CircularProgress.js +213 -29
- package/core/CanvasFramework.js +1 -0
- package/core/Component.js +90 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/utils/CryptoManager.js +303 -0
package/README.md
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
<img width="1536" height="1024" alt="image" src="https://github.com/user-attachments/assets/feae14e9-8d68-41f9-9e33-e444f1a6f360" />
|
|
1
|
+
<img width="1536" height="1024" alt="image" src="https://github.com/user-attachments/assets/feae14e9-8d68-41f9-9e33-e444f1a6f360" />
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
# Canvas UI Engine (
|
|
4
|
+
# Canvas UI Engine (UI engine inspired by Flutter, built for the Web)
|
|
5
5
|
|
|
6
6
|
> **Canvas-based UI Engine for Mobile & Embedded Apps**
|
|
7
7
|
> A high-performance UI engine rendered with Canvas/WebGL, running inside a WebView runtime (Capacitor / Cordova), without DOM, HTML or CSS.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
# Installation
|
|
12
|
+
- npm install canvasframework
|
|
13
|
+
- npm install -D vite
|
|
14
|
+
|
|
15
|
+
# add in package.json
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "vite",
|
|
18
|
+
"build": "vite build"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# launch
|
|
22
|
+
npm run dev
|
|
23
|
+
---
|
|
24
|
+
|
|
11
25
|
## 🚀 Overview
|
|
12
26
|
|
|
13
27
|
**Canvas UI Engine** is a **low-level UI engine** that renders the entire user interface using **Canvas 2D or WebGL**, instead of the DOM.
|
|
@@ -549,6 +563,711 @@ app.add(screen);
|
|
|
549
563
|
screen.layoutRecursive();
|
|
550
564
|
```
|
|
551
565
|
|
|
566
|
+
## UIBuilder
|
|
567
|
+
with UIBuilder you can build your interface with more simplicity
|
|
568
|
+
|
|
569
|
+
```
|
|
570
|
+
import { CanvasFramework, Column, ui, createRef, Table, Divider, MorphingFAB, PasswordInput, InputTags, InputDatalist, SpeedDialFAB, FAB, FileUpload, OpenStreetMap, SignaturePad, TreeView, SearchInput, ContextMenu, BottomNavigationBar, Card, View, RadioButton, Dialog, Checkbox, PullToRefresh, ProgressBar, AppBar, Skeleton, Drawer, Text, Button, Input, Slider, Select, Switch } from './canvas-framework/index.js';
|
|
571
|
+
|
|
572
|
+
const app = new CanvasFramework('app-canvas',{
|
|
573
|
+
useWebGL: true,
|
|
574
|
+
showFps: true,
|
|
575
|
+
debug: true,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
app.useWebGL = true;
|
|
579
|
+
|
|
580
|
+
// Route principale
|
|
581
|
+
app.route('/', (framework) => {
|
|
582
|
+
const platform = framework.platform === 'material' ? 'Material Design' : 'Cupertino (iOS)';
|
|
583
|
+
|
|
584
|
+
if (framework.useWebGL) {
|
|
585
|
+
console.log("✅ WebGL est activé");
|
|
586
|
+
} else {
|
|
587
|
+
console.log("⚠️ WebGL non disponible, fallback en Canvas 2D");
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ✅ Créer une ref pour le texte du slider
|
|
591
|
+
const sliderValueTextRef = createRef();
|
|
592
|
+
|
|
593
|
+
ui.app(
|
|
594
|
+
ui.Column({ x: 0, y: 0, spacing: 0 }, [
|
|
595
|
+
// Titre
|
|
596
|
+
ui.Text({
|
|
597
|
+
x: 20,
|
|
598
|
+
y: 50,
|
|
599
|
+
width: framework.width - 40,
|
|
600
|
+
text: `Canvas Framework - ${platform}`,
|
|
601
|
+
fontSize: 24,
|
|
602
|
+
bold: true,
|
|
603
|
+
align: 'center'
|
|
604
|
+
}),
|
|
605
|
+
|
|
606
|
+
// Bouton Toast
|
|
607
|
+
ui.Button({
|
|
608
|
+
x: framework.width / 2 - 100,
|
|
609
|
+
y: 120,
|
|
610
|
+
width: 200,
|
|
611
|
+
height: 50,
|
|
612
|
+
text: 'Afficher Toast',
|
|
613
|
+
onClick: () => {
|
|
614
|
+
framework.showToast('Toast affiché avec succès!', 2000);
|
|
615
|
+
}
|
|
616
|
+
}),
|
|
617
|
+
|
|
618
|
+
// Input
|
|
619
|
+
ui.Input({
|
|
620
|
+
x: 20,
|
|
621
|
+
y: 200,
|
|
622
|
+
width: framework.width - 40,
|
|
623
|
+
height: 50,
|
|
624
|
+
placeholder: 'Entrez du texte...'
|
|
625
|
+
}),
|
|
626
|
+
|
|
627
|
+
// Label Slider avec ref
|
|
628
|
+
ui.Text({
|
|
629
|
+
ref: sliderValueTextRef, // ✅ Utiliser ref au lieu de onMount
|
|
630
|
+
x: 20,
|
|
631
|
+
y: 280,
|
|
632
|
+
width: framework.width - 40,
|
|
633
|
+
text: 'Valeur: 50',
|
|
634
|
+
fontSize: 14,
|
|
635
|
+
color: '#666666'
|
|
636
|
+
}),
|
|
637
|
+
|
|
638
|
+
// Slider
|
|
639
|
+
ui.Slider({
|
|
640
|
+
x: 20,
|
|
641
|
+
y: 310,
|
|
642
|
+
width: framework.width - 40,
|
|
643
|
+
height: 40,
|
|
644
|
+
value: 50,
|
|
645
|
+
onChange: (value) => {
|
|
646
|
+
if (sliderValueTextRef.current) {
|
|
647
|
+
sliderValueTextRef.current.text = `Valeur: ${Math.round(value)}`;
|
|
648
|
+
sliderValueTextRef.current.markDirty();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}),
|
|
652
|
+
|
|
653
|
+
// Label Select
|
|
654
|
+
ui.Text({
|
|
655
|
+
x: 20,
|
|
656
|
+
y: 380,
|
|
657
|
+
width: framework.width - 40,
|
|
658
|
+
text: 'Menu déroulant:',
|
|
659
|
+
fontSize: 14,
|
|
660
|
+
color: '#666666'
|
|
661
|
+
}),
|
|
662
|
+
|
|
663
|
+
// Select
|
|
664
|
+
ui.Select({
|
|
665
|
+
x: 20,
|
|
666
|
+
y: 410,
|
|
667
|
+
width: framework.width - 40,
|
|
668
|
+
height: 50,
|
|
669
|
+
options: ['Option 1', 'Option 2', 'Option 3'],
|
|
670
|
+
selectedIndex: 0,
|
|
671
|
+
onChange: (selectedOption, selectedIndex) => {
|
|
672
|
+
console.log(`Option sélectionnée : ${selectedOption} (index: ${selectedIndex})`);
|
|
673
|
+
}
|
|
674
|
+
}),
|
|
675
|
+
|
|
676
|
+
// Label Switch
|
|
677
|
+
ui.Text({
|
|
678
|
+
x: 20,
|
|
679
|
+
y: 490,
|
|
680
|
+
width: framework.width - 100,
|
|
681
|
+
text: 'Activer notifications',
|
|
682
|
+
fontSize: 16
|
|
683
|
+
}),
|
|
684
|
+
|
|
685
|
+
// Switch
|
|
686
|
+
ui.Switch({
|
|
687
|
+
x: framework.width - 71,
|
|
688
|
+
y: 485,
|
|
689
|
+
checked: false,
|
|
690
|
+
onChange: (checked) => {
|
|
691
|
+
framework.showToast(checked ? 'Notifications activées' : 'Notifications désactivées');
|
|
692
|
+
}
|
|
693
|
+
}),
|
|
694
|
+
|
|
695
|
+
// Bouton Page 2
|
|
696
|
+
ui.Button({
|
|
697
|
+
x: 20,
|
|
698
|
+
y: 550,
|
|
699
|
+
type: 'outlined',
|
|
700
|
+
shape: 'square',
|
|
701
|
+
width: framework.width / 2 - 30,
|
|
702
|
+
height: 50,
|
|
703
|
+
text: 'Page 2 →',
|
|
704
|
+
bgColor: framework.platform === 'material' ? '#03DAC6' : '#FF9500',
|
|
705
|
+
onClick: () => {
|
|
706
|
+
framework.navigate('/page2', { transition: 'fade' });
|
|
707
|
+
}
|
|
708
|
+
}),
|
|
709
|
+
|
|
710
|
+
// Bouton Tout Tester
|
|
711
|
+
ui.Button({
|
|
712
|
+
x: framework.width / 2 + 10,
|
|
713
|
+
y: 550,
|
|
714
|
+
width: framework.width / 2 - 30,
|
|
715
|
+
height: 50,
|
|
716
|
+
type: 'filled',
|
|
717
|
+
shape: 'rounded',
|
|
718
|
+
text: 'Tout Tester →',
|
|
719
|
+
bgColor: framework.platform === 'material' ? '#FF9800' : '#34C759',
|
|
720
|
+
onClick: () => {
|
|
721
|
+
framework.navigate('/test', { transition: 'slide' });
|
|
722
|
+
}
|
|
723
|
+
})
|
|
724
|
+
])
|
|
725
|
+
).mount(framework);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// Page 2 avec Drawer et Navigation
|
|
729
|
+
app.route('/page2', (framework) => {
|
|
730
|
+
// ✅ Créer une ref pour le drawer
|
|
731
|
+
const drawerRef = createRef();
|
|
732
|
+
|
|
733
|
+
ui.app(
|
|
734
|
+
ui.Column({ x: 0, y: 0, spacing: 0 }, [
|
|
735
|
+
|
|
736
|
+
// Drawer avec ref
|
|
737
|
+
ui.Drawer({
|
|
738
|
+
ref: drawerRef, // ✅ Utiliser ref
|
|
739
|
+
header: { title: 'Mon App' },
|
|
740
|
+
items: [
|
|
741
|
+
{ icon: '🏠', label: 'Accueil' },
|
|
742
|
+
{ icon: '⚙️', label: 'Paramètres' },
|
|
743
|
+
{ icon: '❤️', label: 'Favoris', divider: true },
|
|
744
|
+
{ icon: '👤', label: 'Profil' }
|
|
745
|
+
],
|
|
746
|
+
onItemClick: (index, item) => {
|
|
747
|
+
framework.showToast(`${item.label} cliqué`);
|
|
748
|
+
framework.navigate('/', { transition: 'none' });
|
|
749
|
+
}
|
|
750
|
+
}),
|
|
751
|
+
|
|
752
|
+
// AppBar (sera au-dessus)
|
|
753
|
+
ui.AppBar({
|
|
754
|
+
title: 'Accueil',
|
|
755
|
+
leftIcon: 'menu',
|
|
756
|
+
rightIcon: 'search',
|
|
757
|
+
onLeftClick: () => {
|
|
758
|
+
if (drawerRef.current) drawerRef.current.open();
|
|
759
|
+
},
|
|
760
|
+
onRightClick: () => {
|
|
761
|
+
framework.showToast('Recherche');
|
|
762
|
+
}
|
|
763
|
+
}),
|
|
764
|
+
|
|
765
|
+
// PasswordInput
|
|
766
|
+
ui.PasswordInput({
|
|
767
|
+
placeholder: 'Entrez votre mot de passe',
|
|
768
|
+
value: '',
|
|
769
|
+
fontSize: 16,
|
|
770
|
+
x: 20,
|
|
771
|
+
y: 120,
|
|
772
|
+
width: 300,
|
|
773
|
+
height: 40,
|
|
774
|
+
maskChar: '•',
|
|
775
|
+
showPassword: false,
|
|
776
|
+
onFocus: () => {
|
|
777
|
+
console.log('PasswordInput focus');
|
|
778
|
+
},
|
|
779
|
+
onBlur: () => {
|
|
780
|
+
console.log('PasswordInput blur');
|
|
781
|
+
}
|
|
782
|
+
}),
|
|
783
|
+
|
|
784
|
+
// InputTags
|
|
785
|
+
ui.InputTags({
|
|
786
|
+
placeholder: 'Ajouter des tags...',
|
|
787
|
+
tags: ['javascript', 'canvas'],
|
|
788
|
+
x: 20,
|
|
789
|
+
y: 160,
|
|
790
|
+
width: 300,
|
|
791
|
+
height: 50,
|
|
792
|
+
tagColor: '#E3F2FD',
|
|
793
|
+
tagTextColor: '#1565C0',
|
|
794
|
+
deleteButtonColor: '#1565C0',
|
|
795
|
+
onTagAdd: (tag, allTags) => {
|
|
796
|
+
console.log('Tag ajouté:', tag, 'Tags:', allTags);
|
|
797
|
+
},
|
|
798
|
+
onTagRemove: (tag, allTags) => {
|
|
799
|
+
console.log('Tag supprimé:', tag, 'Tags:', allTags);
|
|
800
|
+
}
|
|
801
|
+
}),
|
|
802
|
+
|
|
803
|
+
// InputDatalist
|
|
804
|
+
ui.InputDatalist({
|
|
805
|
+
placeholder: 'Sélectionnez un pays...',
|
|
806
|
+
value: '',
|
|
807
|
+
options: [
|
|
808
|
+
'France', 'Allemagne', 'Espagne', 'Italie', 'Portugal',
|
|
809
|
+
'Belgique', 'Suisse', 'Canada', 'États-Unis', 'Japon',
|
|
810
|
+
'Chine', 'Corée du Sud', 'Australie', 'Brésil', 'Mexique'
|
|
811
|
+
],
|
|
812
|
+
x: 50,
|
|
813
|
+
y: 210,
|
|
814
|
+
width: 300,
|
|
815
|
+
height: 40,
|
|
816
|
+
maxDropdownItems: 8,
|
|
817
|
+
dropdownBackground: '#FFFFFF',
|
|
818
|
+
hoverBackground: '#F0F0F0',
|
|
819
|
+
selectedBackground: '#E3F2FD',
|
|
820
|
+
borderColor: '#CCCCCC',
|
|
821
|
+
onSelect: (selectedValue) => {
|
|
822
|
+
console.log('Option sélectionnée:', selectedValue);
|
|
823
|
+
},
|
|
824
|
+
onInput: (currentValue) => {
|
|
825
|
+
console.log('Valeur en cours:', currentValue);
|
|
826
|
+
}
|
|
827
|
+
}),
|
|
828
|
+
|
|
829
|
+
// BottomNavigationBar
|
|
830
|
+
ui.BottomNavigationBar({
|
|
831
|
+
items: [
|
|
832
|
+
{ icon: 'home', label: 'Home' },
|
|
833
|
+
{ icon: 'search', label: 'Search' },
|
|
834
|
+
{ icon: 'favorite', label: 'Favorites' },
|
|
835
|
+
{ icon: 'person', label: 'Profile' },
|
|
836
|
+
{ icon: 'settings', label: 'Settings' }
|
|
837
|
+
],
|
|
838
|
+
selectedIndex: 0,
|
|
839
|
+
selectedColor: '#6200EE',
|
|
840
|
+
onChange: (index, item) => {
|
|
841
|
+
console.log(`Tab changed to: ${item.label} (index ${index})`);
|
|
842
|
+
switch(index) {
|
|
843
|
+
case 0: framework.navigate('home'); break;
|
|
844
|
+
case 1: framework.navigate('search'); break;
|
|
845
|
+
case 2: framework.navigate('favorites'); break;
|
|
846
|
+
case 3: framework.navigate('profile'); break;
|
|
847
|
+
case 4: framework.navigate('settings'); break;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
})
|
|
851
|
+
|
|
852
|
+
])
|
|
853
|
+
).mount(framework);
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
// Page de test complète
|
|
857
|
+
app.route('/test', (framework) => {
|
|
858
|
+
// ✅ Créer toutes les refs nécessaires
|
|
859
|
+
const progressBarRef = createRef();
|
|
860
|
+
const testInputRef = createRef();
|
|
861
|
+
const sliderDisplayRef = createRef();
|
|
862
|
+
const testSwitchRef = createRef();
|
|
863
|
+
|
|
864
|
+
let yPosition = 80;
|
|
865
|
+
|
|
866
|
+
ui.app(
|
|
867
|
+
ui.Column({ x: 0, y: 0, spacing: 0 }, [
|
|
868
|
+
|
|
869
|
+
// AppBar de retour (fixe)
|
|
870
|
+
ui.AppBar({
|
|
871
|
+
title: 'Test Complet',
|
|
872
|
+
leftIcon: 'back',
|
|
873
|
+
onLeftClick: () => {
|
|
874
|
+
framework.navigate('/', { transition: 'slide' });
|
|
875
|
+
}
|
|
876
|
+
}),
|
|
877
|
+
|
|
878
|
+
// Titre principal
|
|
879
|
+
ui.Text({
|
|
880
|
+
x: 20,
|
|
881
|
+
y: yPosition,
|
|
882
|
+
width: framework.width - 40,
|
|
883
|
+
text: 'Test de tous les composants',
|
|
884
|
+
fontSize: 24,
|
|
885
|
+
bold: true,
|
|
886
|
+
align: 'center'
|
|
887
|
+
}),
|
|
888
|
+
|
|
889
|
+
// 1. ProgressBar Section
|
|
890
|
+
ui.Text({
|
|
891
|
+
x: 20,
|
|
892
|
+
y: yPosition + 60,
|
|
893
|
+
width: framework.width - 40,
|
|
894
|
+
text: '1. ProgressBar:',
|
|
895
|
+
fontSize: 18,
|
|
896
|
+
bold: true
|
|
897
|
+
}),
|
|
898
|
+
|
|
899
|
+
// ✅ ProgressBar avec ref - PLUS BESOIN de création manuelle !
|
|
900
|
+
ui.ProgressBar({
|
|
901
|
+
ref: progressBarRef,
|
|
902
|
+
x: 20,
|
|
903
|
+
y: yPosition + 90,
|
|
904
|
+
width: framework.width - 40,
|
|
905
|
+
progress: 30
|
|
906
|
+
}),
|
|
907
|
+
|
|
908
|
+
ui.Button({
|
|
909
|
+
x: 20,
|
|
910
|
+
y: yPosition + 140,
|
|
911
|
+
width: 150,
|
|
912
|
+
height: 40,
|
|
913
|
+
text: 'Augmenter',
|
|
914
|
+
onClick: () => {
|
|
915
|
+
if (progressBarRef.current) {
|
|
916
|
+
progressBarRef.current.progress = Math.min(100, progressBarRef.current.progress + 10);
|
|
917
|
+
progressBarRef.current.markDirty();
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}),
|
|
921
|
+
|
|
922
|
+
// 2. RadioButton Section
|
|
923
|
+
ui.Text({
|
|
924
|
+
x: 20,
|
|
925
|
+
y: yPosition + 200,
|
|
926
|
+
width: framework.width - 40,
|
|
927
|
+
text: '2. RadioButton (Groupe 1):',
|
|
928
|
+
fontSize: 18,
|
|
929
|
+
bold: true
|
|
930
|
+
}),
|
|
931
|
+
|
|
932
|
+
ui.RadioButton({
|
|
933
|
+
x: 40,
|
|
934
|
+
y: yPosition + 230,
|
|
935
|
+
group: 'groupe1',
|
|
936
|
+
label: 'Option A',
|
|
937
|
+
checked: true,
|
|
938
|
+
onChange: (checked) => {
|
|
939
|
+
if (checked) framework.showToast('Radio A sélectionné');
|
|
940
|
+
}
|
|
941
|
+
}),
|
|
942
|
+
|
|
943
|
+
ui.RadioButton({
|
|
944
|
+
x: 40,
|
|
945
|
+
y: yPosition + 270,
|
|
946
|
+
group: 'groupe1',
|
|
947
|
+
label: 'Option B',
|
|
948
|
+
onChange: (checked) => {
|
|
949
|
+
if (checked) framework.showToast('Radio B sélectionné');
|
|
950
|
+
}
|
|
951
|
+
}),
|
|
952
|
+
|
|
953
|
+
// 3. Checkbox Section
|
|
954
|
+
ui.Text({
|
|
955
|
+
x: 20,
|
|
956
|
+
y: yPosition + 330,
|
|
957
|
+
width: framework.width - 40,
|
|
958
|
+
text: '3. Checkbox:',
|
|
959
|
+
fontSize: 18,
|
|
960
|
+
bold: true
|
|
961
|
+
}),
|
|
962
|
+
|
|
963
|
+
ui.Checkbox({
|
|
964
|
+
x: 40,
|
|
965
|
+
y: yPosition + 360,
|
|
966
|
+
label: 'Accepter les termes',
|
|
967
|
+
checked: false,
|
|
968
|
+
onChange: (checked) => {
|
|
969
|
+
framework.showToast(checked ? 'Coché' : 'Décoché');
|
|
970
|
+
}
|
|
971
|
+
}),
|
|
972
|
+
|
|
973
|
+
// 4. Card Section
|
|
974
|
+
ui.Text({
|
|
975
|
+
x: 20,
|
|
976
|
+
y: yPosition + 420,
|
|
977
|
+
width: framework.width - 40,
|
|
978
|
+
text: '4. Card:',
|
|
979
|
+
fontSize: 18,
|
|
980
|
+
bold: true
|
|
981
|
+
}),
|
|
982
|
+
|
|
983
|
+
ui.Card({
|
|
984
|
+
x: 20,
|
|
985
|
+
y: yPosition + 450,
|
|
986
|
+
width: framework.width - 40,
|
|
987
|
+
height: 180,
|
|
988
|
+
padding: 16,
|
|
989
|
+
elevation: 4,
|
|
990
|
+
borderRadius: 8
|
|
991
|
+
}, [
|
|
992
|
+
ui.Text({
|
|
993
|
+
x: 0,
|
|
994
|
+
y: 0,
|
|
995
|
+
width: framework.width - 72,
|
|
996
|
+
text: 'Titre de la carte',
|
|
997
|
+
fontSize: 18,
|
|
998
|
+
bold: true
|
|
999
|
+
}),
|
|
1000
|
+
|
|
1001
|
+
ui.Text({
|
|
1002
|
+
x: 0,
|
|
1003
|
+
y: 40,
|
|
1004
|
+
width: framework.width - 72,
|
|
1005
|
+
maxWidth: framework.width - 72,
|
|
1006
|
+
text: 'Ceci est un exemple de texte à l\'intérieur d\'une carte. Le texte ne devrait plus déborder maintenant car il va à la ligne automatiquement quand il atteint la largeur maximale.',
|
|
1007
|
+
fontSize: 14,
|
|
1008
|
+
color: '#666666',
|
|
1009
|
+
wrap: true
|
|
1010
|
+
}),
|
|
1011
|
+
|
|
1012
|
+
ui.Button({
|
|
1013
|
+
x: 0,
|
|
1014
|
+
y: 120,
|
|
1015
|
+
width: 150,
|
|
1016
|
+
height: 40,
|
|
1017
|
+
text: 'Bouton dans Card',
|
|
1018
|
+
onClick: () => {
|
|
1019
|
+
framework.showToast('Bouton dans Card cliqué!');
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
]),
|
|
1023
|
+
|
|
1024
|
+
// 5. Dialog Section
|
|
1025
|
+
ui.Text({
|
|
1026
|
+
x: 20,
|
|
1027
|
+
y: yPosition + 650,
|
|
1028
|
+
width: framework.width - 40,
|
|
1029
|
+
text: '5. Dialog:',
|
|
1030
|
+
fontSize: 18,
|
|
1031
|
+
bold: true
|
|
1032
|
+
}),
|
|
1033
|
+
|
|
1034
|
+
ui.Button({
|
|
1035
|
+
x: 20,
|
|
1036
|
+
y: yPosition + 680,
|
|
1037
|
+
width: 200,
|
|
1038
|
+
height: 50,
|
|
1039
|
+
text: 'Afficher Dialog',
|
|
1040
|
+
onClick: () => {
|
|
1041
|
+
const dialog = new Dialog(framework, {
|
|
1042
|
+
title: 'Titre du Dialog',
|
|
1043
|
+
message: 'Ceci est un message de dialog. Voulez-vous continuer ?',
|
|
1044
|
+
buttons: ['Annuler', 'OK'],
|
|
1045
|
+
onButtonClick: (index, text) => {
|
|
1046
|
+
framework.showToast(`Bouton cliqué: ${text}`);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
framework.add(dialog);
|
|
1050
|
+
dialog.show();
|
|
1051
|
+
}
|
|
1052
|
+
}),
|
|
1053
|
+
|
|
1054
|
+
// 6. View Section
|
|
1055
|
+
ui.Text({
|
|
1056
|
+
x: 20,
|
|
1057
|
+
y: yPosition + 760,
|
|
1058
|
+
width: framework.width - 40,
|
|
1059
|
+
text: '6. View (Container):',
|
|
1060
|
+
fontSize: 18,
|
|
1061
|
+
bold: true
|
|
1062
|
+
}),
|
|
1063
|
+
|
|
1064
|
+
ui.View({
|
|
1065
|
+
x: 20,
|
|
1066
|
+
y: yPosition + 790,
|
|
1067
|
+
width: framework.width - 40,
|
|
1068
|
+
height: 200,
|
|
1069
|
+
padding: 20,
|
|
1070
|
+
gap: 10,
|
|
1071
|
+
direction: 'column',
|
|
1072
|
+
bgColor: '#F0F0F0',
|
|
1073
|
+
borderRadius: 8
|
|
1074
|
+
}, [
|
|
1075
|
+
ui.Text({
|
|
1076
|
+
width: framework.width - 80,
|
|
1077
|
+
text: 'Contenu dans un View',
|
|
1078
|
+
fontSize: 16,
|
|
1079
|
+
bold: true
|
|
1080
|
+
}),
|
|
1081
|
+
|
|
1082
|
+
ui.Button({
|
|
1083
|
+
width: 150,
|
|
1084
|
+
height: 40,
|
|
1085
|
+
text: 'Bouton dans View',
|
|
1086
|
+
onClick: () => {
|
|
1087
|
+
framework.showToast('Bouton dans View cliqué!');
|
|
1088
|
+
}
|
|
1089
|
+
}),
|
|
1090
|
+
|
|
1091
|
+
ui.Switch({
|
|
1092
|
+
checked: true,
|
|
1093
|
+
onChange: (checked) => {
|
|
1094
|
+
framework.showToast(`Switch dans View: ${checked ? 'ON' : 'OFF'}`);
|
|
1095
|
+
}
|
|
1096
|
+
})
|
|
1097
|
+
]),
|
|
1098
|
+
|
|
1099
|
+
// 7. ContextMenu Section
|
|
1100
|
+
ui.Text({
|
|
1101
|
+
x: 20,
|
|
1102
|
+
y: yPosition + 1020,
|
|
1103
|
+
width: framework.width - 40,
|
|
1104
|
+
text: '7. ContextMenu:',
|
|
1105
|
+
fontSize: 18,
|
|
1106
|
+
bold: true
|
|
1107
|
+
}),
|
|
1108
|
+
|
|
1109
|
+
ui.Button({
|
|
1110
|
+
x: 20,
|
|
1111
|
+
y: yPosition + 1050,
|
|
1112
|
+
width: 200,
|
|
1113
|
+
height: 50,
|
|
1114
|
+
text: 'Ouvrir Menu Contextuel',
|
|
1115
|
+
onClick: () => {
|
|
1116
|
+
const menu = new ContextMenu(framework, {
|
|
1117
|
+
x: 20,
|
|
1118
|
+
y: yPosition + 1110,
|
|
1119
|
+
width: 200,
|
|
1120
|
+
options: ['Option 1', 'Option 2', 'Option 3', 'Option 4'],
|
|
1121
|
+
onSelect: (index) => {
|
|
1122
|
+
framework.showToast(`Option ${index + 1} sélectionnée`);
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
framework.add(menu);
|
|
1126
|
+
}
|
|
1127
|
+
}),
|
|
1128
|
+
|
|
1129
|
+
// 8. Input Section
|
|
1130
|
+
ui.Text({
|
|
1131
|
+
x: 20,
|
|
1132
|
+
y: yPosition + 1130,
|
|
1133
|
+
width: framework.width - 40,
|
|
1134
|
+
text: '8. Input avec valeur:',
|
|
1135
|
+
fontSize: 18,
|
|
1136
|
+
bold: true
|
|
1137
|
+
}),
|
|
1138
|
+
|
|
1139
|
+
// ✅ Input avec ref
|
|
1140
|
+
ui.Input({
|
|
1141
|
+
ref: testInputRef,
|
|
1142
|
+
x: 20,
|
|
1143
|
+
y: yPosition + 1160,
|
|
1144
|
+
width: framework.width - 40,
|
|
1145
|
+
height: 50,
|
|
1146
|
+
placeholder: 'Tapez quelque chose...',
|
|
1147
|
+
value: ''
|
|
1148
|
+
}),
|
|
1149
|
+
|
|
1150
|
+
ui.Button({
|
|
1151
|
+
x: 20,
|
|
1152
|
+
y: yPosition + 1230,
|
|
1153
|
+
width: 200,
|
|
1154
|
+
height: 50,
|
|
1155
|
+
text: 'Afficher valeur',
|
|
1156
|
+
onClick: () => {
|
|
1157
|
+
const value = testInputRef.current?.value || '(vide)';
|
|
1158
|
+
framework.showToast(`Valeur: ${value}`);
|
|
1159
|
+
}
|
|
1160
|
+
}),
|
|
1161
|
+
|
|
1162
|
+
// 9. Select Section
|
|
1163
|
+
ui.Text({
|
|
1164
|
+
x: 20,
|
|
1165
|
+
y: yPosition + 1300,
|
|
1166
|
+
width: framework.width - 40,
|
|
1167
|
+
text: '9. Select avec callback:',
|
|
1168
|
+
fontSize: 18,
|
|
1169
|
+
bold: true
|
|
1170
|
+
}),
|
|
1171
|
+
|
|
1172
|
+
ui.Select({
|
|
1173
|
+
x: 20,
|
|
1174
|
+
y: yPosition + 1330,
|
|
1175
|
+
width: framework.width - 40,
|
|
1176
|
+
height: 50,
|
|
1177
|
+
options: ['Pomme', 'Banane', 'Orange', 'Fraise'],
|
|
1178
|
+
selectedIndex: 0,
|
|
1179
|
+
onChange: (value, index) => {
|
|
1180
|
+
framework.showToast(`Sélectionné: ${value} (index: ${index})`);
|
|
1181
|
+
}
|
|
1182
|
+
}),
|
|
1183
|
+
|
|
1184
|
+
// 10. Switch Section
|
|
1185
|
+
ui.Text({
|
|
1186
|
+
x: 20,
|
|
1187
|
+
y: yPosition + 1410,
|
|
1188
|
+
width: framework.width - 40,
|
|
1189
|
+
text: '10. Switch avec état:',
|
|
1190
|
+
fontSize: 18,
|
|
1191
|
+
bold: true
|
|
1192
|
+
}),
|
|
1193
|
+
|
|
1194
|
+
// ✅ Switch avec ref
|
|
1195
|
+
ui.Switch({
|
|
1196
|
+
ref: testSwitchRef,
|
|
1197
|
+
x: 20,
|
|
1198
|
+
y: yPosition + 1440,
|
|
1199
|
+
checked: false,
|
|
1200
|
+
onChange: (checked) => {
|
|
1201
|
+
framework.showToast(`Switch: ${checked ? 'ACTIVÉ' : 'DÉSACTIVÉ'}`);
|
|
1202
|
+
}
|
|
1203
|
+
}),
|
|
1204
|
+
|
|
1205
|
+
// 11. Slider Section
|
|
1206
|
+
ui.Text({
|
|
1207
|
+
x: 20,
|
|
1208
|
+
y: yPosition + 1500,
|
|
1209
|
+
width: framework.width - 40,
|
|
1210
|
+
text: '11. Slider avec valeur:',
|
|
1211
|
+
fontSize: 18,
|
|
1212
|
+
bold: true
|
|
1213
|
+
}),
|
|
1214
|
+
|
|
1215
|
+
// ✅ Text du slider avec ref
|
|
1216
|
+
ui.Text({
|
|
1217
|
+
ref: sliderDisplayRef,
|
|
1218
|
+
x: 20,
|
|
1219
|
+
y: yPosition + 1530,
|
|
1220
|
+
width: framework.width - 40,
|
|
1221
|
+
text: 'Valeur: 50',
|
|
1222
|
+
fontSize: 14
|
|
1223
|
+
}),
|
|
1224
|
+
|
|
1225
|
+
ui.Slider({
|
|
1226
|
+
x: 20,
|
|
1227
|
+
y: yPosition + 1560,
|
|
1228
|
+
width: framework.width - 40,
|
|
1229
|
+
height: 40,
|
|
1230
|
+
value: 50,
|
|
1231
|
+
onChange: (value) => {
|
|
1232
|
+
if (sliderDisplayRef.current) {
|
|
1233
|
+
sliderDisplayRef.current.text = `Valeur: ${Math.round(value)}`;
|
|
1234
|
+
sliderDisplayRef.current.markDirty();
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}),
|
|
1238
|
+
|
|
1239
|
+
// FAB flottant
|
|
1240
|
+
ui.FAB({
|
|
1241
|
+
icon: '+',
|
|
1242
|
+
variant: 'medium',
|
|
1243
|
+
x: framework.width - 56,
|
|
1244
|
+
y: framework.height - 56,
|
|
1245
|
+
bgColor: '#6750A4',
|
|
1246
|
+
onClick: () => {
|
|
1247
|
+
framework.showToast('FAB cliqué!');
|
|
1248
|
+
}
|
|
1249
|
+
}),
|
|
1250
|
+
|
|
1251
|
+
// Bouton pour remonter
|
|
1252
|
+
ui.Button({
|
|
1253
|
+
x: framework.width / 2 - 100,
|
|
1254
|
+
y: yPosition + 1640,
|
|
1255
|
+
width: 200,
|
|
1256
|
+
height: 50,
|
|
1257
|
+
text: '↑ Remonter ↑',
|
|
1258
|
+
onClick: () => {
|
|
1259
|
+
framework.scrollOffset = 0;
|
|
1260
|
+
}
|
|
1261
|
+
})
|
|
1262
|
+
|
|
1263
|
+
])
|
|
1264
|
+
).mount(framework);
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
// Lancer l'app
|
|
1268
|
+
app.navigate('/', { transition: 'none' });
|
|
1269
|
+
```
|
|
1270
|
+
|
|
552
1271
|
## 📄 License
|
|
553
1272
|
|
|
554
1273
|
MIT
|