diff --git a/power-apps/keyboard/README.md b/power-apps/keyboard/README.md
new file mode 100644
index 0000000..6817ec7
--- /dev/null
+++ b/power-apps/keyboard/README.md
@@ -0,0 +1,39 @@
+# Keyboard
+
+This custom PowerApps component provides a fully functional virtual keyboard supporting letters, numbers, and special characters. It offers seamless language switching between French and English, making it ideal for multilingual applications and touch-based interfaces. Designed for flexibility and ease of integration, it enhances user input across a wide range of scenarios.
+
+This component is designed to complement the default keyboard, especially on large tablets or touchscreen kiosks 🖥️. It lets you position the keyboard wherever you want 📍, adjust its size 📐, and even customize the keys and colors 🎨 to fit your needs.
+Flexibility and user experience first! 🚀
+
+
+
+
+
+
+## Authors
+
+Snippet|Author
+--------|---------
+Steve Bourdin | [GitHub](https://github.com/SteveBourdin) ([LinkedIn](https://www.linkedin.com/in/steve-bourdin-ab998762/) )
+
+## Minimal path to awesome
+
+Copy the provided **[YAML-file](./source/keyboard.yaml)** code into the Component section of your PowerApps project.
+Use the Default property of the component to set an initial text value.
+The current value typed using the keyboard is accessible via the component’s Value property.
+
+Once the component is placed on a canvas screen, to test it:
+- Add a text input control.
+- Set the Value property of the input to keyboard.Value.
+- Set the Default property of the keyboard component to the value of the input control (e.g., TextInput1.Text).
+
+
+## Code
+ **[YAML-file](./source/keyboard.yaml)**
+
+
+## Disclaimer
+
+**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
+
+
diff --git a/power-apps/keyboard/assets/keyboard.gif b/power-apps/keyboard/assets/keyboard.gif
new file mode 100644
index 0000000..68224b7
Binary files /dev/null and b/power-apps/keyboard/assets/keyboard.gif differ
diff --git a/power-apps/keyboard/assets/sample.json b/power-apps/keyboard/assets/sample.json
new file mode 100644
index 0000000..6f29dcb
--- /dev/null
+++ b/power-apps/keyboard/assets/sample.json
@@ -0,0 +1,52 @@
+[
+ {
+ "$schema": "https://developer.microsoft.com/en-us/json-schemas/pnp/samples/v1.0/metadata-schema.json",
+ "name": "pnp-powerplatform-snippets-keyboard",
+ "version": "1.0.0.0",
+ "source": "pnp",
+ "creationDateTime": "2025-04-29T00:00:00.000Z",
+ "updateDateTime": "2025-04-29T00:00:00.000Z",
+ "title": "Keyboard",
+ "shortDescription": "A keyboard for PowerApps",
+ "longDescription": [
+ "This custom PowerApps component provides a fully functional virtual keyboard supporting letters, numbers, and special characters. It offers seamless language switching between French and English, making it ideal for multilingual applications and touch-based interfaces. Designed for flexibility and ease of integration, it enhances user input across a wide range of scenarios."
+ ],
+ "url": "https://github.com/pnp/powerplatform-snippets/tree/main/power-apps/keyboard/",
+ "products": [
+ "Power Platform",
+ "Power Apps",
+ "powerplatform-snippets",
+ "power-apps-snippets"
+ ],
+ "tags": [
+ ],
+ "categories": [
+ ],
+ "metadata": [
+ {
+ "key": "Product",
+ "value": "Power Apps"
+ },
+ {
+ "key": "Type",
+ "value": "Snippet"
+ }
+ ],
+ "thumbnails": [
+ {
+ "type": "image",
+ "order": 100,
+ "url": "https://raw.githubusercontent.com/pnp/powerplatform-snippets/62453fde084d0904826ecec20b9f54bf05acee38/power-apps/keyboard/assets/keyboard.gif",
+ "alt": "Preview PNG"
+ }
+ ],
+ "authors": [
+ {
+ "gitHubAccount": "SteveBourdin",
+ "name": "Steve Bourdin",
+ "pictureUrl": "https://github.com/SteveBourdin.png"
+ }
+ ]
+ }
+
+]
diff --git a/power-apps/keyboard/source/keyboard.yaml b/power-apps/keyboard/source/keyboard.yaml
new file mode 100644
index 0000000..b7cc015
--- /dev/null
+++ b/power-apps/keyboard/source/keyboard.yaml
@@ -0,0 +1,480 @@
+ComponentDefinitions:
+ keyboard:
+ DefinitionType: CanvasComponent
+ CustomProperties:
+ Default:
+ PropertyKind: Input
+ DisplayName: Default
+ Description: Propriété personnalisée
+ DataType: Text
+ Default: ="Texte"
+ French:
+ PropertyKind: Input
+ DisplayName: French
+ Description: Propriété personnalisée
+ DataType: Boolean
+ Default: =false
+ Value:
+ PropertyKind: Output
+ DisplayName: Value
+ Description: Propriété personnalisée
+ DataType: Text
+ Properties:
+ ChildTabPriority: |-
+ =/*Steve BOURDIN - MOCA by ASI*/
+ true
+ Height: =338
+ Value: =varKeyboard
+ Width: =412
+ Children:
+ - cont_keyboard:
+ Control: GroupContainer@1.3.0
+ Variant: AutoLayout
+ Properties:
+ DropShadow: =DropShadow.Regular
+ Height: =Parent.Height-10
+ LayoutAlignItems: =LayoutAlignItems.Center
+ LayoutDirection: =LayoutDirection.Vertical
+ LayoutJustifyContent: =LayoutJustifyContent.Center
+ PaddingBottom: =8
+ PaddingLeft: =8
+ PaddingRight: =8
+ PaddingTop: =8
+ Width: =Parent.Width-10
+ X: =5
+ Y: =5
+ Children:
+ - cont_line1:
+ Control: GroupContainer@1.3.0
+ Variant: AutoLayout
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ DropShadow: =DropShadow.None
+ LayoutDirection: =LayoutDirection.Horizontal
+ LayoutMinHeight: =0
+ Width: =Parent.Width-Parent.PaddingRight-Parent.PaddingLeft
+ Children:
+ - gal_line1:
+ Control: Gallery@2.15.0
+ Variant: Horizontal
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ BorderColor: =RGBA(0, 18, 107, 1)
+ Height: =Parent.Height
+ Items: =["1","2","3","4","5","6","7","8","9","0"]
+ LayoutMinWidth: =0
+ ShowScrollbar: =false
+ TemplateSize: =(Self.Width/If(Self.AllItemsCount>0,Self.AllItemsCount,1))-Self.TemplatePadding
+ Width: =Parent.Width
+ Children:
+ - btn_line1:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.TemplateHeight
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =RGBA(208,188,255,1)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: =If(varKeyboardMaj=true,Upper(ThisItem.Value),Lower(ThisItem.Value))
+ Width: =Parent.TemplateWidth
+ - cont_line2:
+ Control: GroupContainer@1.3.0
+ Variant: AutoLayout
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ DropShadow: =DropShadow.None
+ LayoutDirection: =LayoutDirection.Horizontal
+ LayoutMinHeight: =0
+ Width: =Parent.Width-Parent.PaddingRight-Parent.PaddingLeft
+ Children:
+ - gal_line2:
+ Control: Gallery@2.15.0
+ Variant: Horizontal
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ BorderColor: =RGBA(0, 18, 107, 1)
+ Height: =Parent.Height
+ Items: =If(varKeyboardSpec=true,["+","*","/","=","\","_","€","£","~","`"],If(keyboard.French,["a","z","e","r","t","y","u","i","o","p"], ["q","w","e","r","t","y","u","i","o","p"]))
+ LayoutMinWidth: =0
+ ShowScrollbar: =false
+ TemplateSize: =(Self.Width/If(Self.AllItemsCount>0,Self.AllItemsCount,1))-Self.TemplatePadding
+ Width: =Parent.Width
+ Children:
+ - btn_line2:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.TemplateHeight
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =RGBA(208,188,255,1)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: =If(varKeyboardMaj=true,Upper(ThisItem.Value),Lower(ThisItem.Value))
+ Width: =Parent.TemplateWidth
+ - cont_line3:
+ Control: GroupContainer@1.3.0
+ Variant: AutoLayout
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ DropShadow: =DropShadow.None
+ LayoutDirection: =LayoutDirection.Horizontal
+ LayoutJustifyContent: =LayoutJustifyContent.Center
+ LayoutMinHeight: =0
+ Width: =Parent.Width-Parent.PaddingRight-Parent.PaddingLeft
+ Children:
+ - gal_line3:
+ Control: Gallery@2.15.0
+ Variant: Horizontal
+ Properties:
+ AlignInContainer: =AlignInContainer.Center
+ BorderColor: =RGBA(0, 18, 107, 1)
+ FillPortions: =0
+ Height: =Parent.Height
+ Items: =If(varKeyboardSpec=true,["!","@","#","$","%","^","&","*","(",")"],If(keyboard.French,["q","s","d","f","g","h","j","k","l","m"],["a","s","d","f","g","h","j","k","l"]))
+ LayoutMinWidth: =0
+ ShowScrollbar: =false
+ TemplateSize: =gal_line2.TemplateWidth
+ Width: =Self.AllItemsCount*(gal_line2.TemplateWidth+gal_line2.TemplatePadding)
+ Children:
+ - btn_line3:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.TemplateHeight
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =ColorFade(RGBA(56, 96, 178, 1), -20%)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: =If(varKeyboardMaj=true,Upper(ThisItem.Value),Lower(ThisItem.Value))
+ Width: =Parent.TemplateWidth
+ - cont_line4:
+ Control: GroupContainer@1.3.0
+ Variant: AutoLayout
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ DropShadow: =DropShadow.None
+ LayoutDirection: =LayoutDirection.Horizontal
+ LayoutJustifyContent: =LayoutJustifyContent.Center
+ LayoutMinHeight: =0
+ Width: =Parent.Width-Parent.PaddingRight-Parent.PaddingLeft
+ Children:
+ - img_upshift:
+ Control: Image@2.2.3
+ Properties:
+ BorderColor: =RGBA(0, 18, 107, 1)
+ Height: =Parent.Height
+ Image: |-
+ ="data:image/svg+xml;utf8," & EncodeUrl(
+ ""
+ )
+ ImagePosition: =ImagePosition.Center
+ OnSelect: =Set(varKeyboardMaj,Not varKeyboardMaj)
+ PaddingBottom: =5
+ PaddingRight: =5
+ PaddingTop: =5
+ Tooltip: =Self.Width
+ Width: =(Parent.Width-gal_line5.Width)/2
+ - gal_line5:
+ Control: Gallery@2.15.0
+ Variant: Horizontal
+ Properties:
+ AlignInContainer: =AlignInContainer.Center
+ BorderColor: =RGBA(0, 18, 107, 1)
+ FillPortions: =0
+ Height: =Parent.Height
+ Items: =If(varKeyboardSpec=true,["-","'","""",":",";",",","?"],If(keyboard.French,["w","x","c","v","b","n"],["z","x","c","v","b","n","m"]))
+ LayoutMinWidth: =0
+ ShowScrollbar: =false
+ TemplateSize: =gal_line2.TemplateWidth
+ Width: =Self.AllItemsCount*(gal_line2.TemplateWidth+gal_line2.TemplatePadding)
+ Children:
+ - btn_line5:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.TemplateHeight
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =ColorFade(RGBA(56, 96, 178, 1), -20%)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: =If(varKeyboardMaj=true,Upper(ThisItem.Value),Lower(ThisItem.Value))
+ Width: =Parent.TemplateWidth
+ - img_erase:
+ Control: Image@2.2.3
+ Properties:
+ BorderColor: =RGBA(0, 18, 107, 1)
+ Height: =Parent.Height
+ Image: |-
+ ="data:image/svg+xml;utf8," & EncodeUrl(
+ "
+ "
+ )
+ ImagePosition: =ImagePosition.Stretch
+ OnSelect: =If(Len(varKeyboard)>0, Set(varKeyboard,Left(varKeyboard, Len(varKeyboard)-1)))
+ PaddingBottom: =5
+ PaddingLeft: =5
+ PaddingTop: =5
+ Width: =(Parent.Width-gal_line5.Width)/2
+ - cont_line5:
+ Control: GroupContainer@1.3.0
+ Variant: AutoLayout
+ Properties:
+ AlignInContainer: =AlignInContainer.SetByContainer
+ DropShadow: =DropShadow.None
+ LayoutAlignItems: =LayoutAlignItems.Center
+ LayoutDirection: =LayoutDirection.Horizontal
+ LayoutGap: =5
+ LayoutJustifyContent: =LayoutJustifyContent.Center
+ LayoutMinHeight: =0
+ Width: =Parent.Width-Parent.PaddingRight-Parent.PaddingLeft
+ Children:
+ - btn_spec:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(208, 188, 255, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.Height-gal_line2.TemplatePadding*2-1
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =ColorFade(RGBA(56, 96, 178, 1), -20%)
+ OnSelect: |-
+ =Set(
+ varKeyboardSpec,
+ Not varKeyboardSpec
+ )
+ PaddingBottom: =10
+ PaddingLeft: =10
+ PaddingRight: =10
+ PaddingTop: =10
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =20
+ RadiusBottomRight: =20
+ RadiusTopLeft: =20
+ RadiusTopRight: =20
+ Size: =14
+ Text: =If(varKeyboardSpec=true,"ABC","?#@")
+ Width: =img_return.Width
+ - btn_comma:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.Height-gal_line2.TemplatePadding*2
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =ColorFade(RGBA(56, 96, 178, 1), -20%)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: =","
+ Width: =gal_line2.TemplateWidth
+ - btn_space:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ FillPortions: =1
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.Height-gal_line2.TemplatePadding*2
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =ColorFade(RGBA(56, 96, 178, 1), -20%)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: =" "
+ Width: =Parent.Width
+ - btn_dot:
+ Control: Classic/Button@2.2.0
+ Properties:
+ BorderColor: =ColorFade(Self.Fill, -15%)
+ BorderThickness: =0
+ Color: =Color.Black
+ DisabledBorderColor: =RGBA(166, 166, 166, 1)
+ Fill: =RGBA(247, 242, 250, 1)
+ Font: =Font.'Open Sans'
+ FontWeight: =FontWeight.Normal
+ Height: =Parent.Height-gal_line2.TemplatePadding*2
+ HoverBorderColor: =ColorFade(Self.BorderColor, 20%)
+ HoverColor: =RGBA(255, 255, 255, 1)
+ HoverFill: =ColorFade(RGBA(56, 96, 178, 1), -20%)
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ Self.Text
+ )
+ )
+ PressedBorderColor: =Self.Fill
+ PressedColor: =Self.Fill
+ PressedFill: =Self.Color
+ RadiusBottomLeft: =6
+ RadiusBottomRight: =6
+ RadiusTopLeft: =6
+ RadiusTopRight: =6
+ Size: =20
+ Text: ="."
+ Width: =gal_line2.TemplateWidth
+ - img_return:
+ Control: Image@2.2.3
+ Properties:
+ BorderColor: =RGBA(0, 18, 107, 1)
+ Height: =Parent.Height
+ Image: |-
+ ="data:image/svg+xml;utf8," & EncodeUrl(
+ "
+
+ "
+ )
+ ImagePosition: =ImagePosition.Stretch
+ OnSelect: |-
+ =Set(
+ varKeyboard,
+ Concatenate(
+ keyboard.Default,
+ "
+ "
+ )
+ )
+ PaddingBottom: =5
+ PaddingLeft: =5
+ PaddingTop: =5
+ Width: =(Parent.Width-gal_line5.Width)/2