@rogieking/figui3 1.0.14 → 1.0.16

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.
Files changed (4) hide show
  1. package/example.html +159 -0
  2. package/fig.css +57 -19
  3. package/fig.js +20 -8
  4. package/package.json +1 -1
package/example.html ADDED
@@ -0,0 +1,159 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport"
7
+ content="width=device-width, initial-scale=1.0">
8
+ <title>Figma UI3 Web Components</title>
9
+ <link rel="stylesheet"
10
+ type="text/css"
11
+ href="fig.css">
12
+ <script src="fig.js"></script>
13
+ </head>
14
+
15
+ <body>
16
+ <fig-header>
17
+ <h3>UI3 Components</h3>
18
+ </fig-header>
19
+ <fig-content>
20
+ <fig-tabs>
21
+ <fig-tab selected>Tab #1</fig-tab>
22
+ <fig-tab>Tab #2</fig-tab>
23
+ </fig-tabs>
24
+ <br /><br />
25
+ <fig-segmented-control>
26
+ <fig-segment selected>One</fig-segment>
27
+ <fig-segment>Two</fig-segment>
28
+ <fig-segment>Three</fig-segment>
29
+ </fig-segmented-control>
30
+ <br /><br />
31
+ <hstack>
32
+ <fig-button>Primary</fig-button>
33
+ <fig-button variant="secondary">Secondary</fig-button>
34
+ <fig-button variant="ghost">Ghost</fig-button>
35
+ <fig-button variant="ghost"
36
+ icon="true">
37
+ <svg class="svg"
38
+ xmlns="http://www.w3.org/2000/svg"
39
+ width="16"
40
+ height="16"
41
+ viewBox="0 0 16 16">
42
+ <path fill="currentColor"
43
+ fill-opacity="1"
44
+ fill-rule="evenodd"
45
+ stroke="none"
46
+ d="M8 3h2c1.933 0 3.5 1.567 3.5 3.5 0 1.933-1.567 3.5-3.5 3.5H4.707l2.147 2.146-.708.708-3-3-.353-.354.353-.354 3-3 .708.708L4.707 9H10c1.38 0 2.5-1.12 2.5-2.5C12.5 5.12 11.38 4 10 4H8z">
47
+ </path>
48
+ </svg>
49
+ </fig-button>
50
+ <fig-tooltip text="Toggle off/on">
51
+ <fig-button variant="toggle"
52
+ icon="true"
53
+ selected="true">
54
+ <svg width="24"
55
+ height="24"
56
+ fill="none"
57
+ viewBox="0 0 24 24">
58
+ <path fill="currentColor"
59
+ fill-rule="evenodd"
60
+ d="M9.5 7h-2a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5m-2-1A1.5 1.5 0 0 0 6 7.5v9A1.5 1.5 0 0 0 7.5 18h2a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 9.5 6zm9 1h-2a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-.5-.5m-2-1A1.5 1.5 0 0 0 13 7.5v2a1.5 1.5 0 0 0 1.5 1.5h2A1.5 1.5 0 0 0 18 9.5v-2A1.5 1.5 0 0 0 16.5 6zm3.384 8.32a.5.5 0 0 0-.768-.64l-2.15 2.58-1.112-1.114a.5.5 0 0 0-.708.708l1.5 1.5a.5.5 0 0 0 .738-.034z"
61
+ clip-rule="evenodd"></path>
62
+ </svg>
63
+ </fig-button>
64
+ </fig-tooltip>
65
+ </hstack>
66
+ <br />
67
+ <fig-dropdown>
68
+ <option>One</option>
69
+ <option>Two</option>
70
+ </fig-dropdown>
71
+ <br />
72
+ <fig-tooltip text="Tooltip text">
73
+ <p>Tooltip</p>
74
+ </fig-tooltip>
75
+ <br /><br />
76
+ <hstack>
77
+ <input type="radio"
78
+ name="radio"
79
+ checked />
80
+ <input type="radio"
81
+ name="radio" />
82
+ </hstack>
83
+ <br /><br />
84
+ <fig-checkbox label="Checkbox"></fig-checkbox>
85
+ <br /><br />
86
+ <fig-radio label="Radio #1"
87
+ name="r1"></fig-radio>
88
+ <br /> <br />
89
+ <fig-radio label="Radio #2"
90
+ name="r1"></fig-radio>
91
+ <br /><br />
92
+ <vstack>
93
+ <fig-switch on="true"></fig-switch>
94
+ <fig-switch indeterminate="true"></fig-switch>
95
+ <fig-switch disabled></fig-switch>
96
+ </vstack>
97
+ <br /><br />
98
+ <fig-input-color value="#00FFFF33"
99
+ text="true"></fig-input-color>
100
+ <br /><br />
101
+ <fig-input-color value="#00FF00"
102
+ alpha="true"
103
+ text="true"></fig-input-color>
104
+ <br /><br />
105
+ <fig-input-text multiline="true"
106
+ value=""
107
+ autoresize="true"
108
+ resizable="true"
109
+ placeholder="Anthropic API key">
110
+ </fig-input-text>
111
+ <br />
112
+ <fig-input-text type="number"
113
+ value="10"></fig-input-text>
114
+ <br /><br />
115
+ <fig-field direction="horizontal">
116
+ <label>Field label</label>
117
+ <fig-input-text value=""
118
+ placeholder="Field placeholder"></fig-input-text>
119
+ </fig-field>
120
+ <fig-field>
121
+ <label>Label</label>
122
+ <fig-input-text value=""
123
+ placeholder="Field placeholder"></fig-input-text>
124
+ </fig-field>
125
+ <fig-slider></fig-slider>
126
+ <br />
127
+ <fig-slider text="true"
128
+ value="50"></fig-slider>
129
+ <br />
130
+ <fig-slider type="opacity"
131
+ value="0.75"
132
+ color="#ff0000"></fig-slider>
133
+ <br />
134
+ <fig-slider type="hue"
135
+ value="55"></fig-slider>
136
+ <br />
137
+ <fig-slider value="50"
138
+ step="25">
139
+ <datalist id="markers">
140
+ <option value="0"></option>
141
+ <option value="25"></option>
142
+ <option value="50"></option>
143
+ <option value="75"></option>
144
+ <option value="100"></option>
145
+ </datalist>
146
+ </fig-slider>
147
+ <br />
148
+ <fig-slider type="delta"
149
+ value="2"
150
+ default="0"
151
+ max="5"
152
+ min="-5"></fig-slider>
153
+ <br />
154
+ <fig-spinner></fig-spinner>
155
+ </fig-content>
156
+
157
+ </body>
158
+
159
+ </html>
package/fig.css CHANGED
@@ -361,7 +361,7 @@
361
361
  --bg-selected-active: #E5F4FF;
