@dev-crew-berlin/enter-js-utils 0.98.10 → 0.98.12
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/dist/api-client/api-base.js +62 -9
- package/dist/api-client/api-client.test.js +190 -0
- package/dist/ui/companion-info.d.ts +3 -0
- package/dist/ui/companion-info.js +24 -8
- package/dist/ui/companion-info.stories.js +11 -0
- package/dist/ui/form-elements/input.d.ts +1 -1
- package/dist/ui/form-elements/select.d.ts +1 -1
- package/dist/ui/form-elements/select.js +4 -4
- package/dist/ui/tag.js +3 -3
- package/package.json +1 -1
|
@@ -5,15 +5,22 @@ const BACKOFF_INITIAL_MS = 1_000;
|
|
|
5
5
|
const BACKOFF_MAX_MS = 30_000;
|
|
6
6
|
const BACKOFF_RESET_AFTER_MS = 10_000;
|
|
7
7
|
const WATCHDOG_TIMEOUT_MS = 45_000;
|
|
8
|
-
function sleep(ms,
|
|
8
|
+
function sleep(ms, ...signals) {
|
|
9
9
|
return new Promise(resolve => {
|
|
10
10
|
const timer = setTimeout(resolve, ms);
|
|
11
|
-
|
|
11
|
+
const cleanup = () => {
|
|
12
12
|
clearTimeout(timer);
|
|
13
13
|
resolve();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
};
|
|
15
|
+
for (const signal of signals) {
|
|
16
|
+
if (signal.aborted) {
|
|
17
|
+
cleanup();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
signal.addEventListener('abort', cleanup, {
|
|
21
|
+
once: true
|
|
22
|
+
});
|
|
23
|
+
}
|
|
17
24
|
});
|
|
18
25
|
}
|
|
19
26
|
export default class APIBase {
|
|
@@ -182,11 +189,38 @@ export default class APIBase {
|
|
|
182
189
|
let lastEventId = options.initialLastEventId ?? null;
|
|
183
190
|
let backoffMs = BACKOFF_INITIAL_MS;
|
|
184
191
|
let isFirstConnect = true;
|
|
192
|
+
|
|
193
|
+
// iOS standalone PWAs are frozen when backgrounded; the heartbeat watchdog
|
|
194
|
+
// cannot fire while JS is suspended. When the user returns to the app the
|
|
195
|
+
// foreground events fire immediately, so we use them as a hard-reconnect
|
|
196
|
+
// trigger instead of waiting for the watchdog.
|
|
197
|
+
let currentWakeController = null;
|
|
198
|
+
const onForegroundResume = () => {
|
|
199
|
+
backoffMs = BACKOFF_INITIAL_MS;
|
|
200
|
+
currentWakeController?.abort();
|
|
201
|
+
};
|
|
202
|
+
const onVisibilityChange = () => {
|
|
203
|
+
if (document.visibilityState === 'visible') onForegroundResume();
|
|
204
|
+
};
|
|
205
|
+
const hasDocument = typeof document !== 'undefined';
|
|
206
|
+
if (hasDocument) {
|
|
207
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
208
|
+
document.addEventListener('pageshow', onForegroundResume);
|
|
209
|
+
}
|
|
185
210
|
try {
|
|
186
211
|
while (!outerAbort.signal.aborted) {
|
|
187
212
|
// Fresh abort controller per attempt so the watchdog or a read error
|
|
188
213
|
// on one attempt cannot bleed into the next.
|
|
189
214
|
const attemptAbort = new AbortController();
|
|
215
|
+
// wakeController is aborted by foreground events to immediately skip
|
|
216
|
+
// the backoff sleep and break out of a wedged reader.read() race.
|
|
217
|
+
const wakeController = new AbortController();
|
|
218
|
+
currentWakeController = wakeController;
|
|
219
|
+
// Propagate a foreground wake to the in-flight fetch so it is
|
|
220
|
+
// cancelled and we open a fresh connection right away.
|
|
221
|
+
wakeController.signal.addEventListener('abort', () => attemptAbort.abort(), {
|
|
222
|
+
once: true
|
|
223
|
+
});
|
|
190
224
|
const propagateAbort = () => attemptAbort.abort();
|
|
191
225
|
outerAbort.signal.addEventListener('abort', propagateAbort);
|
|
192
226
|
const hadCursor = lastEventId !== null;
|
|
@@ -223,7 +257,7 @@ export default class APIBase {
|
|
|
223
257
|
for await (const {
|
|
224
258
|
data,
|
|
225
259
|
id
|
|
226
|
-
} of this.readSseEvents(res.data.body, attemptAbort, onReset)) {
|
|
260
|
+
} of this.readSseEvents(res.data.body, attemptAbort, onReset, wakeController.signal)) {
|
|
227
261
|
if (id) lastEventId = id;
|
|
228
262
|
yield data;
|
|
229
263
|
}
|
|
@@ -242,13 +276,17 @@ export default class APIBase {
|
|
|
242
276
|
const lived = connectedAt > 0 ? Date.now() - connectedAt : 0;
|
|
243
277
|
if (lived >= BACKOFF_RESET_AFTER_MS) backoffMs = BACKOFF_INITIAL_MS;
|
|
244
278
|
onConnectionChange?.('reconnecting');
|
|
245
|
-
await sleep(backoffMs, outerAbort.signal);
|
|
279
|
+
await sleep(backoffMs, outerAbort.signal, wakeController.signal);
|
|
246
280
|
backoffMs = Math.min(backoffMs * 2, BACKOFF_MAX_MS) * (0.5 + Math.random());
|
|
247
281
|
}
|
|
248
282
|
} finally {
|
|
249
283
|
// Aborting here also unblocks any in-progress sleep() call.
|
|
250
284
|
outerAbort.abort();
|
|
251
285
|
externalSignal?.removeEventListener('abort', onExternalAbort);
|
|
286
|
+
if (hasDocument) {
|
|
287
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
288
|
+
document.removeEventListener('pageshow', onForegroundResume);
|
|
289
|
+
}
|
|
252
290
|
}
|
|
253
291
|
}
|
|
254
292
|
|
|
@@ -257,7 +295,7 @@ export default class APIBase {
|
|
|
257
295
|
// if no message arrives within WATCHDOG_TIMEOUT_MS the connection is
|
|
258
296
|
// considered half-open (common on mobile networks) and is aborted so the
|
|
259
297
|
// outer loop can reconnect.
|
|
260
|
-
async *readSseEvents(body, attemptAbort, onReset) {
|
|
298
|
+
async *readSseEvents(body, attemptAbort, onReset, wakeSignal) {
|
|
261
299
|
const reader = body.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()).getReader();
|
|
262
300
|
let watchdogTimer = null;
|
|
263
301
|
const resetWatchdog = () => {
|
|
@@ -265,12 +303,27 @@ export default class APIBase {
|
|
|
265
303
|
watchdogTimer = setTimeout(() => attemptAbort.abort(), WATCHDOG_TIMEOUT_MS);
|
|
266
304
|
};
|
|
267
305
|
resetWatchdog();
|
|
306
|
+
|
|
307
|
+
// Resolves to a sentinel when a foreground-resume event fires. Racing
|
|
308
|
+
// reader.read() against this promise means the loop can advance to a
|
|
309
|
+
// fresh connection even if the old read is permanently wedged (iOS).
|
|
310
|
+
const reconnect = new Promise(resolve => {
|
|
311
|
+
if (wakeSignal.aborted) {
|
|
312
|
+
resolve('reconnect');
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
wakeSignal.addEventListener('abort', () => resolve('reconnect'), {
|
|
316
|
+
once: true
|
|
317
|
+
});
|
|
318
|
+
});
|
|
268
319
|
try {
|
|
269
320
|
while (true) {
|
|
321
|
+
const result = await Promise.race([reader.read(), reconnect]);
|
|
322
|
+
if (result === 'reconnect') break;
|
|
270
323
|
const {
|
|
271
324
|
done,
|
|
272
325
|
value: event
|
|
273
|
-
} =
|
|
326
|
+
} = result;
|
|
274
327
|
if (done) break;
|
|
275
328
|
|
|
276
329
|
// Any message — including heartbeats — proves the connection is alive.
|
|
@@ -645,6 +645,196 @@ describe('resilientStream', () => {
|
|
|
645
645
|
await pending;
|
|
646
646
|
});
|
|
647
647
|
});
|
|
648
|
+
|
|
649
|
+
// Minimal document stub for foreground-event tests (test env is node, no DOM).
|
|
650
|
+
// visibilityState and dispatch() let tests simulate visibilitychange / pageshow.
|
|
651
|
+
function makeMockDocument() {
|
|
652
|
+
const listeners = new Map();
|
|
653
|
+
return {
|
|
654
|
+
visibilityState: 'visible',
|
|
655
|
+
addEventListener(type, fn) {
|
|
656
|
+
if (!listeners.has(type)) listeners.set(type, new Set());
|
|
657
|
+
listeners.get(type).add(fn);
|
|
658
|
+
},
|
|
659
|
+
removeEventListener(type, fn) {
|
|
660
|
+
listeners.get(type)?.delete(fn);
|
|
661
|
+
},
|
|
662
|
+
dispatch(type) {
|
|
663
|
+
for (const fn of listeners.get(type) ?? []) fn(new Event(type));
|
|
664
|
+
},
|
|
665
|
+
listenerCount(type) {
|
|
666
|
+
return listeners.get(type)?.size ?? 0;
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// A stream that delivers initialChunks immediately but never closes and never
|
|
672
|
+
// errors, so reader.read() hangs forever once the chunks are consumed.
|
|
673
|
+
function makeWedgedStream(initialChunks = []) {
|
|
674
|
+
return new ReadableStream({
|
|
675
|
+
start(controller) {
|
|
676
|
+
for (const chunk of initialChunks) controller.enqueue(chunk);
|
|
677
|
+
// deliberately never closed or errored
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
describe('foreground reconnect (visibilitychange / pageshow)', () => {
|
|
682
|
+
let mockDoc;
|
|
683
|
+
beforeEach(() => {
|
|
684
|
+
vi.useFakeTimers();
|
|
685
|
+
mockDoc = makeMockDocument();
|
|
686
|
+
vi.stubGlobal('document', mockDoc);
|
|
687
|
+
});
|
|
688
|
+
afterEach(() => {
|
|
689
|
+
vi.useRealTimers();
|
|
690
|
+
vi.restoreAllMocks();
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// ─── immediate reconnect on visibilitychange ──────────────────────────────
|
|
694
|
+
|
|
695
|
+
it('reconnects immediately on visibilitychange without waiting for backoff', async () => {
|
|
696
|
+
let connectionCount = 0;
|
|
697
|
+
vi.stubGlobal('fetch', (_url, init) => {
|
|
698
|
+
connectionCount++;
|
|
699
|
+
// All connections return a silent stream (never delivers events)
|
|
700
|
+
return Promise.resolve(okResponse(makeSilentStream(init.signal)));
|
|
701
|
+
});
|
|
702
|
+
const abortController = new AbortController();
|
|
703
|
+
const api = makeAPI();
|
|
704
|
+
const gen = api.subscribe('/test', {
|
|
705
|
+
signal: abortController.signal
|
|
706
|
+
});
|
|
707
|
+
gen.next();
|
|
708
|
+
await flushMicrotasks();
|
|
709
|
+
expect(connectionCount).toBe(1);
|
|
710
|
+
|
|
711
|
+
// Simulate returning to foreground — should trigger immediate reconnect
|
|
712
|
+
mockDoc.visibilityState = 'visible';
|
|
713
|
+
mockDoc.dispatch('visibilitychange');
|
|
714
|
+
await flushMicrotasks();
|
|
715
|
+
|
|
716
|
+
// New connection opened without advancing fake timers at all
|
|
717
|
+
expect(connectionCount).toBeGreaterThanOrEqual(2);
|
|
718
|
+
abortController.abort();
|
|
719
|
+
await gen.return(undefined);
|
|
720
|
+
});
|
|
721
|
+
it('reconnects immediately on pageshow without waiting for backoff', async () => {
|
|
722
|
+
let connectionCount = 0;
|
|
723
|
+
vi.stubGlobal('fetch', (_url, init) => {
|
|
724
|
+
connectionCount++;
|
|
725
|
+
return Promise.resolve(okResponse(makeSilentStream(init.signal)));
|
|
726
|
+
});
|
|
727
|
+
const abortController = new AbortController();
|
|
728
|
+
const api = makeAPI();
|
|
729
|
+
const gen = api.subscribe('/test', {
|
|
730
|
+
signal: abortController.signal
|
|
731
|
+
});
|
|
732
|
+
gen.next();
|
|
733
|
+
await flushMicrotasks();
|
|
734
|
+
expect(connectionCount).toBe(1);
|
|
735
|
+
mockDoc.dispatch('pageshow');
|
|
736
|
+
await flushMicrotasks();
|
|
737
|
+
expect(connectionCount).toBeGreaterThanOrEqual(2);
|
|
738
|
+
abortController.abort();
|
|
739
|
+
await gen.return(undefined);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// ─── Last-Event-ID carried on foreground reconnect ────────────────────────
|
|
743
|
+
|
|
744
|
+
it('foreground reconnect carries Last-Event-ID from last received event', async () => {
|
|
745
|
+
const capturedInits = [];
|
|
746
|
+
vi.stubGlobal('fetch', (_url, init) => {
|
|
747
|
+
capturedInits.push(init);
|
|
748
|
+
if (capturedInits.length === 1) {
|
|
749
|
+
// First connection: delivers one event with cursor, then hangs
|
|
750
|
+
return Promise.resolve(okResponse(makeWedgedStream([sseChunk([{
|
|
751
|
+
id: 'cursor-42',
|
|
752
|
+
data: '{"n":1}'
|
|
753
|
+
}])])));
|
|
754
|
+
}
|
|
755
|
+
// Second+ connections: abort-responsive so the test can clean up
|
|
756
|
+
return Promise.resolve(okResponse(makeSilentStream(init.signal)));
|
|
757
|
+
});
|
|
758
|
+
const abortController = new AbortController();
|
|
759
|
+
const api = makeAPI();
|
|
760
|
+
const gen = api.subscribe('/test', {
|
|
761
|
+
signal: abortController.signal
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// Consume the first event; generator is now suspended at 'yield data'
|
|
765
|
+
const response1 = await gen.next();
|
|
766
|
+
expect(response1.value).toEqual({
|
|
767
|
+
n: 1
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// Drive the generator forward so it re-enters readSseEvents's Promise.race.
|
|
771
|
+
// Without this call the generator stays frozen at 'yield data' and the
|
|
772
|
+
// foreground event cannot be picked up until the consumer drives it.
|
|
773
|
+
const nextPromise = gen.next();
|
|
774
|
+
await flushMicrotasks();
|
|
775
|
+
|
|
776
|
+
// Foreground event fires while reader.read() is wedged on the first stream
|
|
777
|
+
mockDoc.visibilityState = 'visible';
|
|
778
|
+
mockDoc.dispatch('visibilitychange');
|
|
779
|
+
await flushMicrotasks();
|
|
780
|
+
expect(capturedInits.length).toBeGreaterThanOrEqual(2);
|
|
781
|
+
const secondHeaders = capturedInits[1]?.headers;
|
|
782
|
+
expect(secondHeaders?.['Last-Event-ID']).toBe('cursor-42');
|
|
783
|
+
abortController.abort();
|
|
784
|
+
await nextPromise;
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// ─── wedged read — recovery without stream closing ────────────────────────
|
|
788
|
+
|
|
789
|
+
it('recovers even when the prior response body never closes', async () => {
|
|
790
|
+
let connectionCount = 0;
|
|
791
|
+
vi.stubGlobal('fetch', (_url, init) => {
|
|
792
|
+
connectionCount++;
|
|
793
|
+
if (connectionCount === 1) {
|
|
794
|
+
// First connection is truly wedged — reader.read() hangs forever and
|
|
795
|
+
// the stream ignores the abort signal, simulating iOS zombie streams.
|
|
796
|
+
return Promise.resolve(okResponse(makeWedgedStream()));
|
|
797
|
+
}
|
|
798
|
+
// Subsequent connections are abort-responsive so the test can clean up.
|
|
799
|
+
return Promise.resolve(okResponse(makeSilentStream(init.signal)));
|
|
800
|
+
});
|
|
801
|
+
const abortController = new AbortController();
|
|
802
|
+
const api = makeAPI();
|
|
803
|
+
const gen = api.subscribe('/test', {
|
|
804
|
+
signal: abortController.signal
|
|
805
|
+
});
|
|
806
|
+
gen.next();
|
|
807
|
+
await flushMicrotasks();
|
|
808
|
+
expect(connectionCount).toBe(1);
|
|
809
|
+
|
|
810
|
+
// Foreground event — must open new connection even though the first
|
|
811
|
+
// stream body is completely wedged and reader.read() never resolves.
|
|
812
|
+
mockDoc.visibilityState = 'visible';
|
|
813
|
+
mockDoc.dispatch('visibilitychange');
|
|
814
|
+
await flushMicrotasks();
|
|
815
|
+
expect(connectionCount).toBeGreaterThanOrEqual(2);
|
|
816
|
+
abortController.abort();
|
|
817
|
+
await gen.return(undefined);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// ─── no listener / timer leaks ────────────────────────────────────────────
|
|
821
|
+
|
|
822
|
+
it('removes visibilitychange and pageshow listeners after consumer aborts', async () => {
|
|
823
|
+
vi.stubGlobal('fetch', (_url, init) => Promise.resolve(okResponse(makeSilentStream(init.signal))));
|
|
824
|
+
const abortController = new AbortController();
|
|
825
|
+
const api = makeAPI();
|
|
826
|
+
const pending = api.subscribe('/test', {
|
|
827
|
+
signal: abortController.signal
|
|
828
|
+
}).next();
|
|
829
|
+
await flushMicrotasks();
|
|
830
|
+
expect(mockDoc.listenerCount('visibilitychange')).toBe(1);
|
|
831
|
+
expect(mockDoc.listenerCount('pageshow')).toBe(1);
|
|
832
|
+
abortController.abort();
|
|
833
|
+
await pending;
|
|
834
|
+
expect(mockDoc.listenerCount('visibilitychange')).toBe(0);
|
|
835
|
+
expect(mockDoc.listenerCount('pageshow')).toBe(0);
|
|
836
|
+
});
|
|
837
|
+
});
|
|
648
838
|
function makeFullAPI() {
|
|
649
839
|
return new API({
|
|
650
840
|
credentials: {
|
|
@@ -3,13 +3,29 @@ import React from 'react';
|
|
|
3
3
|
import 'styled-jsx';
|
|
4
4
|
import { colors, fonts } from '../lib/theme';
|
|
5
5
|
export const CompanionInfo = props => {
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
if (props.companionStatus.isCompanion) {
|
|
7
|
+
return /*#__PURE__*/React.createElement("p", {
|
|
8
|
+
className: _JSXStyle.dynamic([["747595454", [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]]]) + " " + 'companion-info'
|
|
9
|
+
}, "companion of", ' ', /*#__PURE__*/React.createElement("span", {
|
|
10
|
+
className: _JSXStyle.dynamic([["747595454", [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]]]) + " " + 'companion-name'
|
|
11
|
+
}, props.companionStatus.mainGuestName), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
12
|
+
id: "747595454",
|
|
13
|
+
dynamic: [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]
|
|
14
|
+
}, `.companion-info.__jsx-style-dynamic-selector{margin:-0.5em 0 0.4em;color:${colors.enterMediumGrey};font-family:${fonts.primary};}.companion-name.__jsx-style-dynamic-selector{color:${colors.enterDarkGrey};}`));
|
|
15
|
+
}
|
|
16
|
+
const companionNames = Object.values(props.companionStatus.companionNames ?? {});
|
|
17
|
+
if (companionNames.length === 0) return null;
|
|
18
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
19
|
+
className: _JSXStyle.dynamic([["2921445348", [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]]]) + " " + 'companions-info'
|
|
20
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
21
|
+
className: _JSXStyle.dynamic([["2921445348", [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]]]) + " " + 'companions-label'
|
|
22
|
+
}, "companions:"), /*#__PURE__*/React.createElement("div", {
|
|
23
|
+
className: _JSXStyle.dynamic([["2921445348", [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]]]) + " " + 'companions-list'
|
|
24
|
+
}, companionNames.map((name, index) => /*#__PURE__*/React.createElement("span", {
|
|
25
|
+
key: index,
|
|
26
|
+
className: _JSXStyle.dynamic([["2921445348", [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]]]) + " " + 'companion-name'
|
|
27
|
+
}, name))), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
28
|
+
id: "2921445348",
|
|
13
29
|
dynamic: [colors.enterMediumGrey, fonts.primary, colors.enterDarkGrey]
|
|
14
|
-
}, `.
|
|
30
|
+
}, `.companions-info.__jsx-style-dynamic-selector{display:grid;grid-template-columns:auto 1fr;-webkit-column-gap:0.6rem;column-gap:0.6rem;margin:-0.5em 0 0.4em;color:${colors.enterMediumGrey};font-family:${fonts.primary};}.companions-label.__jsx-style-dynamic-selector{white-space:nowrap;}.companions-list.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}.companions-list.__jsx-style-dynamic-selector .companion-name.__jsx-style-dynamic-selector{display:block;width:-webkit-max-content;width:-moz-max-content;width:max-content;color:${colors.enterDarkGrey};}`));
|
|
15
31
|
};
|
|
@@ -19,4 +19,15 @@ export const NotACompanion = {
|
|
|
19
19
|
isCompanion: false
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
};
|
|
23
|
+
export const WithCompanions = {
|
|
24
|
+
args: {
|
|
25
|
+
companionStatus: {
|
|
26
|
+
isCompanion: false,
|
|
27
|
+
companionNames: {
|
|
28
|
+
'companion-1': 'Erika Musterfrau',
|
|
29
|
+
'companion-2': 'John Doe'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
22
33
|
};
|
|
@@ -24,14 +24,14 @@ export function Select({
|
|
|
24
24
|
id: id,
|
|
25
25
|
value: value,
|
|
26
26
|
onChange: e => onChange(e.target.value),
|
|
27
|
-
className: _JSXStyle.dynamic([["
|
|
27
|
+
className: _JSXStyle.dynamic([["3612400046", [fonts.primary, colors.enterDarkGrey, chevronSVGImageURL]]]) + " " + (inputProps && inputProps.className != null && inputProps.className || "")
|
|
28
28
|
}), options.map(option => /*#__PURE__*/React.createElement("option", {
|
|
29
29
|
key: option,
|
|
30
30
|
value: option,
|
|
31
31
|
disabled: optionDisabled(option),
|
|
32
|
-
className: _JSXStyle.dynamic([["
|
|
32
|
+
className: _JSXStyle.dynamic([["3612400046", [fonts.primary, colors.enterDarkGrey, chevronSVGImageURL]]])
|
|
33
33
|
}, optionFormat(option)))), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
34
|
-
id: "
|
|
34
|
+
id: "3612400046",
|
|
35
35
|
dynamic: [fonts.primary, colors.enterDarkGrey, chevronSVGImageURL]
|
|
36
|
-
}, `select.__jsx-style-dynamic-selector{font-family:${fonts.primary};font-size:0.95rem;display:block;width:100%;padding:0.55rem 2.25rem 0.55rem 0.875rem;color:${colors.enterDarkGrey};background-color:#ffffff;border:1px solid #e5e7eb;border-radius:
|
|
36
|
+
}, `select.__jsx-style-dynamic-selector{font-family:${fonts.primary};font-size:0.95rem;display:block;width:100%;padding:0.55rem 2.25rem 0.55rem 0.875rem;color:${colors.enterDarkGrey};background-color:#ffffff;border:1px solid #e5e7eb;border-radius:999px;outline:none;box-shadow:0 1px 2px rgba(16,24,40,0.05);-webkit-transition:border-color 120ms ease,box-shadow 120ms ease, background-color 120ms ease;transition:border-color 120ms ease,box-shadow 120ms ease, background-color 120ms ease;-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-appearance:none;-moz-appearance:none;background-image:url('${chevronSVGImageURL}');background-repeat:no-repeat;background-position:right 0.75rem center;background-size:12px;margin-bottom:1rem;}select.__jsx-style-dynamic-selector:hover{border-color:#d1d5db;}select.__jsx-style-dynamic-selector:focus{border-color:#9ca3af;box-shadow:0 0 0 2px rgba(229,231,235,0.9);}select.__jsx-style-dynamic-selector:disabled{color:#9ca3af;background-color:#f9fafb;cursor:not-allowed;}option.__jsx-style-dynamic-selector:disabled{color:#9ca3af;}`));
|
|
37
37
|
}
|
package/dist/ui/tag.js
CHANGED
|
@@ -14,9 +14,9 @@ function getContrastYIQ(hexcolor) {
|
|
|
14
14
|
export const Tag = props => {
|
|
15
15
|
const textColor = getContrastYIQ(props.color ?? '#ffffff');
|
|
16
16
|
return /*#__PURE__*/React.createElement("div", {
|
|
17
|
-
className: _JSXStyle.dynamic([["
|
|
17
|
+
className: _JSXStyle.dynamic([["2236124549", [fonts.primary, props.big ? '14px' : '0.8em', textColor === 'black' ? colors.enterDarkGrey : 'white', props.color ? props.color : 'transparent', colors.enterGrey, props.big ? '7px 10px 5px 0' : '0 0 0.5em 0.5em']]]) + " " + 'tag'
|
|
18
18
|
}, props.children, /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
19
|
-
id: "
|
|
19
|
+
id: "2236124549",
|
|
20
20
|
dynamic: [fonts.primary, props.big ? '14px' : '0.8em', textColor === 'black' ? colors.enterDarkGrey : 'white', props.color ? props.color : 'transparent', colors.enterGrey, props.big ? '7px 10px 5px 0' : '0 0 0.5em 0.5em']
|
|
21
|
-
}, `.tag.__jsx-style-dynamic-selector{font-family:${fonts.primary};font-size:${props.big ? '14px' : '0.8em'};color:${textColor === 'black' ? colors.enterDarkGrey : 'white'};text-transform:uppercase;display:inline-block;background-color:${props.color ? props.color : 'transparent'};border-radius:2px;border:1px solid ${colors.enterGrey};padding:2px 8px;margin:${props.big ? '7px 10px 5px 0' : '0 0 0.5em 0.5em'};word-wrap:anywhere;}`));
|
|
21
|
+
}, `.tag.__jsx-style-dynamic-selector{font-family:${fonts.primary};font-size:${props.big ? '14px' : '0.8em'};color:${textColor === 'black' ? colors.enterDarkGrey : 'white'};text-transform:uppercase;display:inline-block;background-color:${props.color ? props.color : 'transparent'};border-radius:2px;border:1px solid ${colors.enterGrey};padding:2px 8px;margin:${props.big ? '7px 10px 5px 0' : '0 0 0.5em 0.5em'};word-wrap:anywhere;border-radius:8px;}`));
|
|
22
22
|
};
|