@invopop/popui 0.1.50 → 0.1.52

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.
@@ -34,6 +34,7 @@
34
34
  disabled,
35
35
  stackedLeft,
36
36
  stackedRight,
37
+ shortcut,
37
38
  onclick,
38
39
  class: `${fullWidthClass} ${className || ''}`,
39
40
  ...rest
@@ -12,6 +12,7 @@
12
12
  placement = 'bottom-start',
13
13
  matchParentWidth = false,
14
14
  usePortal = true,
15
+ strategy: strategyProp,
15
16
  class: className = '',
16
17
  trigger,
17
18
  children,
@@ -19,7 +20,7 @@
19
20
  }: BaseDropdownProps = $props()
20
21
 
21
22
  // Conditional portal action - noop if disabled
22
- const conditionalPortal = usePortal ? portal : () => {}
23
+ const conditionalPortal = $derived(usePortal ? portal : () => {})
23
24
 
24
25
  const middleware = [offset(6), flip(), shift()]
25
26
 
@@ -38,8 +39,8 @@
38
39
 
39
40
  let closedFromClickOutside = $state(false)
40
41
 
41
- // Create floating actions with strategy based on usePortal
42
- const strategy = usePortal ? 'absolute' : 'fixed'
42
+ // Create floating actions with strategy based on prop or usePortal
43
+ const strategy = $derived(strategyProp ?? (usePortal ? 'absolute' : 'fixed'))
43
44
  const [floatingRef, floatingContent] = createFloatingActions({
44
45
  strategy,
45
46
  placement,
@@ -1,12 +1,21 @@
1
1
  <script lang="ts">
2
2
  import 'flag-icons/css/flag-icons.min.css'
3
3
  import type { BaseFlagProps } from './types'
4
+ import { Icon } from '@steeze-ui/svelte-icon'
5
+ import { World } from '@invopop/ui-icons'
4
6
 
5
7
  let { country = '' }: BaseFlagProps = $props()
6
8
 
7
9
  let iso = $derived(country.toLowerCase())
10
+ let isGlobal = $derived(country.toUpperCase() === 'GLOBAL')
8
11
  </script>
9
12
 
10
- <div class="w-3.5 h-2.5 rounded-[1.5px] overflow-hidden flex items-center justify-center">
11
- <span class="fi fi-{country.toLocaleLowerCase()} text-[14px] leading-none"></span>
12
- </div>
13
+ {#if isGlobal}
14
+ <div class="w-3.5 h-2.5 flex items-center justify-center">
15
+ <Icon src={World} class="size-3.5 text-icon" />
16
+ </div>
17
+ {:else}
18
+ <div class="w-3.5 h-2.5 rounded-[1.5px] overflow-hidden flex items-center justify-center">
19
+ <span class="fi fi-{country.toLocaleLowerCase()} text-[14px] leading-none"></span>
20
+ </div>
21
+ {/if}
@@ -19,12 +19,10 @@
19
19
  }: CardCheckboxProps = $props()
20
20
 
21
21
  let containerStyles = $derived(
22
- clsx('border gap-3', {
22
+ clsx('border gap-3 rounded-xl', {
23
23
  'border-foreground-selected': checked,
24
24
  'border-border': !checked,
25
- 'bg-background-default-secondary': disabled,
26
- 'rounded-lg': hideRadio,
27
- 'rounded-xl': !hideRadio
25
+ 'bg-background-default-secondary': disabled
28
26
  })
29
27
  )
30
28
 
@@ -39,11 +37,11 @@
39
37
  <label for={id} class="cursor-pointer">
40
38
  <div class={containerStyles}>
41
39
  <div class={headerStyles}>
42
- <div class="flex grow items-start gap-1 min-w-0">
40
+ <div class="flex grow items-start gap-1 min-w-0" data-card-content>
43
41
  {#if icon}
44
- <Icon src={icon} class="size-4 text-icon shrink-0 mt-0.5" />
42
+ <Icon src={icon} class="size-4 text-icon shrink-0 mt-0.5" data-card-icon />
45
43
  {/if}
46
- <div class="flex flex-col gap-1 min-w-0">
44
+ <div class="flex flex-col gap-1 min-w-0" data-card-text>
47
45
  <span class="text-base font-medium text-foreground">
48
46
  {title}
49
47
  </span>
@@ -42,6 +42,7 @@
42
42
  draggable = false,
43
43
  widthClass = 'w-60',
44
44
  collapsibleGroups = true,
45
+ flagPosition = 'after',
45
46
  onclick,
46
47
  onselect,
47
48
  onreorder,
@@ -473,7 +474,7 @@
473
474
  {:else}
474
475
  <div class:px-1={!item.groupBy} class:cursor-grab={draggable && !item.locked}>
475
476
  <DrawerContextItem
476
- item={{ ...item, focused: item.value === focusedItemValue }}
477
+ item={{ ...item, focused: item.value === focusedItemValue, flagPosition: item.flagPosition || flagPosition }}
477
478
  {multiple}
478
479
  {onclick}
479
480
  onchange={updateItem}
@@ -76,6 +76,8 @@
76
76
  <ProfileAvatar name={item?.label || ''} picture={item?.picture || ''} variant="sm" />
77
77
  {:else if item?.picture}
78
78
  <ProfileAvatar name={item?.label || ''} picture={item?.picture} variant="sm" />
79
+ {:else if typeof item?.icon === 'string'}
80
+ <img src={item.icon} alt="" class="w-4 h-4 text-icon {item?.locked ? 'opacity-30' : ''}" />
79
81
  {:else if item?.icon}
80
82
  <Icon
81
83
  src={item.icon}
@@ -91,9 +93,12 @@
91
93
  {#if item?.color}
92
94
  <TagStatus status={item.color} dot />
93
95
  {/if}
96
+ {#if item?.country && item?.flagPosition === 'before'}
97
+ <BaseFlag country={item.country} />
98
+ {/if}
94
99
  <span class="{labelStyles} text-base font-medium truncate">{item?.label || ''}</span>
95
100
 
96
- {#if item?.country}
101
+ {#if item?.country && item?.flagPosition === 'after'}
97
102
  <BaseFlag country={item.country} />
98
103
  <span class="text-xs font-medium text-foreground-default-secondary uppercase">
99
104
  {item.country}
@@ -6,6 +6,7 @@
6
6
  import DrawerContext from './DrawerContext.svelte'
7
7
  import clsx from 'clsx'
8
8
  import TagStatus from './TagStatus.svelte'
9
+ import BaseFlag from './BaseFlag.svelte'
9
10
  import { resolveIcon } from './helpers.js'
10
11
  import { buttonVariants } from './button/button.svelte'
11
12
 
@@ -18,11 +19,13 @@
18
19
  multiple = false,
19
20
  fullWidth = false,
20
21
  widthClass = 'min-w-[160px] max-w-[420px]',
22
+ flagPosition = 'before',
21
23
  onSelect,
22
24
  onOpenChange,
23
25
  stackLeft = false,
24
26
  stackRight = false,
25
- multipleLabel = 'items'
27
+ multipleLabel = 'items',
28
+ strategy
26
29
  }: DropdownSelectProps = $props()
27
30
 
28
31
  let selectDropdown: BaseDropdown | undefined = $state()
@@ -126,6 +129,7 @@
126
129
  bind:isOpen
127
130
  placement="bottom-start"
128
131
  {fullWidth}
132
+ {strategy}
129
133
  bind:this={selectDropdown}
130
134
  class={fullWidth || isStacked ? '' : widthClass}
131
135
  >
@@ -151,9 +155,16 @@
151
155
  <TagStatus dot status={selectedColor} />
152
156
  {@render label()}
153
157
  </div>
158
+ {:else if !multiple && selectedItem?.country}
159
+ <div class="flex items-center gap-1 flex-1 min-w-0">
160
+ <BaseFlag country={selectedItem.country} />
161
+ {@render label()}
162
+ </div>
154
163
  {:else if selectedIcon || resolvedIcon}
155
164
  <div class="flex items-center gap-1 flex-1 min-w-0">
156
- {#if selectedIcon}
165
+ {#if typeof selectedIcon === 'string'}
166
+ <img src={selectedIcon} alt="" class="size-4 flex-shrink-0 text-icon" />
167
+ {:else if selectedIcon}
157
168
  <Icon src={selectedIcon} {iconTheme} class="{selectedIconColor} size-4 flex-shrink-0" />
158
169
  {:else if resolvedIcon}
159
170
  <Icon src={resolvedIcon} {iconTheme} class="text-icon size-4 flex-shrink-0" />
@@ -169,6 +180,7 @@
169
180
  widthClass="min-w-[256px]"
170
181
  {multiple}
171
182
  {items}
183
+ {flagPosition}
172
184
  onclick={handleClick}
173
185
  onselect={handleSelected}
174
186
  />
@@ -170,11 +170,13 @@
170
170
  iconClass?: string
171
171
  stackedLeft?: boolean
172
172
  stackedRight?: boolean
173
+ shortcut?: boolean
173
174
  }
174
175
  </script>
175
176
 
176
177
  <script lang="ts">
177
178
  import { Icon } from '@steeze-ui/svelte-icon'
179
+ import ShortcutWrapper from '../ShortcutWrapper.svelte'
178
180
 
179
181
  let {
180
182
  class: className,
@@ -185,6 +187,7 @@
185
187
  iconClass = '',
186
188
  stackedLeft = false,
187
189
  stackedRight = false,
190
+ shortcut = false,
188
191
  ref = $bindable(null),
189
192
  href = undefined,
190
193
  type = 'button',
@@ -211,7 +214,7 @@
211
214
  )
212
215
  </script>
213
216
 
214
- {#snippet iconContent()}
217
+ {#snippet iconElement()}
215
218
  {#if icon}
216
219
  <div class={cn('relative z-10', iconClass && `[&_svg]:${iconClass}`)}>
217
220
  <Icon src={icon} class={iconSize} />
@@ -219,6 +222,18 @@
219
222
  {/if}
220
223
  {/snippet}
221
224
 
225
+ {#snippet iconContent()}
226
+ {#if shortcut}
227
+ <ShortcutWrapper
228
+ theme={['dark', 'primary', 'danger'].includes(variant) ? 'navigation' : 'light'}
229
+ >
230
+ {@render iconElement()}
231
+ </ShortcutWrapper>
232
+ {:else}
233
+ {@render iconElement()}
234
+ {/if}
235
+ {/snippet}
236
+
222
237
  {#snippet buttonContent()}
223
238
  <div
224
239
  class={cn(
@@ -112,6 +112,7 @@ export type ButtonProps = WithElementRef<HTMLButtonAttributes> & WithElementRef<
112
112
  iconClass?: string;
113
113
  stackedLeft?: boolean;
114
114
  stackedRight?: boolean;
115
+ shortcut?: boolean;
115
116
  };
116
117
  declare const Button: import("svelte").Component<ButtonProps, {}, "ref">;
117
118
  type Button = ReturnType<typeof Button>;
@@ -4,14 +4,15 @@
4
4
  * This file can be imported in any project using:
5
5
  * @import "@invopop/popui/tailwind.theme.css";
6
6
  *
7
+ * To use the Geist Mono font, add this to your HTML or main CSS file:
8
+ * @import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&display=swap');
9
+ *
7
10
  * Three-tier color system:
8
11
  * 1. Primitive colors: Raw color values from Figma (e.g., mint-100)
9
12
  * 2. Semantic colors: Named palettes using base colors (e.g., positive-100)
10
13
  * 3. Token colors: Application-specific using semantic colors (e.g., foreground-default)
11
14
  */
12
15
 
13
- @import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&display=swap');
14
-
15
16
  @theme {
16
17
  /* ============================================
17
18
  PRIMITIVE COLORS
package/dist/types.d.ts CHANGED
@@ -30,7 +30,7 @@ export type DrawerOption = SelectOption & {
30
30
  destructive?: boolean;
31
31
  selected?: boolean;
32
32
  focused?: boolean;
33
- icon?: IconSource | undefined;
33
+ icon?: IconSource | string | undefined;
34
34
  rightIcon?: IconSource | undefined;
35
35
  country?: string;
36
36
  color?: StatusType;
@@ -42,6 +42,7 @@ export type DrawerOption = SelectOption & {
42
42
  groupBy?: string;
43
43
  useAvatar?: boolean;
44
44
  action?: Snippet<[DrawerOption]>;
45
+ flagPosition?: 'before' | 'after';
45
46
  };
46
47
  export type Company = {
47
48
  id: string;
@@ -215,6 +216,7 @@ export interface BaseDropdownProps {
215
216
  placement?: Placement;
216
217
  matchParentWidth?: boolean;
217
218
  usePortal?: boolean;
219
+ strategy?: 'absolute' | 'fixed';
218
220
  trigger?: Snippet;
219
221
  children?: Snippet;
220
222
  [key: string]: any;
@@ -332,6 +334,7 @@ export interface DrawerContextProps {
332
334
  draggable?: boolean;
333
335
  widthClass?: string;
334
336
  collapsibleGroups?: boolean;
337
+ flagPosition?: 'before' | 'after';
335
338
  onclick?: (value: AnyProp) => void;
336
339
  onselect?: (selected: DrawerOption[]) => void;
337
340
  onreorder?: (items: DrawerOption[]) => void;
@@ -356,11 +359,13 @@ export interface DropdownSelectProps {
356
359
  multiple?: boolean;
357
360
  fullWidth?: boolean;
358
361
  widthClass?: string;
362
+ flagPosition?: 'before' | 'after';
359
363
  onSelect?: (value: AnyProp) => void;
360
364
  onOpenChange?: (isOpen: boolean) => void;
361
365
  stackLeft?: boolean;
362
366
  stackRight?: boolean;
363
367
  multipleLabel?: string;
368
+ strategy?: 'absolute' | 'fixed';
364
369
  }
365
370
  export interface EmptyStateProps {
366
371
  icon?: Snippet;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@invopop/popui",
3
3
  "license": "MIT",
4
- "version": "0.1.50",
4
+ "version": "0.1.52",
5
5
  "repository": {
6
6
  "url": "https://github.com/invopop/popui"
7
7
  },
@@ -85,7 +85,7 @@
85
85
  "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.5",
86
86
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
87
87
  "@floating-ui/core": "^1.5.1",
88
- "@invopop/ui-icons": "^0.0.82",
88
+ "@invopop/ui-icons": "^0.0.84",
89
89
  "@steeze-ui/heroicons": "^2.2.3",
90
90
  "@steeze-ui/svelte-icon": "^1.6.2",
91
91
  "@tailwindcss/vite": "^4.1.12",