362
362
  --bg-tooltip: #1E1E1E;
363
363
  --body-medium-fontSize: 0.6875rem;
364
- --border-selected: #0D99FF;
364
+ --figma-color-border-selected: #0D99FF;
365
365
  --spacer-1: .25rem;
366
366
  --spacer-2: .5rem;
367
367
  --spacer-3: 1rem;
@@ -498,7 +498,7 @@ input[type=number],
498
498
  color: inherit;
499
499
 
500
500
  &:focus {
501
- box-shadow: inset 0 0 0 1px var(--border-selected);
501
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
502
502
  outline: 0;
503
503
  }
504
504
 
@@ -563,7 +563,7 @@ input[type=text][list] {
563
563
  accent-color: var(--figma-color-bg-brand);
564
564
 
565
565
  &:focus {
566
- box-shadow: inset 0 0 0 1px var(--border-selected);
566
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
567
567
  outline: 0;
568
568
  }
569
569
 
@@ -616,6 +616,7 @@ input[type=button] {
616
616
  color: var(--figma-color-text-onbrand);
617
617
  border-radius: var(--radius-medium);
618
618
  font-weight: 500;
619
+ box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
619
620
 
620
621
  &:active {
621
622
  background-color: var(--figma-color-bg-brand-pressed);
@@ -636,13 +637,6 @@ input[type=button] {
636
637
  outline: 0;
637
638
  background-color: var(--figma-color-bg-selected);
638
639
  }
639
-
640
- &:focus {
641
- outline: 0;
642
- background-color: transparent;
643
- box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
644
- }
645
-
646
640
  }
647
641
 
648
642
  &[variant="ghost"][disabled=true],
@@ -731,6 +725,11 @@ input[type=button] {
731
725
  color: var(--figma-color-icon-brand);
732
726
  background-color: var(--figma-color-bg-selected);
733
727
  }
728
+
729
+ &:focus {
730
+ outline: 0;
731
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
732
+ }
734
733
  }
735
734
 
736
735
  fig-button>button {
@@ -848,7 +847,7 @@ input[type=color] {
848
847
  background-color: var(--figma-color-bg-secondary);
849
848
 
850
849
  &:focus {
851
- box-shadow: inset 0 0 0 1px var(--border-selected);
850
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
852
851
  outline: 0;
853
852
  }
854
853
  }
@@ -987,6 +986,11 @@ input[type=checkbox]:not(.switch) {
987
986
  background-color: var(--figma-color-bg-brand);
988
987
  background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4.50012 7.5L7.50012 10.5L11.5001 4.5' stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.125' /%3E%3C/svg%3E%0A");
989
988
  }
989
+
990
+ &:focus {
991
+ outline: 0;
992
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
993
+ }
990
994
  }
991
995
 
992
996
  .figma-light input[type=checkbox]:not(.switch):hover {
@@ -1023,8 +1027,15 @@ input[type=radio] {
1023
1027
  }
1024
1028
 
1025
1029
  &:focus {
1026
- box-shadow: inset 0 0 0 1px var(--border-selected);
1027
1030
  outline: 0;
1031
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
1032
+ }
1033
+
1034
+ &:hover:not(:checked) {
1035
+ &::after {
1036
+ transform: scale(1);
1037
+ opacity: 0.25;
1038
+ }
1028
1039
  }
1029
1040
 
1030
1041
  &::after {
@@ -1034,7 +1045,6 @@ input[type=radio] {
1034
1045
  border-radius: 0.25rem;
1035
1046
  background-color: var(--figma-color-text-onbrand);
1036
1047
  transform: scale(0);
1037
- transition: var(--input-transition);
1038
1048
  box-shadow:
1039
1049
  0px 0 0 0.75px rgba(0, 0, 0, 0.1),
1040
1050
  0px 1px 3px 0px rgba(0, 0, 0, 0.1),
@@ -1168,6 +1178,8 @@ details {
1168
1178
  fig-slider {
1169
1179
  --slider-height: 1rem;
1170
1180
  --slider-percent: calc(var(--slider-complete) * 100%);
1181
+ --start-percent: calc(var(--default, 0) * 100%);
1182
+ --slider-tick-size: calc(var(--slider-height)/4);
1171
1183
  --slider-handle-shadow: inset 0 0 0 4px var(--figma-color-icon-onbrand),
1172
1184
  inset 0 0 0 5px rgba(0, 0, 0, 0.1),
1173
1185
  0px 0 0 0.75px rgba(0, 0, 0, 0.1),
@@ -1180,7 +1192,7 @@ fig-slider {
1180
1192
  0px 1px 3px 0px rgba(0, 0, 0, 0.1),
1181
1193
  0px 3px 8px 0px rgba(0, 0, 0, 0.1),
1182
1194
  0px 0px 0.5px 0px rgba(0, 0, 0, 0.18),
1183
- 0 0 0 1px var(--border-selected);
1195
+ 0 0 0 1px var(--figma-color-border-selected);
1184
1196
 
1185
1197
  display: inline-flex;
1186
1198
  align-items: center;
@@ -1202,11 +1214,38 @@ fig-slider {
1202
1214
  bottom: 0;
1203
1215
  border-radius: 0.5rem;
1204
1216
  min-width: var(--slider-height);
1217
+ box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
1205
1218
  width: calc(var(--slider-percent) + (1 - var(--slider-complete)) * var(--slider-height));
1206
1219
  max-width: 100%;
1207
1220
  }
1208
1221
  }
1209
1222
 
1223
+ /* TODO: Delta sizing */
1224
+ &[type=delta] {
1225
+ .fig-slider-input-container {
1226
+ &::before {
1227
+ --left-start: calc(var(--start-percent) - var(--slider-height)/2);
1228
+ left: min(var(--left-start), var(--slider-percent));
1229
+ --width: calc(var(--slider-percent) - var(--start-percent));
1230
+ --abs-width: max(var(--width) + var(--slider-height)/2, -1*var(--width) + var(--slider-height)/2);
1231
+ width: var(--abs-width);
1232
+ }
1233
+
1234
+ &::after {
1235
+ content: '';
1236
+ width: var(--slider-tick-size);
1237
+ height: var(--slider-tick-size);
1238
+ aspect-ratio: 1;
1239
+ border-radius: 100%;
1240
+ font-size: 0;
1241
+ position: absolute;
1242
+ left: calc(var(--start-percent) - var(--slider-tick-size)/2);
1243
+ top: calc(50% - var(--slider-tick-size)/2);
1244
+ background: var(--figma-color-icon-tertiary);
1245
+ }
1246
+ }
1247
+ }
1248
+
1210
1249
  &[type=hue],
1211
1250
  &[type=opacity] {
1212
1251
  .fig-slider-input-container {
@@ -1342,7 +1381,6 @@ fig-slider {
1342
1381
  }
1343
1382
 
1344
1383
  datalist {
1345
- --dot-size: calc(var(--slider-height)/4);
1346
1384
  position: absolute;
1347
1385
  inset: 0;
1348
1386
  display: flex;
@@ -1351,7 +1389,7 @@ fig-slider {
1351
1389
  margin: 0;
1352
1390
  border: 0;
1353
1391
  appearance: none;
1354
- padding: 0 calc((var(--slider-height)/2) - var(--dot-size)/2);
1392
+ padding: 0 calc((var(--slider-height)/2) - var(--slider-tick-size)/2);
1355
1393
  height: var(--slider-height);
1356
1394
  pointer-events: none;
1357
1395
  justify-content: space-between;
@@ -1359,8 +1397,8 @@ fig-slider {
1359
1397
 
1360
1398
  & option {
1361
1399
  appearance: none;
1362
- width: var(--dot-size);
1363
- height: var(--dot-size);
1400
+ width: var(--slider-tick-size);
1401
+ height: var(--slider-tick-size);
1364
1402
  aspect-ratio: 1;
1365
1403
  margin: 0;
1366
1404
  padding: 0;
@@ -1680,7 +1718,7 @@ fig-input-text {
1680
1718
  }
1681
1719
 
1682
1720
  &:has(input:focus) {
1683
- box-shadow: inset 0 0 0 1px var(--border-selected);
1721
+ box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
1684
1722
  outline: 0;
1685
1723
  }
1686
1724
  }
package/fig.js CHANGED
@@ -21,10 +21,12 @@ class FigButton extends HTMLElement {
21
21
  button, button:hover, button:active {
22
22
  padding: 0;
23
23
  appearance: none;
24
- display: block;
24
+ display: flex;
25
25
  border: 0;
26
26
  font: inherit;
27
27
  color: inherit;
28
+ outline: 0;
29
+ place-items: center;
28
30
  background: transparent;
29
31
  }
30
32
  </style>
@@ -490,6 +492,7 @@ class FigSlider extends HTMLElement {
490
492
  #typeDefaults = {
491
493
  range: { min: 0, max: 100, step: 1 },
492
494
  hue: { min: 0, max: 255, step: 1 },
495
+ delta: { min: -100, max: 100, step: 1 },
493
496
  opacity: { min: 0, max: 1, step: 0.01, color: "#FF0000" }
494
497
  }
495
498
  constructor() {
@@ -498,6 +501,7 @@ class FigSlider extends HTMLElement {
498
501
  connectedCallback() {
499
502
 
500
503
  this.value = this.getAttribute("value")
504
+ this.default = this.getAttribute("default") || null
501
505
  this.type = this.getAttribute("type") || "range"
502
506
 
503
507
  const defaults = this.#typeDefaults[this.type]
@@ -554,6 +558,10 @@ class FigSlider extends HTMLElement {
554
558
  if (this.textInput) {
555
559
  this.textInput.addEventListener("input", this.handleTextInput.bind(this))
556
560
  }
561
+
562
+ if (this.default) {
563
+ this.style.setProperty("--default", this.calculateNormal(this.default))
564
+ }
557
565
  }
558
566
  static get observedAttributes() {
559
567
  return ['value', 'step', 'min', 'max', 'type', 'disabled']
@@ -596,13 +604,17 @@ class FigSlider extends HTMLElement {
596
604
  this.handleInput()
597
605
  }
598
606
  }
599
-
600
- handleInput() {
607
+ calculateNormal(value) {
601
608
  let min = Number(this.input.min)
602
609
  let max = Number(this.input.max)
610
+ let val = Number(value)
611
+ return (val - min) / (max - min)
612
+ }
613
+
614
+ handleInput() {
603
615
  let val = Number(this.input.value)
604
616
  this.value = val
605
- let complete = (val - min) / (max - min)
617
+ let complete = this.calculateNormal(val)
606
618
  this.style.setProperty("--slider-complete", complete)
607
619
  if (this.textInput) {
608
620
  this.textInput.value = val
@@ -681,15 +693,15 @@ class FigField extends HTMLElement {
681
693
  requestAnimationFrame(() => {
682
694
  this.label = this.querySelector('label')
683
695
  this.input = Array.from(this.childNodes).find(node => node.nodeName.toLowerCase().startsWith("fig-"))
684
- if (this.input) {
696
+ if (this.input && this.label) {
685
697
  this.label.addEventListener('click', this.focus.bind(this))
698
+ let inputId = this.input.getAttribute("id") || uniqueId()
699
+ this.input.setAttribute("id", inputId)
700
+ this.label.setAttribute("for", inputId)
686
701
  }
687
702
  })
688
703
  }
689
704
  focus() {
690
- if (!this.input) {
691
- this.input = Array.from(this.childNodes).find(node => node.nodeName.toLowerCase().startsWith("fig-"))
692
- }
693
705
  this.input.focus()
694
706
  }
695
707
  }
package/package.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "1.0.14"
3
+ "version": "1.0.16"
4
4
  }