@alfakit/vue 0.1.8 → 0.1.10
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/CHANGELOG.md +14 -0
- package/dist/app/components.d.ts.map +1 -1
- package/dist/app/components.js +1 -0
- package/dist/app/components.js.map +1 -1
- package/dist/components/actions/ActionModal.js.map +1 -1
- package/dist/components/actions/ActionModal.vue.d.ts.map +1 -1
- package/dist/components/actions/ActionModal.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/actions/ActionModal.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/dev-tools/DevToolsInspector.js.map +1 -1
- package/dist/components/dev-tools/DevToolsInspector.vue.d.ts.map +1 -1
- package/dist/components/dev-tools/DevToolsInspector.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/dev-tools/DevToolsInspector.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/CheckboxListField.js.map +1 -1
- package/dist/components/fields/CheckboxListField.vue.d.ts.map +1 -1
- package/dist/components/fields/CheckboxListField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/fields/CheckboxListField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/DatePickerField.js.map +1 -1
- package/dist/components/fields/DatePickerField.vue.d.ts.map +1 -1
- package/dist/components/fields/DatePickerField.vue_vue_type_script_setup_true_lang.js +34 -32
- package/dist/components/fields/DatePickerField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/RichEditorSuggestions.js.map +1 -1
- package/dist/components/fields/RichEditorSuggestions.vue.d.ts +1 -1
- package/dist/components/fields/RichEditorSuggestions.vue.d.ts.map +1 -1
- package/dist/components/fields/RichEditorSuggestions.vue_vue_type_script_setup_true_lang.js +29 -23
- package/dist/components/fields/RichEditorSuggestions.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/RichEditorToolbarGroup.js.map +1 -1
- package/dist/components/fields/RichEditorToolbarGroup.vue.d.ts.map +1 -1
- package/dist/components/fields/RichEditorToolbarGroup.vue_vue_type_script_setup_true_lang.js +9 -4
- package/dist/components/fields/RichEditorToolbarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/file-manager/FileManagerView.js.map +1 -1
- package/dist/components/file-manager/FileManagerView.vue.d.ts.map +1 -1
- package/dist/components/file-manager/FileManagerView.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/components/file-manager/FileManagerView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/FormBlock.js.map +1 -1
- package/dist/components/form/FormBlock.vue.d.ts.map +1 -1
- package/dist/components/form/FormBlock.vue_vue_type_script_setup_true_lang.js +79 -78
- package/dist/components/form/FormBlock.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/CalloutComponent.js.map +1 -1
- package/dist/components/layout/CalloutComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/CalloutComponent.vue_vue_type_script_setup_true_lang.js +43 -39
- package/dist/components/layout/CalloutComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/EmptyStateComponent.js.map +1 -1
- package/dist/components/layout/EmptyStateComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/EmptyStateComponent.vue_vue_type_script_setup_true_lang.js +29 -26
- package/dist/components/layout/EmptyStateComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/MixedContent.js.map +1 -1
- package/dist/components/layout/MixedContent.vue.d.ts +5 -3
- package/dist/components/layout/MixedContent.vue.d.ts.map +1 -1
- package/dist/components/layout/MixedContent.vue_vue_type_script_setup_true_lang.js +5 -4
- package/dist/components/layout/MixedContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/SectionComponent.js.map +1 -1
- package/dist/components/layout/SectionComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/SectionComponent.vue_vue_type_script_setup_true_lang.js +12 -8
- package/dist/components/layout/SectionComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/notifications/NotificationsMenu.js.map +1 -1
- package/dist/components/notifications/NotificationsMenu.vue.d.ts.map +1 -1
- package/dist/components/notifications/NotificationsMenu.vue_vue_type_script_setup_true_lang.js +133 -104
- package/dist/components/notifications/NotificationsMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/BottomNav.js.map +1 -1
- package/dist/components/panel/BottomNav.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/panel/BottomNav.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/KeyboardShortcuts.js.map +1 -1
- package/dist/components/panel/KeyboardShortcuts.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/panel/KeyboardShortcuts.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavGroup.js.map +1 -1
- package/dist/components/panel/NavGroup.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/panel/NavGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavItem.js.map +1 -1
- package/dist/components/panel/NavItem.vue.d.ts.map +1 -1
- package/dist/components/panel/NavItem.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/panel/NavItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavSubItem.js.map +1 -1
- package/dist/components/panel/NavSubItem.vue.d.ts.map +1 -1
- package/dist/components/panel/NavSubItem.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/panel/NavSubItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/ShortcutsOverlay.js.map +1 -1
- package/dist/components/panel/ShortcutsOverlay.vue.d.ts.map +1 -1
- package/dist/components/panel/ShortcutsOverlay.vue_vue_type_script_setup_true_lang.js +20 -19
- package/dist/components/panel/ShortcutsOverlay.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/ColumnManager.js.map +1 -1
- package/dist/components/table/ColumnManager.vue.d.ts.map +1 -1
- package/dist/components/table/ColumnManager.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/table/ColumnManager.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/Spreadsheet.js.map +1 -1
- package/dist/components/table/Spreadsheet.vue.d.ts.map +1 -1
- package/dist/components/table/Spreadsheet.vue_vue_type_script_setup_true_lang.js +37 -33
- package/dist/components/table/Spreadsheet.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/translation-manager/TranslationManagerView.js.map +1 -1
- package/dist/components/translation-manager/TranslationManagerView.vue.d.ts.map +1 -1
- package/dist/components/translation-manager/TranslationManagerView.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/translation-manager/TranslationManagerView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/SidebarMenuButton.js.map +1 -1
- package/dist/components/ui/SidebarMenuButton.vue.d.ts.map +1 -1
- package/dist/components/ui/SidebarMenuButton.vue_vue_type_script_setup_true_lang.js +12 -12
- package/dist/components/ui/SidebarMenuButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/TooltipTrigger.js.map +1 -1
- package/dist/components/ui/TooltipTrigger.vue.d.ts +2 -2
- package/dist/components/ui/TooltipTrigger.vue.d.ts.map +1 -1
- package/dist/components/ui/TooltipTrigger.vue_vue_type_script_setup_true_lang.js +10 -7
- package/dist/components/ui/TooltipTrigger.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/widgets/StatCard.js.map +1 -1
- package/dist/components/widgets/StatCard.vue.d.ts.map +1 -1
- package/dist/components/widgets/StatCard.vue_vue_type_script_setup_true_lang.js +30 -30
- package/dist/components/widgets/StatCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/eject-manifest.json +2 -2
- package/dist/layouts/TopNavLayout.js.map +1 -1
- package/dist/layouts/TopNavLayout.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/layouts/TopNavLayout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/layouts/TopNavMenu.js.map +1 -1
- package/dist/layouts/TopNavMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/layouts/TopNavMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/pages/AlfakitPage.js.map +1 -1
- package/dist/pages/AlfakitPage.vue.d.ts.map +1 -1
- package/dist/pages/AlfakitPage.vue_vue_type_script_setup_true_lang.js +16 -19
- package/dist/pages/AlfakitPage.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/standalone/AlfakitComponent.js.map +1 -1
- package/dist/standalone/AlfakitComponent.vue.d.ts.map +1 -1
- package/dist/standalone/AlfakitComponent.vue_vue_type_script_setup_true_lang.js +29 -24
- package/dist/standalone/AlfakitComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/standalone/runtime.d.ts.map +1 -1
- package/dist/standalone/runtime.js +25 -25
- package/dist/standalone/runtime.js.map +1 -1
- package/dist/standalone.css +12 -7
- package/dist/starter-kit/AlfakitStarterKitRoot.js.map +1 -1
- package/dist/starter-kit/AlfakitStarterKitRoot.vue.d.ts.map +1 -1
- package/dist/starter-kit/AlfakitStarterKitRoot.vue_vue_type_script_setup_true_lang.js +25 -21
- package/dist/starter-kit/AlfakitStarterKitRoot.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/starter-kit/navigation.d.ts +6 -1
- package/dist/starter-kit/navigation.d.ts.map +1 -1
- package/dist/starter-kit/navigation.js +11 -10
- package/dist/starter-kit/navigation.js.map +1 -1
- package/dist/styles.css +2 -1
- package/dist/themes.css +4 -0
- package/package.json +2 -2
- package/src/app/components.ts +3 -0
- package/src/components/actions/ActionModal.vue +2 -0
- package/src/components/dev-tools/DevToolsInspector.vue +1 -1
- package/src/components/fields/CheckboxListField.vue +2 -2
- package/src/components/fields/DatePickerField.vue +1 -0
- package/src/components/fields/RichEditorSuggestions.vue +13 -6
- package/src/components/fields/RichEditorToolbarGroup.vue +16 -1
- package/src/components/file-manager/FileManagerView.vue +3 -1
- package/src/components/form/FormBlock.vue +15 -2
- package/src/components/layout/CalloutComponent.vue +15 -2
- package/src/components/layout/EmptyStateComponent.vue +10 -1
- package/src/components/layout/MixedContent.vue +12 -4
- package/src/components/layout/SectionComponent.vue +20 -4
- package/src/components/notifications/NotificationsMenu.vue +45 -1
- package/src/components/panel/BottomNav.vue +2 -2
- package/src/components/panel/KeyboardShortcuts.vue +1 -1
- package/src/components/panel/NavGroup.vue +3 -3
- package/src/components/panel/NavItem.vue +5 -1
- package/src/components/panel/NavSubItem.vue +5 -1
- package/src/components/panel/ShortcutsOverlay.vue +27 -22
- package/src/components/table/ColumnManager.vue +6 -4
- package/src/components/table/Spreadsheet.vue +9 -4
- package/src/components/translation-manager/TranslationManagerView.vue +1 -1
- package/src/components/ui/SidebarMenuButton.vue +6 -0
- package/src/components/ui/TooltipTrigger.vue +4 -1
- package/src/components/widgets/StatCard.vue +6 -3
- package/src/layouts/TopNavLayout.vue +2 -2
- package/src/layouts/TopNavMenu.vue +1 -1
- package/src/pages/AlfakitPage.vue +13 -3
- package/src/standalone/AlfakitComponent.vue +16 -5
- package/src/standalone/runtime.ts +3 -0
- package/src/starter-kit/AlfakitStarterKitRoot.vue +7 -0
- package/src/starter-kit/navigation.ts +12 -1
- package/src/styles.css +2 -1
package/dist/components/layout/EmptyStateComponent.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyStateComponent.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_mergeProps","_unref","_normalizeClass","_createVNode","Icon","_createCommentVNode","_toDisplayString","_createBlock","ActionList","MixedContent","Icon_default","ActionList_default","MixedContent_default","_hoisted_1","_hoisted_2","EmptyStateComponent_vue_vue_type_script_setup_true_lang_default","props","__props","
|
|
1
|
+
{"version":3,"file":"EmptyStateComponent.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_mergeProps","_unref","_normalizeClass","_createVNode","Icon","_createCommentVNode","_toDisplayString","_createBlock","ActionList","MixedContent","Icon_default","ActionList_default","MixedContent_default","_hoisted_1","_hoisted_2","EmptyStateComponent_vue_vue_type_script_setup_true_lang_default","props","__props","form","useOptionalFormStore","pageContext","usePageActionContext","fieldContext","useFieldActionContext","context","computed","openBlock","createElementBlock","mergeProps","unref","cn","normalizeClass","colorClass","iconBackgroundColorClasses","createVNode","createCommentVNode","toDisplayString","createBlock"],"sources":["../../../src/components/layout/EmptyStateComponent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { EmptyStateComponentSchema } from '@alfakit/core'\nimport { computed } from 'vue'\nimport { colorClass, iconBackgroundColorClasses } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport { useOptionalFormStore } from '../form/form-context'\nimport ActionList from '../actions/ActionList.vue'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport MixedContent from './MixedContent.vue'\n\nconst props = defineProps<{ component: EmptyStateComponentSchema }>()\n// Inside a form the actions run as field actions of this component, so `$get` / `$set`\n// reach the form (like a section's header actions).\nconst form = useOptionalFormStore()\nconst pageContext = usePageActionContext()\nconst fieldContext = useFieldActionContext(() => props.component)\nconst context = computed(() => (form ? fieldContext.value : pageContext))\n</script>\n\n<template>\n <div\n data-slot=\"empty-state\"\n :class=\"\n cn(\n 'flex flex-col items-center justify-center gap-3 px-6 py-10 text-center',\n props.component.contained && 'rounded-xl border border-dashed',\n )\n \"\n v-bind=\"props.component.extraAttributes\"\n >\n <div\n v-if=\"props.component.icon\"\n :class=\"\n cn(\n 'flex size-12 items-center justify-center rounded-full',\n colorClass(iconBackgroundColorClasses, props.component.color),\n )\n \"\n >\n <Icon :name=\"props.component.icon\" class=\"size-6\" />\n </div>\n <h3 v-if=\"props.component.heading\" class=\"text-base font-semibold\">\n {{ props.component.heading }}\n </h3>\n <div\n v-if=\"props.component.description\"\n class=\"text-muted-foreground max-w-md text-sm\"\n v-html=\"props.component.description\"\n />\n <ActionList\n v-if=\"props.component.actions && props.component.actions.length > 0\"\n :actions=\"props.component.actions\"\n :context=\"context\"\n class=\"mt-2 justify-center\"\n />\n <MixedContent\n :items=\"props.component.footer\"\n :context=\"context\"\n name=\"empty-state-footer\"\n class=\"justify-center\"\n />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;EAYA,IAAMkB,IAAQC,GAGRC,IAAOC,EAAqB,GAC5BC,IAAcC,EAAqB,GACnCC,IAAeC,QAA4BP,EAAM,SAAS,GAC1DQ,IAAUC,QAAgBP,IAAOI,EAAa,QAAQF,CAAY;oBAItEtB,EAAA,GAAAC,EAyCM,OAzCNC,EAyCM;GAxCJ,aAAU;GACT,OAAcC,EAAA6B,CAAA,CAAE,CAAA,0EAA4Fd,EAAM,UAAU,aAAS,iCAAA;EAM9H,GAAAA,EAAM,UAAU,eAAe,GAAA;GAG/BA,EAAM,UAAU,QADxBlB,EAAA,GAAAC,EAUM,OAAA;;IARH,OAAKG,EAAWD,EAAA6B,CAAA,CAAE,CAAA,yDAA+E7B,EAAA+B,CAAA,CAAU,CAAC/B,EAAAgC,CAAA,GAA4BjB,EAAM,UAAU,KAAK,CAAA,CAAA;GAO9Jb,GAAAA,CAAAA,EAAoDF,EAAAG,CAAA,GAAA;IAA7C,MAAMY,EAAM,UAAU;IAAM,OAAM;;GAEjCA,EAAM,UAAU,WAA1BlB,EAAA,GAAAC,EAEK,MAFLc,GAEKP,EADAU,EAAM,UAAU,OAAO,GAAA,CAAA,KAAAmB,EAAA,IAAA,EAAA;GAGpBnB,EAAM,UAAU,eADxBlB,EAAA,GAAAC,EAIE,OAAA;;IAFA,OAAM;IACN,WAAQiB,EAAM,UAAU;;GAGlBA,EAAM,UAAU,WAAWA,EAAM,UAAU,QAAQ,SAAM,KADjElB,EAAA,GAAAS,EAKEC,GAAA;;IAHC,SAASQ,EAAM,UAAU;IACzB,SAASQ,EAAA;IACV,OAAM;;GAERrB,EAKEM,GAAA;IAJC,OAAOO,EAAM,UAAU;IACvB,SAASQ,EAAA;IACV,MAAK;IACL,OAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MixedContent.js","names":["_sfc_main","MixedContent_vue_vue_type_script_setup_true_lang_default","MixedContent_default"],"sources":["../../../src/components/layout/MixedContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {
|
|
1
|
+
{"version":3,"file":"MixedContent.js","names":["_sfc_main","MixedContent_vue_vue_type_script_setup_true_lang_default","MixedContent_default"],"sources":["../../../src/components/layout/MixedContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {\n ActionContext,\n AnyActionSchema,\n ContentSlotItem,\n SchemaComponent,\n} from '@alfakit/core'\nimport { computed, type HTMLAttributes } from 'vue'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport ActionList from '../actions/ActionList.vue'\nimport RenderComponent from '../schema/RenderComponent.vue'\n\n/**\n * Actions and components placed in a layout component's fixed spot (a section's\n * `afterHeader()`, a callout's `controls()`). Actions run in the owner's action context\n * (`context`: inside a form, the owner's field context, so `$get` / `$set` work), else the\n * page's.\n */\nconst props = defineProps<{\n items: ContentSlotItem[] | undefined\n name: string\n class?: HTMLAttributes['class']\n context?: ActionContext\n}>()\n\nconst pageContext = usePageActionContext()\nconst context = computed(() => props.context ?? pageContext)\n\ntype Group =\n | { kind: 'actions'; key: string; actions: AnyActionSchema[] }\n | { kind: 'component'; key: string; component: SchemaComponent }\n\n// Consecutive actions share one action list so they sit together.\nconst groups = computed<Group[]>(() => {\n const result: Group[] = []\n\n for (const item of props.items ?? []) {\n if (item.action) {\n const last = result[result.length - 1]\n\n if (last?.kind === 'actions') {\n last.actions.push(item.action)\n } else {\n result.push({ kind: 'actions', key: `actions-${result.length}`, actions: [item.action] })\n }\n } else {\n result.push({ kind: 'component', key: item.component.key, component: item.component })\n }\n }\n\n return result\n})\n</script>\n\n<template>\n <div\n v-if=\"props.items?.length\"\n :data-slot=\"props.name\"\n :class=\"cn('flex flex-wrap items-center gap-2', props.class)\"\n >\n <template v-for=\"group in groups\" :key=\"group.key\">\n <ActionList\n v-if=\"group.kind === 'actions'\"\n :actions=\"group.actions\"\n :context=\"context\"\n size=\"sm\"\n />\n <RenderComponent v-else :component=\"group.component\" />\n </template>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { ContentSlotItem } from '@alfakit/core';
|
|
1
|
+
import type { ActionContext, ContentSlotItem } from '@alfakit/core';
|
|
2
2
|
import { type HTMLAttributes } from 'vue';
|
|
3
3
|
/**
|
|
4
4
|
* Actions and components placed in a layout component's fixed spot (a section's
|
|
5
|
-
* `afterHeader()`, a callout's `controls()`). Actions run in the
|
|
6
|
-
*
|
|
5
|
+
* `afterHeader()`, a callout's `controls()`). Actions run in the owner's action context
|
|
6
|
+
* (`context`: inside a form, the owner's field context, so `$get` / `$set` work), else the
|
|
7
|
+
* page's.
|
|
7
8
|
*/
|
|
8
9
|
type __VLS_Props = {
|
|
9
10
|
items: ContentSlotItem[] | undefined;
|
|
10
11
|
name: string;
|
|
11
12
|
class?: HTMLAttributes['class'];
|
|
13
|
+
context?: ActionContext;
|
|
12
14
|
};
|
|
13
15
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
16
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MixedContent.vue.d.ts","sourceRoot":"","sources":["../../../src/components/layout/MixedContent.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MixedContent.vue.d.ts","sourceRoot":"","sources":["../../../src/components/layout/MixedContent.vue"],"names":[],"mappings":"AA0EA,OAAO,KAAK,EACV,aAAa,EAEb,eAAe,EAEhB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,KAAK,CAAA;AAMnD;;;;;GAKG;AACH,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,eAAe,EAAE,GAAG,SAAS,CAAA;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IAC/B,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB,CAAC;;AAgGF,wBAMG"}
|
|
@@ -15,10 +15,11 @@ var m = ["data-slot"], h = /*@__PURE__*/ l({
|
|
|
15
15
|
String,
|
|
16
16
|
Object,
|
|
17
17
|
Array
|
|
18
|
-
] }
|
|
18
|
+
] },
|
|
19
|
+
context: {}
|
|
19
20
|
},
|
|
20
21
|
setup(l) {
|
|
21
|
-
let h = l, g = e(), _ = a(() => {
|
|
22
|
+
let h = l, g = e(), _ = a(() => h.context ?? g), v = a(() => {
|
|
22
23
|
let e = [];
|
|
23
24
|
for (let t of h.items ?? []) if (t.action) {
|
|
24
25
|
let n = e[e.length - 1];
|
|
@@ -38,10 +39,10 @@ var m = ["data-slot"], h = /*@__PURE__*/ l({
|
|
|
38
39
|
key: 0,
|
|
39
40
|
"data-slot": h.name,
|
|
40
41
|
class: u(p(t)("flex flex-wrap items-center gap-2", h.class))
|
|
41
|
-
}, [(d(!0), c(i, null, f(
|
|
42
|
+
}, [(d(!0), c(i, null, f(v.value, (e) => (d(), c(i, { key: e.key }, [e.kind === "actions" ? (d(), o(n, {
|
|
42
43
|
key: 0,
|
|
43
44
|
actions: e.actions,
|
|
44
|
-
context:
|
|
45
|
+
context: _.value,
|
|
45
46
|
size: "sm"
|
|
46
47
|
}, null, 8, ["actions", "context"])) : (d(), o(r, {
|
|
47
48
|
key: 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MixedContent.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_Fragment","_renderList","_createBlock","ActionList","RenderComponent","_createCommentVNode","ActionList_default","RenderComponent_default","_hoisted_1","MixedContent_vue_vue_type_script_setup_true_lang_default","props","__props","
|
|
1
|
+
{"version":3,"file":"MixedContent.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_Fragment","_renderList","_createBlock","ActionList","RenderComponent","_createCommentVNode","ActionList_default","RenderComponent_default","_hoisted_1","MixedContent_vue_vue_type_script_setup_true_lang_default","props","__props","pageContext","usePageActionContext","context","computed","groups","result","item","last","openBlock","createElementBlock","normalizeClass","unref","cn","Fragment","renderList","group","createBlock","createCommentVNode"],"sources":["../../../src/components/layout/MixedContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {\n ActionContext,\n AnyActionSchema,\n ContentSlotItem,\n SchemaComponent,\n} from '@alfakit/core'\nimport { computed, type HTMLAttributes } from 'vue'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport ActionList from '../actions/ActionList.vue'\nimport RenderComponent from '../schema/RenderComponent.vue'\n\n/**\n * Actions and components placed in a layout component's fixed spot (a section's\n * `afterHeader()`, a callout's `controls()`). Actions run in the owner's action context\n * (`context`: inside a form, the owner's field context, so `$get` / `$set` work), else the\n * page's.\n */\nconst props = defineProps<{\n items: ContentSlotItem[] | undefined\n name: string\n class?: HTMLAttributes['class']\n context?: ActionContext\n}>()\n\nconst pageContext = usePageActionContext()\nconst context = computed(() => props.context ?? pageContext)\n\ntype Group =\n | { kind: 'actions'; key: string; actions: AnyActionSchema[] }\n | { kind: 'component'; key: string; component: SchemaComponent }\n\n// Consecutive actions share one action list so they sit together.\nconst groups = computed<Group[]>(() => {\n const result: Group[] = []\n\n for (const item of props.items ?? []) {\n if (item.action) {\n const last = result[result.length - 1]\n\n if (last?.kind === 'actions') {\n last.actions.push(item.action)\n } else {\n result.push({ kind: 'actions', key: `actions-${result.length}`, actions: [item.action] })\n }\n } else {\n result.push({ kind: 'component', key: item.component.key, component: item.component })\n }\n }\n\n return result\n})\n</script>\n\n<template>\n <div\n v-if=\"props.items?.length\"\n :data-slot=\"props.name\"\n :class=\"cn('flex flex-wrap items-center gap-2', props.class)\"\n >\n <template v-for=\"group in groups\" :key=\"group.key\">\n <ActionList\n v-if=\"group.kind === 'actions'\"\n :actions=\"group.actions\"\n :context=\"context\"\n size=\"sm\"\n />\n <RenderComponent v-else :component=\"group.component\" />\n </template>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAmBA,IAAMc,IAAQC,GAORC,IAAcC,EAAqB,GACnCC,IAAUC,QAAeL,EAAM,WAAWE,CAAW,GAOrDI,IAASD,QAAwB;GACrC,IAAME,IAAkB,CAAC;GAEzB,KAAK,IAAMC,KAAQR,EAAM,SAAS,CAAC,GACjC,IAAIQ,EAAK,QAAQ;IACf,IAAMC,IAAOF,EAAOA,EAAO,SAAS;IAEpC,AAAIE,GAAM,SAAS,YACjBA,EAAK,QAAQ,KAAKD,EAAK,MAAM,IAE7BD,EAAO,KAAK;KAAE,MAAM;KAAW,KAAK,WAAWA,EAAO;KAAU,SAAS,CAACC,EAAK,MAAM;IAAE,CAAC;GAE5F,OACE,EAAO,KAAK;IAAE,MAAM;IAAa,KAAKA,EAAK,UAAU;IAAK,WAAWA,EAAK;GAAU,CAAC;GAIzF,OAAOD;EACT,CAAC;mBAKSP,EAAM,OAAO,UADrBd,EAAA,GAAAC,EAcM,OAAA;;GAZH,aAAWa,EAAM;GACjB,OAAKZ,EAAEC,EAAAyB,CAAA,CAAE,CAAA,qCAAsCd,EAAM,KAAK,CAAA;EAE3Dd,GAAAA,EAAAA,EAAA,EAAA,GAAAC,EAQWG,GAAA,MAAAC,EARee,EAAA,QAAT,OAAuBI,EAAA,GAAAC,EAAAI,GAAA,EAAA,KAAAE,EAAM,IAAA,GAAA,CAEpCA,EAAM,SAAI,aADlB/B,EAAA,GAAAM,EAKEC,GAAA;;GAHC,SAASwB,EAAM;GACf,SAASb,EAAA;GACV,MAAK;EAEPlB,GAAAA,MAAAA,GAAAA,CAAAA,WAAAA,SAAAA,CAAAA,MAAAA,EAAA,GAAAM,EAAuDE,GAAA;;GAA9B,WAAWuB,EAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionComponent.js","names":["_sfc_main","SectionComponent_vue_vue_type_script_setup_true_lang_default","SectionComponent_default"],"sources":["../../../src/components/layout/SectionComponent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { SectionSchema } from '@alfakit/core'\nimport {\n actionsAlignmentClass,\n headingTag,\n readPersisted,\n schemaGapClass,\n writePersisted,\n} from '@alfakit/core'\nimport { ChevronDownIcon } from '@lucide/vue'\nimport { computed, onMounted, ref } from 'vue'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport { useOptionalFormStore } from '../form/form-context'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport ActionList from '../actions/ActionList.vue'\nimport SchemaRenderer from '../schema/SchemaRenderer.vue'\nimport Button from '../ui/Button.vue'\nimport Collapsible from '../ui/Collapsible.vue'\nimport CollapsibleContent from '../ui/CollapsibleContent.vue'\nimport CollapsibleTrigger from '../ui/CollapsibleTrigger.vue'\nimport { useT } from '../../context'\nimport DeferredSchema from './DeferredSchema.vue'\nimport MixedContent from './MixedContent.vue'\n\nconst props = defineProps<{ component: SectionSchema }>()\nconst open = ref(!props.component.collapsed)\n// Inside a form the section's actions run as field actions, so `$get` / `$set` work.\nconst sectionForm = useOptionalFormStore()\nconst pageContext = usePageActionContext()\nconst fieldContext = useFieldActionContext(() => props.component)\nconst context = computed(() => (sectionForm ? fieldContext.value : pageContext))\nconst heading = computed(() => headingTag(props.component.headingLevel))\nconst hasHeader = computed(() =>\n Boolean(\n props.component.heading ||\n props.component.description ||\n props.component.icon ||\n props.component.headerActions?.length ||\n props.component.afterHeader?.length,\n ),\n)\n\n// Stored after mounting, so the server render and hydration use the schema's default.\nonMounted(() => {\n const key = props.component.persistCollapsed\n\n if (!key) return\n\n const stored = readPersisted(key)\n\n if (stored !== null) open.value = stored !== 'collapsed'\n})\n\nfunction setOpen(next: boolean) {\n open.value = next\n\n if (props.component.persistCollapsed) {\n writePersisted(props.component.persistCollapsed, next ? 'expanded' : 'collapsed')\n }\n}\nconst hasFooterActions = computed(() => Boolean(props.component.footerActions?.length))\nconst hasFooter = computed(() => hasFooterActions.value || Boolean(props.component.footer?.length))\nconst t = useT()\n</script>\n\n<template>\n <Collapsible\n v-if=\"props.component.aside\"\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n class=\"grid gap-6 lg:grid-cols-3\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div v-if=\"hasHeader\" class=\"flex flex-wrap items-start gap-3 lg:col-span-1\">\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent
|
|
1
|
+
{"version":3,"file":"SectionComponent.js","names":["_sfc_main","SectionComponent_vue_vue_type_script_setup_true_lang_default","SectionComponent_default"],"sources":["../../../src/components/layout/SectionComponent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { SectionSchema } from '@alfakit/core'\nimport {\n actionsAlignmentClass,\n headingTag,\n readPersisted,\n schemaGapClass,\n writePersisted,\n} from '@alfakit/core'\nimport { ChevronDownIcon } from '@lucide/vue'\nimport { computed, onMounted, ref } from 'vue'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport { useOptionalFormStore } from '../form/form-context'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport ActionList from '../actions/ActionList.vue'\nimport SchemaRenderer from '../schema/SchemaRenderer.vue'\nimport Button from '../ui/Button.vue'\nimport Collapsible from '../ui/Collapsible.vue'\nimport CollapsibleContent from '../ui/CollapsibleContent.vue'\nimport CollapsibleTrigger from '../ui/CollapsibleTrigger.vue'\nimport { useT } from '../../context'\nimport DeferredSchema from './DeferredSchema.vue'\nimport MixedContent from './MixedContent.vue'\n\nconst props = defineProps<{ component: SectionSchema }>()\nconst open = ref(!props.component.collapsed)\n// Inside a form the section's actions run as field actions, so `$get` / `$set` work.\nconst sectionForm = useOptionalFormStore()\nconst pageContext = usePageActionContext()\nconst fieldContext = useFieldActionContext(() => props.component)\nconst context = computed(() => (sectionForm ? fieldContext.value : pageContext))\nconst heading = computed(() => headingTag(props.component.headingLevel))\nconst hasHeader = computed(() =>\n Boolean(\n props.component.heading ||\n props.component.description ||\n props.component.icon ||\n props.component.headerActions?.length ||\n props.component.afterHeader?.length,\n ),\n)\n\n// Stored after mounting, so the server render and hydration use the schema's default.\nonMounted(() => {\n const key = props.component.persistCollapsed\n\n if (!key) return\n\n const stored = readPersisted(key)\n\n if (stored !== null) open.value = stored !== 'collapsed'\n})\n\nfunction setOpen(next: boolean) {\n open.value = next\n\n if (props.component.persistCollapsed) {\n writePersisted(props.component.persistCollapsed, next ? 'expanded' : 'collapsed')\n }\n}\nconst hasFooterActions = computed(() => Boolean(props.component.footerActions?.length))\nconst hasFooter = computed(() => hasFooterActions.value || Boolean(props.component.footer?.length))\nconst t = useT()\n</script>\n\n<template>\n <Collapsible\n v-if=\"props.component.aside\"\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n class=\"grid gap-6 lg:grid-cols-3\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div v-if=\"hasHeader\" class=\"flex flex-wrap items-start gap-3 lg:col-span-1\">\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent\n :items=\"props.component.afterHeader\"\n name=\"section-after-header\"\n :context=\"context\"\n />\n <CollapsibleTrigger v-if=\"props.component.collapsible\" as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"t('toggle_section', {}, 'Toggle section')\"\n >\n <Icon\n name=\"alfakit::schemas.section.collapse-button\"\n :fallback=\"ChevronDownIcon\"\n :class=\"cn('transition-transform', open && 'rotate-180')\"\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent as-child>\n <div\n data-surface=\"section\"\n :class=\"\n cn(\n 'bg-card rounded-xl border shadow-xs lg:col-span-2',\n props.component.secondary && 'bg-muted/40',\n )\n \"\n >\n <div :class=\"cn('p-6', props.component.compact && 'p-4')\">\n <DeferredSchema v-slot=\"{ component: loaded }\" :component=\"props.component\">\n <SchemaRenderer\n :components=\"loaded.schema ?? []\"\n :columns=\"loaded.columns\"\n :class=\"schemaGapClass(loaded) || undefined\"\n />\n </DeferredSchema>\n </div>\n <div\n v-if=\"hasFooter\"\n data-slot=\"section-footer\"\n :class=\"\n cn(\n 'flex flex-wrap items-center gap-3 border-t px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <ActionList\n v-if=\"hasFooterActions\"\n :actions=\"props.component.footerActions!\"\n :context=\"context\"\n :class=\"actionsAlignmentClass(props.component.footerActionsAlignment) || undefined\"\n />\n <MixedContent\n :items=\"props.component.footer\"\n name=\"section-footer-content\"\n :context=\"context\"\n />\n </div>\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n <Collapsible\n v-else\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n data-surface=\"section\"\n :class=\"cn('bg-card rounded-xl border shadow-xs', props.component.secondary && 'bg-muted/40')\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div\n v-if=\"hasHeader\"\n :class=\"\n cn(\n 'flex flex-wrap items-start gap-3 border-b px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent\n :items=\"props.component.afterHeader\"\n name=\"section-after-header\"\n :context=\"context\"\n />\n <CollapsibleTrigger v-if=\"props.component.collapsible\" as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"t('toggle_section', {}, 'Toggle section')\"\n >\n <Icon\n name=\"alfakit::schemas.section.collapse-button\"\n :fallback=\"ChevronDownIcon\"\n :class=\"cn('transition-transform', open && 'rotate-180')\"\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <div :class=\"cn('p-6', props.component.compact && 'p-4')\">\n <DeferredSchema v-slot=\"{ component: loaded }\" :component=\"props.component\">\n <SchemaRenderer\n :components=\"loaded.schema ?? []\"\n :columns=\"loaded.columns\"\n :class=\"schemaGapClass(loaded) || undefined\"\n />\n </DeferredSchema>\n </div>\n <div\n v-if=\"hasFooter\"\n data-slot=\"section-footer\"\n :class=\"\n cn(\n 'flex flex-wrap items-center gap-3 border-t px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <ActionList\n v-if=\"hasFooterActions\"\n :actions=\"props.component.footerActions!\"\n :context=\"context\"\n :class=\"actionsAlignmentClass(props.component.footerActionsAlignment) || undefined\"\n />\n <MixedContent\n :items=\"props.component.footer\"\n name=\"section-footer-content\"\n :context=\"context\"\n />\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionComponent.vue.d.ts","sourceRoot":"","sources":["../../../src/components/layout/SectionComponent.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SectionComponent.vue.d.ts","sourceRoot":"","sources":["../../../src/components/layout/SectionComponent.vue"],"names":[],"mappings":"AAoRA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAyBlD,KAAK,WAAW,GAAG;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC;;AAqjBhD,wBAMG"}
|
|
@@ -70,8 +70,9 @@ var L = {
|
|
|
70
70
|
}, null, 8, ["actions", "context"])) : x("", !0),
|
|
71
71
|
T(f, {
|
|
72
72
|
items: H.component.afterHeader,
|
|
73
|
-
name: "section-after-header"
|
|
74
|
-
|
|
73
|
+
name: "section-after-header",
|
|
74
|
+
context: q.value
|
|
75
|
+
}, null, 8, ["items", "context"]),
|
|
75
76
|
H.component.collapsible ? (A(), b(p, {
|
|
76
77
|
key: 2,
|
|
77
78
|
"as-child": ""
|
|
@@ -121,8 +122,9 @@ var L = {
|
|
|
121
122
|
"class"
|
|
122
123
|
])) : x("", !0), T(f, {
|
|
123
124
|
items: H.component.footer,
|
|
124
|
-
name: "section-footer-content"
|
|
125
|
-
|
|
125
|
+
name: "section-footer-content",
|
|
126
|
+
context: q.value
|
|
127
|
+
}, null, 8, ["items", "context"])], 2)) : x("", !0)], 2)]),
|
|
126
128
|
_: 1
|
|
127
129
|
})], 16)]),
|
|
128
130
|
_: 1
|
|
@@ -160,8 +162,9 @@ var L = {
|
|
|
160
162
|
}, null, 8, ["actions", "context"])) : x("", !0),
|
|
161
163
|
T(f, {
|
|
162
164
|
items: H.component.afterHeader,
|
|
163
|
-
name: "section-after-header"
|
|
164
|
-
|
|
165
|
+
name: "section-after-header",
|
|
166
|
+
context: q.value
|
|
167
|
+
}, null, 8, ["items", "context"]),
|
|
165
168
|
H.component.collapsible ? (A(), b(p, {
|
|
166
169
|
key: 2,
|
|
167
170
|
"as-child": ""
|
|
@@ -208,8 +211,9 @@ var L = {
|
|
|
208
211
|
"class"
|
|
209
212
|
])) : x("", !0), T(f, {
|
|
210
213
|
items: H.component.footer,
|
|
211
|
-
name: "section-footer-content"
|
|
212
|
-
|
|
214
|
+
name: "section-footer-content",
|
|
215
|
+
context: q.value
|
|
216
|
+
}, null, 8, ["items", "context"])], 2)) : x("", !0)]),
|
|
213
217
|
_: 1
|
|
214
218
|
})], 16)]),
|
|
215
219
|
_: 1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionComponent.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","Collapsible","_withCtx","_createElementVNode","_mergeProps","_createElementBlock","_unref","Icon","_createCommentVNode","_resolveDynamicComponent","_createTextVNode","_toDisplayString","ActionList","_createVNode","MixedContent","CollapsibleTrigger","Button","_normalizeClass","CollapsibleContent","DeferredSchema","SchemaRenderer","Icon_default","Button_default","ActionList_default","SchemaRenderer_default","DeferredSchema_default","Collapsible_default","CollapsibleContent_default","MixedContent_default","CollapsibleTrigger_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","SectionComponent_vue_vue_type_script_setup_true_lang_default","props","__props","open","ref","sectionForm","useOptionalFormStore","pageContext","usePageActionContext","fieldContext","useFieldActionContext","context","computed","heading","headingTag","hasHeader","key","stored","readPersisted","setOpen","next","writePersisted","hasFooterActions","hasFooter","t","useT","openBlock","createBlock","_cache","$event","withCtx","createElementVNode","mergeProps","createElementBlock","unref","createCommentVNode","resolveDynamicComponent","createTextVNode","toDisplayString","createVNode","ChevronDownIcon","normalizeClass","cn","loaded","schemaGapClass","actionsAlignmentClass"],"sources":["../../../src/components/layout/SectionComponent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { SectionSchema } from '@alfakit/core'\nimport {\n actionsAlignmentClass,\n headingTag,\n readPersisted,\n schemaGapClass,\n writePersisted,\n} from '@alfakit/core'\nimport { ChevronDownIcon } from '@lucide/vue'\nimport { computed, onMounted, ref } from 'vue'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport { useOptionalFormStore } from '../form/form-context'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport ActionList from '../actions/ActionList.vue'\nimport SchemaRenderer from '../schema/SchemaRenderer.vue'\nimport Button from '../ui/Button.vue'\nimport Collapsible from '../ui/Collapsible.vue'\nimport CollapsibleContent from '../ui/CollapsibleContent.vue'\nimport CollapsibleTrigger from '../ui/CollapsibleTrigger.vue'\nimport { useT } from '../../context'\nimport DeferredSchema from './DeferredSchema.vue'\nimport MixedContent from './MixedContent.vue'\n\nconst props = defineProps<{ component: SectionSchema }>()\nconst open = ref(!props.component.collapsed)\n// Inside a form the section's actions run as field actions, so `$get` / `$set` work.\nconst sectionForm = useOptionalFormStore()\nconst pageContext = usePageActionContext()\nconst fieldContext = useFieldActionContext(() => props.component)\nconst context = computed(() => (sectionForm ? fieldContext.value : pageContext))\nconst heading = computed(() => headingTag(props.component.headingLevel))\nconst hasHeader = computed(() =>\n Boolean(\n props.component.heading ||\n props.component.description ||\n props.component.icon ||\n props.component.headerActions?.length ||\n props.component.afterHeader?.length,\n ),\n)\n\n// Stored after mounting, so the server render and hydration use the schema's default.\nonMounted(() => {\n const key = props.component.persistCollapsed\n\n if (!key) return\n\n const stored = readPersisted(key)\n\n if (stored !== null) open.value = stored !== 'collapsed'\n})\n\nfunction setOpen(next: boolean) {\n open.value = next\n\n if (props.component.persistCollapsed) {\n writePersisted(props.component.persistCollapsed, next ? 'expanded' : 'collapsed')\n }\n}\nconst hasFooterActions = computed(() => Boolean(props.component.footerActions?.length))\nconst hasFooter = computed(() => hasFooterActions.value || Boolean(props.component.footer?.length))\nconst t = useT()\n</script>\n\n<template>\n <Collapsible\n v-if=\"props.component.aside\"\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n class=\"grid gap-6 lg:grid-cols-3\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div v-if=\"hasHeader\" class=\"flex flex-wrap items-start gap-3 lg:col-span-1\">\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent :items=\"props.component.afterHeader\" name=\"section-after-header\" />\n <CollapsibleTrigger v-if=\"props.component.collapsible\" as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"t('toggle_section', {}, 'Toggle section')\"\n >\n <Icon\n name=\"alfakit::schemas.section.collapse-button\"\n :fallback=\"ChevronDownIcon\"\n :class=\"cn('transition-transform', open && 'rotate-180')\"\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent as-child>\n <div\n data-surface=\"section\"\n :class=\"\n cn(\n 'bg-card rounded-xl border shadow-xs lg:col-span-2',\n props.component.secondary && 'bg-muted/40',\n )\n \"\n >\n <div :class=\"cn('p-6', props.component.compact && 'p-4')\">\n <DeferredSchema v-slot=\"{ component: loaded }\" :component=\"props.component\">\n <SchemaRenderer\n :components=\"loaded.schema ?? []\"\n :columns=\"loaded.columns\"\n :class=\"schemaGapClass(loaded) || undefined\"\n />\n </DeferredSchema>\n </div>\n <div\n v-if=\"hasFooter\"\n data-slot=\"section-footer\"\n :class=\"\n cn(\n 'flex flex-wrap items-center gap-3 border-t px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <ActionList\n v-if=\"hasFooterActions\"\n :actions=\"props.component.footerActions!\"\n :context=\"context\"\n :class=\"actionsAlignmentClass(props.component.footerActionsAlignment) || undefined\"\n />\n <MixedContent :items=\"props.component.footer\" name=\"section-footer-content\" />\n </div>\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n <Collapsible\n v-else\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n data-surface=\"section\"\n :class=\"cn('bg-card rounded-xl border shadow-xs', props.component.secondary && 'bg-muted/40')\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div\n v-if=\"hasHeader\"\n :class=\"\n cn(\n 'flex flex-wrap items-start gap-3 border-b px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent :items=\"props.component.afterHeader\" name=\"section-after-header\" />\n <CollapsibleTrigger v-if=\"props.component.collapsible\" as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"t('toggle_section', {}, 'Toggle section')\"\n >\n <Icon\n name=\"alfakit::schemas.section.collapse-button\"\n :fallback=\"ChevronDownIcon\"\n :class=\"cn('transition-transform', open && 'rotate-180')\"\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <div :class=\"cn('p-6', props.component.compact && 'p-4')\">\n <DeferredSchema v-slot=\"{ component: loaded }\" :component=\"props.component\">\n <SchemaRenderer\n :components=\"loaded.schema ?? []\"\n :columns=\"loaded.columns\"\n :class=\"schemaGapClass(loaded) || undefined\"\n />\n </DeferredSchema>\n </div>\n <div\n v-if=\"hasFooter\"\n data-slot=\"section-footer\"\n :class=\"\n cn(\n 'flex flex-wrap items-center gap-3 border-t px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <ActionList\n v-if=\"hasFooterActions\"\n :actions=\"props.component.footerActions!\"\n :context=\"context\"\n :class=\"actionsAlignmentClass(props.component.footerActionsAlignment) || undefined\"\n />\n <MixedContent :items=\"props.component.footer\" name=\"section-footer-content\" />\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SectionComponent.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","Collapsible","_withCtx","_createElementVNode","_mergeProps","_createElementBlock","_unref","Icon","_createCommentVNode","_resolveDynamicComponent","_createTextVNode","_toDisplayString","ActionList","_createVNode","MixedContent","CollapsibleTrigger","Button","_normalizeClass","CollapsibleContent","DeferredSchema","SchemaRenderer","Icon_default","Button_default","ActionList_default","SchemaRenderer_default","DeferredSchema_default","Collapsible_default","CollapsibleContent_default","MixedContent_default","CollapsibleTrigger_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","SectionComponent_vue_vue_type_script_setup_true_lang_default","props","__props","open","ref","sectionForm","useOptionalFormStore","pageContext","usePageActionContext","fieldContext","useFieldActionContext","context","computed","heading","headingTag","hasHeader","key","stored","readPersisted","setOpen","next","writePersisted","hasFooterActions","hasFooter","t","useT","openBlock","createBlock","_cache","$event","withCtx","createElementVNode","mergeProps","createElementBlock","unref","createCommentVNode","resolveDynamicComponent","createTextVNode","toDisplayString","createVNode","ChevronDownIcon","normalizeClass","cn","loaded","schemaGapClass","actionsAlignmentClass"],"sources":["../../../src/components/layout/SectionComponent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { SectionSchema } from '@alfakit/core'\nimport {\n actionsAlignmentClass,\n headingTag,\n readPersisted,\n schemaGapClass,\n writePersisted,\n} from '@alfakit/core'\nimport { ChevronDownIcon } from '@lucide/vue'\nimport { computed, onMounted, ref } from 'vue'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { usePageActionContext } from '../actions/action-context'\nimport { useOptionalFormStore } from '../form/form-context'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport ActionList from '../actions/ActionList.vue'\nimport SchemaRenderer from '../schema/SchemaRenderer.vue'\nimport Button from '../ui/Button.vue'\nimport Collapsible from '../ui/Collapsible.vue'\nimport CollapsibleContent from '../ui/CollapsibleContent.vue'\nimport CollapsibleTrigger from '../ui/CollapsibleTrigger.vue'\nimport { useT } from '../../context'\nimport DeferredSchema from './DeferredSchema.vue'\nimport MixedContent from './MixedContent.vue'\n\nconst props = defineProps<{ component: SectionSchema }>()\nconst open = ref(!props.component.collapsed)\n// Inside a form the section's actions run as field actions, so `$get` / `$set` work.\nconst sectionForm = useOptionalFormStore()\nconst pageContext = usePageActionContext()\nconst fieldContext = useFieldActionContext(() => props.component)\nconst context = computed(() => (sectionForm ? fieldContext.value : pageContext))\nconst heading = computed(() => headingTag(props.component.headingLevel))\nconst hasHeader = computed(() =>\n Boolean(\n props.component.heading ||\n props.component.description ||\n props.component.icon ||\n props.component.headerActions?.length ||\n props.component.afterHeader?.length,\n ),\n)\n\n// Stored after mounting, so the server render and hydration use the schema's default.\nonMounted(() => {\n const key = props.component.persistCollapsed\n\n if (!key) return\n\n const stored = readPersisted(key)\n\n if (stored !== null) open.value = stored !== 'collapsed'\n})\n\nfunction setOpen(next: boolean) {\n open.value = next\n\n if (props.component.persistCollapsed) {\n writePersisted(props.component.persistCollapsed, next ? 'expanded' : 'collapsed')\n }\n}\nconst hasFooterActions = computed(() => Boolean(props.component.footerActions?.length))\nconst hasFooter = computed(() => hasFooterActions.value || Boolean(props.component.footer?.length))\nconst t = useT()\n</script>\n\n<template>\n <Collapsible\n v-if=\"props.component.aside\"\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n class=\"grid gap-6 lg:grid-cols-3\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div v-if=\"hasHeader\" class=\"flex flex-wrap items-start gap-3 lg:col-span-1\">\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent\n :items=\"props.component.afterHeader\"\n name=\"section-after-header\"\n :context=\"context\"\n />\n <CollapsibleTrigger v-if=\"props.component.collapsible\" as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"t('toggle_section', {}, 'Toggle section')\"\n >\n <Icon\n name=\"alfakit::schemas.section.collapse-button\"\n :fallback=\"ChevronDownIcon\"\n :class=\"cn('transition-transform', open && 'rotate-180')\"\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent as-child>\n <div\n data-surface=\"section\"\n :class=\"\n cn(\n 'bg-card rounded-xl border shadow-xs lg:col-span-2',\n props.component.secondary && 'bg-muted/40',\n )\n \"\n >\n <div :class=\"cn('p-6', props.component.compact && 'p-4')\">\n <DeferredSchema v-slot=\"{ component: loaded }\" :component=\"props.component\">\n <SchemaRenderer\n :components=\"loaded.schema ?? []\"\n :columns=\"loaded.columns\"\n :class=\"schemaGapClass(loaded) || undefined\"\n />\n </DeferredSchema>\n </div>\n <div\n v-if=\"hasFooter\"\n data-slot=\"section-footer\"\n :class=\"\n cn(\n 'flex flex-wrap items-center gap-3 border-t px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <ActionList\n v-if=\"hasFooterActions\"\n :actions=\"props.component.footerActions!\"\n :context=\"context\"\n :class=\"actionsAlignmentClass(props.component.footerActionsAlignment) || undefined\"\n />\n <MixedContent\n :items=\"props.component.footer\"\n name=\"section-footer-content\"\n :context=\"context\"\n />\n </div>\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n <Collapsible\n v-else\n :open=\"!props.component.collapsible || open\"\n as-child\n @update:open=\"setOpen($event)\"\n >\n <section\n data-slot=\"section\"\n data-surface=\"section\"\n :class=\"cn('bg-card rounded-xl border shadow-xs', props.component.secondary && 'bg-muted/40')\"\n v-bind=\"props.component.extraAttributes\"\n >\n <div\n v-if=\"hasHeader\"\n :class=\"\n cn(\n 'flex flex-wrap items-start gap-3 border-b px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <Icon\n v-if=\"props.component.icon\"\n :name=\"props.component.icon\"\n class=\"text-muted-foreground mt-0.5 size-5\"\n />\n <div class=\"min-w-0 flex-1 basis-48\">\n <component\n :is=\"heading\"\n v-if=\"props.component.heading\"\n class=\"text-base leading-6 font-semibold break-words\"\n >\n {{ props.component.heading }}\n </component>\n <p\n v-if=\"props.component.description\"\n class=\"text-muted-foreground mt-1 text-sm break-words\"\n >\n {{ props.component.description }}\n </p>\n </div>\n <ActionList\n v-if=\"props.component.headerActions && props.component.headerActions.length > 0\"\n :actions=\"props.component.headerActions\"\n :context=\"context\"\n size=\"sm\"\n />\n <MixedContent\n :items=\"props.component.afterHeader\"\n name=\"section-after-header\"\n :context=\"context\"\n />\n <CollapsibleTrigger v-if=\"props.component.collapsible\" as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"t('toggle_section', {}, 'Toggle section')\"\n >\n <Icon\n name=\"alfakit::schemas.section.collapse-button\"\n :fallback=\"ChevronDownIcon\"\n :class=\"cn('transition-transform', open && 'rotate-180')\"\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <div :class=\"cn('p-6', props.component.compact && 'p-4')\">\n <DeferredSchema v-slot=\"{ component: loaded }\" :component=\"props.component\">\n <SchemaRenderer\n :components=\"loaded.schema ?? []\"\n :columns=\"loaded.columns\"\n :class=\"schemaGapClass(loaded) || undefined\"\n />\n </DeferredSchema>\n </div>\n <div\n v-if=\"hasFooter\"\n data-slot=\"section-footer\"\n :class=\"\n cn(\n 'flex flex-wrap items-center gap-3 border-t px-6 py-4',\n props.component.compact && 'px-4 py-3',\n )\n \"\n >\n <ActionList\n v-if=\"hasFooterActions\"\n :actions=\"props.component.footerActions!\"\n :context=\"context\"\n :class=\"actionsAlignmentClass(props.component.footerActionsAlignment) || undefined\"\n />\n <MixedContent\n :items=\"props.component.footer\"\n name=\"section-footer-content\"\n :context=\"context\"\n />\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsMenu.js","names":["_sfc_main","NotificationsMenu_vue_vue_type_script_setup_true_lang_default","NotificationsMenu_default"],"sources":["../../../src/components/notifications/NotificationsMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n NotificationCenter,\n notificationTintStyles,\n onDatabaseNotificationsSent,\n onOpenDatabaseNotifications,\n} from '@alfakit/core'\nimport { BellIcon, CheckCheckIcon, Trash2Icon } from '@lucide/vue'\nimport { computed, onMounted, onScopeDispose, ref, watch } from 'vue'\nimport { useStore } from '../../composables/use-store'\nimport { useDirection, useFormatter, usePanel, useT } from '../../context'\nimport { cssColor, textColorClasses } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport Button from '../ui/Button.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport ScrollArea from '../ui/ScrollArea.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport NotificationActionButton from './NotificationActionButton.vue'\n\n/**\n * Bell menu for database notifications. It also opens when something calls\n * `openDatabaseNotifications()` (or an action with `->openDatabaseNotifications()`).\n * `variant=\"sidebar\"`: a full-width row with a label, for layouts that put it in the sidebar.\n */\nconst props = withDefaults(defineProps<{ variant?: 'topbar' | 'sidebar' }>(), {\n variant: 'topbar',\n})\nconst t = useT()\nconst direction = useDirection()\nconst panel = usePanel()\nconst config = computed(() => panel.value.databaseNotifications)\nconst endpoints = panel.value.endpoints\n\nconst center =\n endpoints.notifications && endpoints.notificationsReadAll && endpoints.notificationsDestroyAll\n ? new NotificationCenter(\n {\n notifications: endpoints.notifications,\n notificationsReadAll: endpoints.notificationsReadAll,\n notificationsDestroyAll: endpoints.notificationsDestroyAll,\n },\n config.value?.unreadCount ?? 0,\n )\n : null\n\nconst open = ref(false)\nconst state = useStore(\n center?.store ?? {\n get: () => ({ notifications: [], unreadCount: 0, loading: false, hasMore: false, page: 0 }),\n subscribe: () => () => {},\n },\n)\n\n// Polling starts in the browser only: a server render must not leave timers behind.\nonMounted(() => {\n watch(\n () => config.value?.pollingInterval,\n (interval) => center?.startPolling(interval),\n { immediate: true },\n )\n})\n\nwatch(open, (isOpen) => {\n if (isOpen) void center?.load()\n})\n\n// Every visit brings the server's unread count (another user after impersonating, or\n// notifications read in another tab): follow it instead of waiting for the next poll.\nwatch(\n () => config.value?.unreadCount,\n (count) => {\n if (center && typeof count === 'number' && count !== center.store.get().unreadCount) {\n center.store.update({ unreadCount: count })\n }\n },\n)\n\n// Realtime notifications announce stored ones, so the unread count updates right away.\nlet stopListening: (() => void) | null = null\nonMounted(() => {\n stopListening = center ? onDatabaseNotificationsSent(() => void center.load()) : null\n})\nonScopeDispose(() => stopListening?.())\n\n// `openDatabaseNotifications()`: opened from code or an action.\nlet stopOpenListener: (() => void) | null = null\nonMounted(() => {\n stopOpenListener = onOpenDatabaseNotifications(() => (open.value = true))\n})\nonScopeDispose(() => stopOpenListener?.())\n\nonScopeDispose(() => center?.stopPolling())\nconst format = useFormatter()\n</script>\n\n<template>\n <Popover v-if=\"config && center\" :open=\"open\" @update:open=\"open = $event\">\n <PopoverTrigger as-child>\n <Button\n v-if=\"props.variant === 'sidebar'\"\n type=\"button\"\n variant=\"ghost\"\n class=\"relative w-full justify-start gap-2 group-data-[collapsible=icon]:size-8 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0\"\n data-slot=\"notifications-trigger\"\n data-variant=\"sidebar\"\n >\n <!-- In a collapsed (icon) sidebar only the bell and its badge stay visible. -->\n <Icon\n name=\"alfakit::panels.topbar.open-database-notifications-button\"\n :fallback=\"BellIcon\"\n />\n <span class=\"flex-1 text-start group-data-[collapsible=icon]:sr-only\">{{\n t('notifications', {}, 'Notifications')\n }}</span>\n <span\n v-if=\"state.unreadCount > 0\"\n class=\"bg-primary text-primary-foreground flex h-5 min-w-5 items-center justify-center rounded-full px-1 text-xs font-medium group-data-[collapsible=icon]:absolute group-data-[collapsible=icon]:-end-1 group-data-[collapsible=icon]:-top-1 group-data-[collapsible=icon]:h-4 group-data-[collapsible=icon]:min-w-4 group-data-[collapsible=icon]:text-[10px]\"\n >{{ state.unreadCount > 99 ? '99+' : state.unreadCount }}\n <span class=\"sr-only\">{{ t('unread_notifications', {}, 'unread') }}</span></span\n >\n </Button>\n <Button\n v-else\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n class=\"relative\"\n data-slot=\"notifications-trigger\"\n :aria-label=\"t('notifications', {}, 'Notifications')\"\n >\n <Icon\n name=\"alfakit::panels.topbar.open-database-notifications-button\"\n :fallback=\"BellIcon\"\n />\n <span\n v-if=\"state.unreadCount > 0\"\n class=\"bg-primary text-primary-foreground absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-medium\"\n >{{ state.unreadCount > 99 ? '99+' : state.unreadCount }}</span\n >\n </Button>\n </PopoverTrigger>\n <PopoverContent\n :align=\"props.variant === 'sidebar' ? 'start' : 'end'\"\n :side=\"props.variant === 'sidebar' ? (direction === 'rtl' ? 'left' : 'right') : 'bottom'\"\n class=\"w-80 p-0 sm:w-96\"\n >\n <div class=\"flex items-center justify-between border-b px-3 py-2\">\n <span class=\"text-sm font-semibold\">{{ t('notifications', {}, 'Notifications') }}</span>\n <div class=\"flex items-center gap-1\">\n <Button\n v-if=\"state.unreadCount > 0\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :title=\"t('mark_all_read', {}, 'Mark all as read')\"\n @click=\"void center.markAllAsRead()\"\n ><CheckCheckIcon\n /></Button>\n <Button\n v-if=\"state.notifications.length > 0\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :title=\"t('clear_notifications', {}, 'Clear notifications')\"\n @click=\"void center.clear()\"\n ><Trash2Icon\n /></Button>\n </div>\n </div>\n <ScrollArea class=\"max-h-96\">\n <div\n v-if=\"state.loading && state.notifications.length === 0\"\n class=\"flex justify-center py-8\"\n >\n <Spinner />\n </div>\n <p\n v-else-if=\"state.notifications.length === 0\"\n class=\"text-muted-foreground px-3 py-8 text-center text-sm\"\n >\n {{ t('no_notifications', {}, 'You have no notifications.') }}\n </p>\n <ul v-else class=\"divide-y\">\n <li\n v-for=\"notification in state.notifications\"\n :key=\"notification.id\"\n :class=\"cn('flex gap-3 px-3 py-3', !notification.readAt && 'bg-primary/5')\"\n :style=\"\n notification.color\n ? notificationTintStyles(cssColor(notification.color)).item\n : undefined\n \"\n >\n <Icon\n v-if=\"notification.icon\"\n :name=\"notification.icon\"\n :class=\"\n cn(\n 'mt-0.5 size-5 shrink-0',\n notification.iconColor && textColorClasses[notification.iconColor],\n )\n \"\n />\n <div class=\"flex min-w-0 flex-1 flex-col gap-1\">\n <div class=\"flex items-start justify-between gap-2\">\n <p class=\"text-sm font-medium\">{{ notification.title }}</p>\n <button\n v-if=\"!notification.readAt\"\n type=\"button\"\n class=\"focus-visible:ring-ring/50 -me-2 -mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full outline-none focus-visible:ring-[3px]\"\n :aria-label=\"t('mark_as_read', {}, 'Mark as read')\"\n @click=\"void center.markAsRead(notification.id)\"\n >\n <span class=\"bg-primary size-2 rounded-full\" aria-hidden=\"true\" />\n </button>\n </div>\n <p\n v-if=\"notification.body\"\n class=\"text-muted-foreground text-sm\"\n v-html=\"notification.body\"\n />\n <time v-if=\"notification.date\" class=\"text-muted-foreground text-xs\">{{\n format.relative(notification.date)\n }}</time>\n <div v-if=\"notification.actions\" class=\"flex flex-wrap gap-1\">\n <NotificationActionButton\n v-for=\"action in notification.actions\"\n :key=\"action.name\"\n :action=\"action\"\n :on-mark-as-read=\"() => void center!.markAsRead(notification.id)\"\n :on-mark-as-unread=\"() => void center!.markAsUnread(notification.id)\"\n />\n </div>\n </div>\n </li>\n </ul>\n <div\n v-if=\"state.hasMore && state.notifications.length > 0\"\n class=\"flex justify-center border-t p-2\"\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n :disabled=\"state.loading\"\n @click=\"void center!.loadMore()\"\n >\n <Spinner v-if=\"state.loading\" />\n {{ t('load_more', {}, 'Load more') }}\n </Button>\n </div>\n </ScrollArea>\n </PopoverContent>\n </Popover>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"NotificationsMenu.js","names":["_sfc_main","NotificationsMenu_vue_vue_type_script_setup_true_lang_default","NotificationsMenu_default"],"sources":["../../../src/components/notifications/NotificationsMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n NotificationCenter,\n notificationTintStyles,\n onDatabaseNotificationsSent,\n onOpenDatabaseNotifications,\n pwaStore,\n startPwa,\n togglePushNotifications,\n} from '@alfakit/core'\nimport { BellIcon, BellOffIcon, BellRingIcon, CheckCheckIcon, Trash2Icon } from '@lucide/vue'\nimport { computed, onMounted, onScopeDispose, ref, watch } from 'vue'\nimport { toast } from 'vue-sonner'\nimport { useStore } from '../../composables/use-store'\nimport { useDirection, useFormatter, usePanel, useT } from '../../context'\nimport { cssColor, textColorClasses } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport Button from '../ui/Button.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport ScrollArea from '../ui/ScrollArea.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport NotificationActionButton from './NotificationActionButton.vue'\n\n/**\n * Bell menu for database notifications. It also opens when something calls\n * `openDatabaseNotifications()` (or an action with `->openDatabaseNotifications()`).\n * `variant=\"sidebar\"`: a full-width row with a label, for layouts that put it in the sidebar.\n */\nconst props = withDefaults(defineProps<{ variant?: 'topbar' | 'sidebar' }>(), {\n variant: 'topbar',\n})\nconst t = useT()\nconst direction = useDirection()\nconst panel = usePanel()\nconst config = computed(() => panel.value.databaseNotifications)\nconst endpoints = panel.value.endpoints\n\nconst center =\n endpoints.notifications && endpoints.notificationsReadAll && endpoints.notificationsDestroyAll\n ? new NotificationCenter(\n {\n notifications: endpoints.notifications,\n notificationsReadAll: endpoints.notificationsReadAll,\n notificationsDestroyAll: endpoints.notificationsDestroyAll,\n },\n config.value?.unreadCount ?? 0,\n )\n : null\n\nconst open = ref(false)\nconst state = useStore(\n center?.store ?? {\n get: () => ({ notifications: [], unreadCount: 0, loading: false, hasMore: false, page: 0 }),\n subscribe: () => () => {},\n },\n)\n\n// Polling starts in the browser only: a server render must not leave timers behind.\nonMounted(() => {\n watch(\n () => config.value?.pollingInterval,\n (interval) => center?.startPolling(interval),\n { immediate: true },\n )\n})\n\nwatch(open, (isOpen) => {\n if (isOpen) void center?.load()\n})\n\n// Every visit brings the server's unread count (another user after impersonating, or\n// notifications read in another tab): follow it instead of waiting for the next poll.\nwatch(\n () => config.value?.unreadCount,\n (count) => {\n if (center && typeof count === 'number' && count !== center.store.get().unreadCount) {\n center.store.update({ unreadCount: count })\n }\n },\n)\n\n// Realtime notifications announce stored ones, so the unread count updates right away.\nlet stopListening: (() => void) | null = null\nonMounted(() => {\n stopListening = center ? onDatabaseNotificationsSent(() => void center.load()) : null\n})\nonScopeDispose(() => stopListening?.())\n\n// `openDatabaseNotifications()`: opened from code or an action.\nlet stopOpenListener: (() => void) | null = null\nonMounted(() => {\n stopOpenListener = onOpenDatabaseNotifications(() => (open.value = true))\n})\nonScopeDispose(() => stopOpenListener?.())\n\nonScopeDispose(() => center?.stopPolling())\nconst format = useFormatter()\n\n// `pwa(push: true)` in a starter kit: the app's user menu has no AlfaKit entries, so Web\n// Push is turned on or off from the bell's menu.\nconst pwa = useStore(pwaStore)\nconst pushToggle = computed(\n () =>\n Boolean(panel.value.starterKit && panel.value.pwa?.push) && pwa.value.push !== 'unsupported',\n)\nonMounted(() => {\n if (panel.value.starterKit && panel.value.pwa?.push) startPwa(panel.value.pwa)\n})\nasync function togglePush() {\n const { message } = await togglePushNotifications()\n const text = t(message.key, {}, message.fallback)\n\n if (message.error) toast.error(text)\n else toast.success(text)\n}\n</script>\n\n<template>\n <Popover v-if=\"config && center\" :open=\"open\" @update:open=\"open = $event\">\n <PopoverTrigger as-child>\n <Button\n v-if=\"props.variant === 'sidebar'\"\n type=\"button\"\n variant=\"ghost\"\n class=\"relative w-full justify-start gap-2 group-data-[collapsible=icon]:size-8 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0\"\n data-slot=\"notifications-trigger\"\n data-variant=\"sidebar\"\n >\n <!-- In a collapsed (icon) sidebar only the bell and its badge stay visible. -->\n <Icon\n name=\"alfakit::panels.topbar.open-database-notifications-button\"\n :fallback=\"BellIcon\"\n />\n <span class=\"flex-1 text-start group-data-[collapsible=icon]:sr-only\">{{\n t('notifications', {}, 'Notifications')\n }}</span>\n <span\n v-if=\"state.unreadCount > 0\"\n class=\"bg-primary text-primary-foreground flex h-5 min-w-5 items-center justify-center rounded-full px-1 text-xs font-medium group-data-[collapsible=icon]:absolute group-data-[collapsible=icon]:-end-1 group-data-[collapsible=icon]:-top-1 group-data-[collapsible=icon]:h-4 group-data-[collapsible=icon]:min-w-4 group-data-[collapsible=icon]:text-[10px]\"\n >{{ state.unreadCount > 99 ? '99+' : state.unreadCount }}\n <span class=\"sr-only\">{{ t('unread_notifications', {}, 'unread') }}</span></span\n >\n </Button>\n <Button\n v-else\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n class=\"relative\"\n data-slot=\"notifications-trigger\"\n :aria-label=\"t('notifications', {}, 'Notifications')\"\n >\n <Icon\n name=\"alfakit::panels.topbar.open-database-notifications-button\"\n :fallback=\"BellIcon\"\n />\n <span\n v-if=\"state.unreadCount > 0\"\n class=\"bg-primary text-primary-foreground absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-medium\"\n >{{ state.unreadCount > 99 ? '99+' : state.unreadCount }}</span\n >\n </Button>\n </PopoverTrigger>\n <PopoverContent\n :align=\"props.variant === 'sidebar' ? 'start' : 'end'\"\n :side=\"props.variant === 'sidebar' ? (direction === 'rtl' ? 'left' : 'right') : 'bottom'\"\n class=\"w-80 p-0 sm:w-96\"\n >\n <div class=\"flex items-center justify-between border-b px-3 py-2\">\n <span class=\"text-sm font-semibold\">{{ t('notifications', {}, 'Notifications') }}</span>\n <div class=\"flex items-center gap-1\">\n <Button\n v-if=\"pushToggle\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n data-slot=\"pwa-push\"\n :disabled=\"pwa.pushBusy || pwa.push === 'denied'\"\n :title=\"\n pwa.push === 'subscribed'\n ? t('pwa.notifications.disable', {}, 'Turn off notifications')\n : t('pwa.notifications.enable', {}, 'Turn on notifications')\n \"\n :aria-label=\"\n pwa.push === 'subscribed'\n ? t('pwa.notifications.disable', {}, 'Turn off notifications')\n : t('pwa.notifications.enable', {}, 'Turn on notifications')\n \"\n @click=\"void togglePush()\"\n >\n <BellOffIcon v-if=\"pwa.push === 'subscribed'\" />\n <BellRingIcon v-else />\n </Button>\n <Button\n v-if=\"state.unreadCount > 0\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :title=\"t('mark_all_read', {}, 'Mark all as read')\"\n @click=\"void center.markAllAsRead()\"\n ><CheckCheckIcon\n /></Button>\n <Button\n v-if=\"state.notifications.length > 0\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :title=\"t('clear_notifications', {}, 'Clear notifications')\"\n @click=\"void center.clear()\"\n ><Trash2Icon\n /></Button>\n </div>\n </div>\n <ScrollArea class=\"max-h-96\">\n <div\n v-if=\"state.loading && state.notifications.length === 0\"\n class=\"flex justify-center py-8\"\n >\n <Spinner />\n </div>\n <p\n v-else-if=\"state.notifications.length === 0\"\n class=\"text-muted-foreground px-3 py-8 text-center text-sm\"\n >\n {{ t('no_notifications', {}, 'You have no notifications.') }}\n </p>\n <ul v-else class=\"divide-y\">\n <li\n v-for=\"notification in state.notifications\"\n :key=\"notification.id\"\n :class=\"cn('flex gap-3 px-3 py-3', !notification.readAt && 'bg-primary/5')\"\n :style=\"\n notification.color\n ? notificationTintStyles(cssColor(notification.color)).item\n : undefined\n \"\n >\n <Icon\n v-if=\"notification.icon\"\n :name=\"notification.icon\"\n :class=\"\n cn(\n 'mt-0.5 size-5 shrink-0',\n notification.iconColor && textColorClasses[notification.iconColor],\n )\n \"\n />\n <div class=\"flex min-w-0 flex-1 flex-col gap-1\">\n <div class=\"flex items-start justify-between gap-2\">\n <p class=\"text-sm font-medium\">{{ notification.title }}</p>\n <button\n v-if=\"!notification.readAt\"\n type=\"button\"\n class=\"focus-visible:ring-ring/50 -me-2 -mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full outline-none focus-visible:ring-[3px]\"\n :aria-label=\"t('mark_as_read', {}, 'Mark as read')\"\n @click=\"void center.markAsRead(notification.id)\"\n >\n <span class=\"bg-primary size-2 rounded-full\" aria-hidden=\"true\" />\n </button>\n </div>\n <p\n v-if=\"notification.body\"\n class=\"text-muted-foreground text-sm\"\n v-html=\"notification.body\"\n />\n <time v-if=\"notification.date\" class=\"text-muted-foreground text-xs\">{{\n format.relative(notification.date)\n }}</time>\n <div v-if=\"notification.actions\" class=\"flex flex-wrap gap-1\">\n <NotificationActionButton\n v-for=\"action in notification.actions\"\n :key=\"action.name\"\n :action=\"action\"\n :on-mark-as-read=\"() => void center!.markAsRead(notification.id)\"\n :on-mark-as-unread=\"() => void center!.markAsUnread(notification.id)\"\n />\n </div>\n </div>\n </li>\n </ul>\n <div\n v-if=\"state.hasMore && state.notifications.length > 0\"\n class=\"flex justify-center border-t p-2\"\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n :disabled=\"state.loading\"\n @click=\"void center!.loadMore()\"\n >\n <Spinner v-if=\"state.loading\" />\n {{ t('load_more', {}, 'Load more') }}\n </Button>\n </div>\n </ScrollArea>\n </PopoverContent>\n </Popover>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsMenu.vue.d.ts","sourceRoot":"","sources":["../../../src/components/notifications/NotificationsMenu.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NotificationsMenu.vue.d.ts","sourceRoot":"","sources":["../../../src/components/notifications/NotificationsMenu.vue"],"names":[],"mappings":"AAyUA;;;;GAIG;AACH,KAAK,WAAW,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAAC;;aAAvB,QAAQ,GAAG,SAAS;;AAgsBnD,wBAOG"}
|